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.
28 #include <pbd/error.h>
29 #include <gtkmm2ext/utils.h>
30 #include <pbd/memento_command.h>
32 #include "ardour_ui.h"
34 #include "time_axis_view.h"
35 #include "audio_time_axis.h"
36 #include "audio_region_view.h"
38 #include "streamview.h"
39 #include "region_gain_line.h"
40 #include "automation_time_axis.h"
43 #include "selection.h"
46 #include "rgb_macros.h"
48 #include <ardour/types.h>
49 #include <ardour/route.h>
50 #include <ardour/audio_track.h>
51 #include <ardour/audio_diskstream.h>
52 #include <ardour/playlist.h>
53 #include <ardour/audioplaylist.h>
54 #include <ardour/audioregion.h>
55 #include <ardour/dB.h>
56 #include <ardour/utils.h>
57 #include <ardour/region_factory.h>
64 using namespace ARDOUR;
68 using namespace Editing;
71 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
85 switch (event->type) {
86 case GDK_BUTTON_RELEASE:
87 case GDK_BUTTON_PRESS:
88 case GDK_2BUTTON_PRESS:
89 case GDK_3BUTTON_PRESS:
90 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
92 case GDK_MOTION_NOTIFY:
93 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
95 case GDK_ENTER_NOTIFY:
96 case GDK_LEAVE_NOTIFY:
97 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
100 case GDK_KEY_RELEASE:
101 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
104 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
108 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
109 position is negative (as can be the case with motion events in particular),
110 the frame location is always positive.
113 return pixel_to_frame (*pcx);
117 Editor::mouse_mode_toggled (MouseMode m)
119 if (ignore_mouse_mode_toggle) {
125 if (mouse_select_button.get_active()) {
131 if (mouse_move_button.get_active()) {
137 if (mouse_gain_button.get_active()) {
143 if (mouse_zoom_button.get_active()) {
149 if (mouse_timefx_button.get_active()) {
155 if (mouse_audition_button.get_active()) {
166 Editor::set_mouse_mode (MouseMode m, bool force)
168 if (drag_info.item) {
172 if (!force && m == mouse_mode) {
180 if (mouse_mode != MouseRange) {
182 /* in all modes except range, hide the range selection,
183 show the object (region) selection.
186 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
187 (*i)->set_should_show_selection (true);
189 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
190 (*i)->hide_selection ();
196 in range mode,show the range selection.
199 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
200 if ((*i)->get_selected()) {
201 (*i)->show_selection (selection->time);
206 /* XXX the hack of unsetting all other buttongs should go
207 away once GTK2 allows us to use regular radio buttons drawn like
208 normal buttons, rather than my silly GroupedButton hack.
211 ignore_mouse_mode_toggle = true;
213 switch (mouse_mode) {
215 mouse_select_button.set_active (true);
216 current_canvas_cursor = selector_cursor;
220 mouse_move_button.set_active (true);
221 current_canvas_cursor = grabber_cursor;
225 mouse_gain_button.set_active (true);
226 current_canvas_cursor = cross_hair_cursor;
230 mouse_zoom_button.set_active (true);
231 current_canvas_cursor = zoom_cursor;
235 mouse_timefx_button.set_active (true);
236 current_canvas_cursor = time_fx_cursor; // just use playhead
240 mouse_audition_button.set_active (true);
241 current_canvas_cursor = speaker_cursor;
245 ignore_mouse_mode_toggle = false;
248 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
253 Editor::step_mouse_mode (bool next)
255 switch (current_mouse_mode()) {
257 if (next) set_mouse_mode (MouseRange);
258 else set_mouse_mode (MouseTimeFX);
262 if (next) set_mouse_mode (MouseZoom);
263 else set_mouse_mode (MouseObject);
267 if (next) set_mouse_mode (MouseGain);
268 else set_mouse_mode (MouseRange);
272 if (next) set_mouse_mode (MouseTimeFX);
273 else set_mouse_mode (MouseZoom);
277 if (next) set_mouse_mode (MouseAudition);
278 else set_mouse_mode (MouseGain);
282 if (next) set_mouse_mode (MouseObject);
283 else set_mouse_mode (MouseTimeFX);
289 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
293 /* in object/audition/timefx mode, any button press sets
294 the selection if the object can be selected. this is a
295 bit of hack, because we want to avoid this if the
296 mouse operation is a region alignment.
298 note: not dbl-click or triple-click
301 if (((mouse_mode != MouseObject) &&
302 (mouse_mode != MouseAudition || item_type != RegionItem) &&
303 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
304 (mouse_mode != MouseRange)) ||
306 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
311 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
313 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
315 /* almost no selection action on modified button-2 or button-3 events */
317 if (item_type != RegionItem && event->button.button != 2) {
323 Selection::Operation op = Keyboard::selection_type (event->button.state);
324 bool press = (event->type == GDK_BUTTON_PRESS);
326 begin_reversible_command (_("select on click"));
330 if (mouse_mode != MouseRange) {
331 commit = set_selected_regionview_from_click (press, op, true);
332 } else if (event->type == GDK_BUTTON_PRESS) {
333 commit = set_selected_track_from_click (press, op, false);
337 case RegionViewNameHighlight:
339 if (mouse_mode != MouseRange) {
340 commit = set_selected_regionview_from_click (press, op, true);
341 } else if (event->type == GDK_BUTTON_PRESS) {
342 commit = set_selected_track_from_click (press, op, false);
346 case FadeInHandleItem:
348 case FadeOutHandleItem:
350 if (mouse_mode != MouseRange) {
351 commit = set_selected_regionview_from_click (press, op, true);
352 } else if (event->type == GDK_BUTTON_PRESS) {
353 commit = set_selected_track_from_click (press, op, false);
357 case GainAutomationControlPointItem:
358 case PanAutomationControlPointItem:
359 case RedirectAutomationControlPointItem:
360 if (mouse_mode != MouseRange) {
361 commit = set_selected_control_point_from_click (op, false);
366 /* for context click or range selection, select track */
367 if (event->button.button == 3) {
368 commit = set_selected_track_from_click (press, op, true);
369 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
370 commit = set_selected_track_from_click (press, op, false);
374 case AutomationTrackItem:
375 commit = set_selected_track_from_click (press, op, true);
383 commit_reversible_command ();
388 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
390 track_canvas.grab_focus();
392 if (session && session->actively_recording()) {
396 button_selection (item, event, item_type);
398 if (drag_info.item == 0 &&
399 (Keyboard::is_delete_event (&event->button) ||
400 Keyboard::is_context_menu_event (&event->button) ||
401 Keyboard::is_edit_event (&event->button))) {
403 /* handled by button release */
407 switch (event->button.button) {
410 if (event->type == GDK_BUTTON_PRESS) {
412 if (drag_info.item) {
413 drag_info.item->ungrab (event->button.time);
416 /* single mouse clicks on any of these item types operate
417 independent of mouse mode, mostly because they are
418 not on the main track canvas or because we want
424 case PlayheadCursorItem:
425 start_cursor_grab (item, event);
429 if (Keyboard::modifier_state_equals (event->button.state,
430 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
431 hide_marker (item, event);
433 start_marker_grab (item, event);
437 case TempoMarkerItem:
438 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
439 start_tempo_marker_copy_grab (item, event);
441 start_tempo_marker_grab (item, event);
445 case MeterMarkerItem:
446 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
447 start_meter_marker_copy_grab (item, event);
449 start_meter_marker_grab (item, event);
459 case RangeMarkerBarItem:
460 start_range_markerbar_op (item, event, CreateRangeMarker);
464 case TransportMarkerBarItem:
465 start_range_markerbar_op (item, event, CreateTransportMarker);
474 switch (mouse_mode) {
477 case StartSelectionTrimItem:
478 start_selection_op (item, event, SelectionStartTrim);
481 case EndSelectionTrimItem:
482 start_selection_op (item, event, SelectionEndTrim);
486 if (Keyboard::modifier_state_contains
487 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
488 // contains and not equals because I can't use alt as a modifier alone.
489 start_selection_grab (item, event);
490 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
491 /* grab selection for moving */
492 start_selection_op (item, event, SelectionMove);
495 /* this was debated, but decided the more common action was to
496 make a new selection */
497 start_selection_op (item, event, CreateSelection);
502 start_selection_op (item, event, CreateSelection);
508 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
509 event->type == GDK_BUTTON_PRESS) {
511 start_rubberband_select (item, event);
513 } else if (event->type == GDK_BUTTON_PRESS) {
516 case FadeInHandleItem:
517 start_fade_in_grab (item, event);
520 case FadeOutHandleItem:
521 start_fade_out_grab (item, event);
525 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
526 start_region_copy_grab (item, event);
527 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
528 start_region_brush_grab (item, event);
530 start_region_grab (item, event);
534 case RegionViewNameHighlight:
535 start_trim (item, event);
540 /* rename happens on edit clicks */
541 start_trim (clicked_regionview->get_name_highlight(), event);
545 case GainAutomationControlPointItem:
546 case PanAutomationControlPointItem:
547 case RedirectAutomationControlPointItem:
548 start_control_point_grab (item, event);
552 case GainAutomationLineItem:
553 case PanAutomationLineItem:
554 case RedirectAutomationLineItem:
555 start_line_grab_from_line (item, event);
560 case AutomationTrackItem:
561 start_rubberband_select (item, event);
564 /* <CMT Additions> */
565 case ImageFrameHandleStartItem:
566 imageframe_start_handle_op(item, event) ;
569 case ImageFrameHandleEndItem:
570 imageframe_end_handle_op(item, event) ;
573 case MarkerViewHandleStartItem:
574 markerview_item_start_handle_op(item, event) ;
577 case MarkerViewHandleEndItem:
578 markerview_item_end_handle_op(item, event) ;
581 /* </CMT Additions> */
583 /* <CMT Additions> */
585 start_markerview_grab(item, event) ;
588 start_imageframe_grab(item, event) ;
590 /* </CMT Additions> */
606 // start_line_grab_from_regionview (item, event);
609 case GainControlPointItem:
610 start_control_point_grab (item, event);
614 start_line_grab_from_line (item, event);
617 case GainAutomationControlPointItem:
618 case PanAutomationControlPointItem:
619 case RedirectAutomationControlPointItem:
620 start_control_point_grab (item, event);
631 case GainAutomationControlPointItem:
632 case PanAutomationControlPointItem:
633 case RedirectAutomationControlPointItem:
634 start_control_point_grab (item, event);
637 case GainAutomationLineItem:
638 case PanAutomationLineItem:
639 case RedirectAutomationLineItem:
640 start_line_grab_from_line (item, event);
644 // XXX need automation mode to identify which
646 // start_line_grab_from_regionview (item, event);
656 if (event->type == GDK_BUTTON_PRESS) {
657 start_mouse_zoom (item, event);
664 if (item_type == RegionItem) {
665 start_time_fx (item, event);
670 /* handled in release */
679 switch (mouse_mode) {
681 if (event->type == GDK_BUTTON_PRESS) {
684 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
685 start_region_copy_grab (item, event);
687 start_region_grab (item, event);
691 case GainAutomationControlPointItem:
692 case PanAutomationControlPointItem:
693 case RedirectAutomationControlPointItem:
694 start_control_point_grab (item, event);
705 case RegionViewNameHighlight:
706 start_trim (item, event);
711 start_trim (clicked_regionview->get_name_highlight(), event);
722 if (event->type == GDK_BUTTON_PRESS) {
723 /* relax till release */
730 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
731 temporal_zoom_session();
733 temporal_zoom_to_frame (true, event_frame(event));
756 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
758 nframes_t where = event_frame (event, 0, 0);
760 /* no action if we're recording */
762 if (session && session->actively_recording()) {
766 /* first, see if we're finishing a drag ... */
768 if (drag_info.item) {
769 if (end_grab (item, event)) {
770 /* grab dragged, so do nothing else */
775 button_selection (item, event, item_type);
777 /* edit events get handled here */
779 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
785 case TempoMarkerItem:
786 edit_tempo_marker (item);
789 case MeterMarkerItem:
790 edit_meter_marker (item);
794 if (clicked_regionview->name_active()) {
795 return mouse_rename_region (item, event);
805 /* context menu events get handled here */
807 if (Keyboard::is_context_menu_event (&event->button)) {
809 if (drag_info.item == 0) {
811 /* no matter which button pops up the context menu, tell the menu
812 widget to use button 1 to drive menu selection.
817 case FadeInHandleItem:
819 case FadeOutHandleItem:
820 popup_fade_context_menu (1, event->button.time, item, item_type);
824 popup_track_context_menu (1, event->button.time, item_type, false, where);
828 case RegionViewNameHighlight:
830 popup_track_context_menu (1, event->button.time, item_type, false, where);
834 popup_track_context_menu (1, event->button.time, item_type, true, where);
837 case AutomationTrackItem:
838 popup_track_context_menu (1, event->button.time, item_type, false, where);
842 case RangeMarkerBarItem:
843 case TransportMarkerBarItem:
846 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
850 marker_context_menu (&event->button, item);
853 case TempoMarkerItem:
854 tm_marker_context_menu (&event->button, item);
857 case MeterMarkerItem:
858 tm_marker_context_menu (&event->button, item);
861 case CrossfadeViewItem:
862 popup_track_context_menu (1, event->button.time, item_type, false, where);
865 /* <CMT Additions> */
867 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
869 case ImageFrameTimeAxisItem:
870 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
873 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
875 case MarkerTimeAxisItem:
876 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
878 /* <CMT Additions> */
889 /* delete events get handled here */
891 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
894 case TempoMarkerItem:
895 remove_tempo_marker (item);
898 case MeterMarkerItem:
899 remove_meter_marker (item);
903 remove_marker (*item, event);
907 if (mouse_mode == MouseObject) {
908 remove_clicked_region ();
912 case GainControlPointItem:
913 if (mouse_mode == MouseGain) {
914 remove_gain_control_point (item, event);
918 case GainAutomationControlPointItem:
919 case PanAutomationControlPointItem:
920 case RedirectAutomationControlPointItem:
921 remove_control_point (item, event);
930 switch (event->button.button) {
934 /* see comments in button_press_handler */
936 case PlayheadCursorItem:
939 case GainAutomationLineItem:
940 case PanAutomationLineItem:
941 case RedirectAutomationLineItem:
942 case StartSelectionTrimItem:
943 case EndSelectionTrimItem:
947 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
948 snap_to (where, 0, true);
950 mouse_add_new_marker (where);
954 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
957 mouse_add_new_tempo_event (where);
961 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
969 switch (mouse_mode) {
972 case AutomationTrackItem:
973 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
987 // Gain only makes sense for audio regions
988 if ( ! dynamic_cast<AudioRegionView*>(clicked_regionview))
993 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
997 case AutomationTrackItem:
998 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
999 add_automation_event (item, event, where, event->button.y);
1008 switch (item_type) {
1010 audition_selected_region ();
1027 switch (mouse_mode) {
1030 switch (item_type) {
1032 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1034 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1037 // Button2 click is unused
1050 // x_style_paste (where, 1.0);
1070 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1076 switch (item_type) {
1077 case GainControlPointItem:
1078 if (mouse_mode == MouseGain) {
1079 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1080 cp->set_visible (true);
1084 at_y = cp->get_y ();
1085 cp->item->i2w (at_x, at_y);
1089 fraction = 1.0 - (cp->get_y() / cp->line.height());
1091 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1092 show_verbose_canvas_cursor ();
1094 if (is_drawable()) {
1095 track_canvas.get_window()->set_cursor (*fader_cursor);
1100 case GainAutomationControlPointItem:
1101 case PanAutomationControlPointItem:
1102 case RedirectAutomationControlPointItem:
1103 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1104 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1105 cp->set_visible (true);
1109 at_y = cp->get_y ();
1110 cp->item->i2w (at_x, at_y);
1114 fraction = 1.0 - (cp->get_y() / cp->line.height());
1116 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1117 show_verbose_canvas_cursor ();
1119 if (is_drawable()) {
1120 track_canvas.get_window()->set_cursor (*fader_cursor);
1126 if (mouse_mode == MouseGain) {
1127 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1129 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1130 if (is_drawable()) {
1131 track_canvas.get_window()->set_cursor (*fader_cursor);
1136 case GainAutomationLineItem:
1137 case RedirectAutomationLineItem:
1138 case PanAutomationLineItem:
1139 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1141 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1143 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1145 if (is_drawable()) {
1146 track_canvas.get_window()->set_cursor (*fader_cursor);
1151 case RegionViewNameHighlight:
1152 if (is_drawable() && mouse_mode == MouseObject) {
1153 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1157 case StartSelectionTrimItem:
1158 case EndSelectionTrimItem:
1159 /* <CMT Additions> */
1160 case ImageFrameHandleStartItem:
1161 case ImageFrameHandleEndItem:
1162 case MarkerViewHandleStartItem:
1163 case MarkerViewHandleEndItem:
1164 /* </CMT Additions> */
1166 if (is_drawable()) {
1167 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1171 case EditCursorItem:
1172 case PlayheadCursorItem:
1173 if (is_drawable()) {
1174 track_canvas.get_window()->set_cursor (*grabber_cursor);
1178 case RegionViewName:
1180 /* when the name is not an active item, the entire name highlight is for trimming */
1182 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1183 if (mouse_mode == MouseObject && is_drawable()) {
1184 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1190 case AutomationTrackItem:
1191 if (is_drawable()) {
1192 Gdk::Cursor *cursor;
1193 switch (mouse_mode) {
1195 cursor = selector_cursor;
1198 cursor = zoom_cursor;
1201 cursor = cross_hair_cursor;
1205 track_canvas.get_window()->set_cursor (*cursor);
1207 AutomationTimeAxisView* atv;
1208 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1209 clear_entered_track = false;
1210 set_entered_track (atv);
1216 case RangeMarkerBarItem:
1217 case TransportMarkerBarItem:
1220 if (is_drawable()) {
1221 time_canvas.get_window()->set_cursor (*timebar_cursor);
1226 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1229 marker->set_color_rgba (color_map[cEnteredMarker]);
1231 case MeterMarkerItem:
1232 case TempoMarkerItem:
1233 if (is_drawable()) {
1234 time_canvas.get_window()->set_cursor (*timebar_cursor);
1237 case FadeInHandleItem:
1238 case FadeOutHandleItem:
1239 if (mouse_mode == MouseObject) {
1240 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1242 rect->property_fill_color_rgba() = 0;
1243 rect->property_outline_pixels() = 1;
1252 /* second pass to handle entered track status in a comprehensible way.
1255 switch (item_type) {
1257 case GainAutomationLineItem:
1258 case RedirectAutomationLineItem:
1259 case PanAutomationLineItem:
1260 case GainControlPointItem:
1261 case GainAutomationControlPointItem:
1262 case PanAutomationControlPointItem:
1263 case RedirectAutomationControlPointItem:
1264 /* these do not affect the current entered track state */
1265 clear_entered_track = false;
1268 case AutomationTrackItem:
1269 /* handled above already */
1273 set_entered_track (0);
1281 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1290 switch (item_type) {
1291 case GainControlPointItem:
1292 case GainAutomationControlPointItem:
1293 case PanAutomationControlPointItem:
1294 case RedirectAutomationControlPointItem:
1295 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1296 if (cp->line.npoints() > 1) {
1297 if (!cp->selected) {
1298 cp->set_visible (false);
1302 if (is_drawable()) {
1303 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1306 hide_verbose_canvas_cursor ();
1309 case RegionViewNameHighlight:
1310 case StartSelectionTrimItem:
1311 case EndSelectionTrimItem:
1312 case EditCursorItem:
1313 case PlayheadCursorItem:
1314 /* <CMT Additions> */
1315 case ImageFrameHandleStartItem:
1316 case ImageFrameHandleEndItem:
1317 case MarkerViewHandleStartItem:
1318 case MarkerViewHandleEndItem:
1319 /* </CMT Additions> */
1320 if (is_drawable()) {
1321 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1326 case GainAutomationLineItem:
1327 case RedirectAutomationLineItem:
1328 case PanAutomationLineItem:
1329 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1331 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1333 line->property_fill_color_rgba() = al->get_line_color();
1335 if (is_drawable()) {
1336 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1340 case RegionViewName:
1341 /* see enter_handler() for notes */
1342 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1343 if (is_drawable() && mouse_mode == MouseObject) {
1344 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1349 case RangeMarkerBarItem:
1350 case TransportMarkerBarItem:
1354 if (is_drawable()) {
1355 time_canvas.get_window()->set_cursor (*timebar_cursor);
1360 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1363 loc = find_location_from_marker (marker, is_start);
1364 if (loc) location_flags_changed (loc, this);
1366 case MeterMarkerItem:
1367 case TempoMarkerItem:
1369 if (is_drawable()) {
1370 time_canvas.get_window()->set_cursor (*timebar_cursor);
1375 case FadeInHandleItem:
1376 case FadeOutHandleItem:
1377 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1379 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1381 rect->property_fill_color_rgba() = rv->get_fill_color();
1382 rect->property_outline_pixels() = 0;
1387 case AutomationTrackItem:
1388 if (is_drawable()) {
1389 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1390 clear_entered_track = true;
1391 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1403 Editor::left_automation_track ()
1405 if (clear_entered_track) {
1406 set_entered_track (0);
1407 clear_entered_track = false;
1413 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1417 /* We call this so that MOTION_NOTIFY events continue to be
1418 delivered to the canvas. We need to do this because we set
1419 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1420 the density of the events, at the expense of a round-trip
1421 to the server. Given that this will mostly occur on cases
1422 where DISPLAY = :0.0, and given the cost of what the motion
1423 event might do, its a good tradeoff.
1426 track_canvas.get_pointer (x, y);
1428 if (current_stepping_trackview) {
1429 /* don't keep the persistent stepped trackview if the mouse moves */
1430 current_stepping_trackview = 0;
1431 step_timeout.disconnect ();
1434 if (session && session->actively_recording()) {
1435 /* Sorry. no dragging stuff around while we record */
1439 drag_info.item_type = item_type;
1440 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1441 &drag_info.current_pointer_y);
1443 if (!from_autoscroll && drag_info.item) {
1444 /* item != 0 is the best test i can think of for dragging.
1446 if (!drag_info.move_threshold_passed) {
1448 bool x_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1449 bool y_threshold_passed = (abs ((int) (drag_info.current_pointer_y - drag_info.grab_y)) > 4);
1451 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1453 // and change the initial grab loc/frame if this drag info wants us to
1455 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1456 drag_info.grab_frame = drag_info.current_pointer_frame;
1457 drag_info.grab_x = drag_info.current_pointer_x;
1458 drag_info.grab_y = drag_info.current_pointer_y;
1459 drag_info.last_pointer_frame = drag_info.grab_frame;
1460 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1465 switch (item_type) {
1466 case PlayheadCursorItem:
1467 case EditCursorItem:
1469 case GainControlPointItem:
1470 case RedirectAutomationControlPointItem:
1471 case GainAutomationControlPointItem:
1472 case PanAutomationControlPointItem:
1473 case TempoMarkerItem:
1474 case MeterMarkerItem:
1475 case RegionViewNameHighlight:
1476 case StartSelectionTrimItem:
1477 case EndSelectionTrimItem:
1480 case RedirectAutomationLineItem:
1481 case GainAutomationLineItem:
1482 case PanAutomationLineItem:
1483 case FadeInHandleItem:
1484 case FadeOutHandleItem:
1485 /* <CMT Additions> */
1486 case ImageFrameHandleStartItem:
1487 case ImageFrameHandleEndItem:
1488 case MarkerViewHandleStartItem:
1489 case MarkerViewHandleEndItem:
1490 /* </CMT Additions> */
1491 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1492 (event->motion.state & Gdk::BUTTON2_MASK))) {
1493 if (!from_autoscroll) {
1494 maybe_autoscroll (event);
1496 (this->*(drag_info.motion_callback)) (item, event);
1505 switch (mouse_mode) {
1510 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1511 (event->motion.state & GDK_BUTTON2_MASK))) {
1512 if (!from_autoscroll) {
1513 maybe_autoscroll (event);
1515 (this->*(drag_info.motion_callback)) (item, event);
1526 track_canvas_motion (event);
1527 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1535 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1537 if (drag_info.item == 0) {
1538 fatal << _("programming error: start_grab called without drag item") << endmsg;
1544 cursor = grabber_cursor;
1547 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1549 if (event->button.button == 2) {
1550 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1551 drag_info.y_constrained = true;
1552 drag_info.x_constrained = false;
1554 drag_info.y_constrained = false;
1555 drag_info.x_constrained = true;
1558 drag_info.x_constrained = false;
1559 drag_info.y_constrained = false;
1562 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1563 drag_info.last_pointer_frame = drag_info.grab_frame;
1564 drag_info.current_pointer_frame = drag_info.grab_frame;
1565 drag_info.current_pointer_x = drag_info.grab_x;
1566 drag_info.current_pointer_y = drag_info.grab_y;
1567 drag_info.cumulative_x_drag = 0;
1568 drag_info.cumulative_y_drag = 0;
1569 drag_info.first_move = true;
1570 drag_info.move_threshold_passed = false;
1571 drag_info.want_move_threshold = false;
1572 drag_info.pointer_frame_offset = 0;
1573 drag_info.brushing = false;
1574 drag_info.copied_location = 0;
1576 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1578 event->button.time);
1580 if (session && session->transport_rolling()) {
1581 drag_info.was_rolling = true;
1583 drag_info.was_rolling = false;
1586 switch (snap_type) {
1587 case SnapToRegionStart:
1588 case SnapToRegionEnd:
1589 case SnapToRegionSync:
1590 case SnapToRegionBoundary:
1591 build_region_boundary_cache ();
1599 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1601 drag_info.item->ungrab (0);
1602 drag_info.item = new_item;
1605 cursor = grabber_cursor;
1608 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1612 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1614 bool did_drag = false;
1616 stop_canvas_autoscroll ();
1618 if (drag_info.item == 0) {
1622 drag_info.item->ungrab (event->button.time);
1624 if (drag_info.finished_callback) {
1625 (this->*(drag_info.finished_callback)) (item, event);
1628 did_drag = !drag_info.first_move;
1630 hide_verbose_canvas_cursor();
1633 drag_info.copy = false;
1634 drag_info.motion_callback = 0;
1635 drag_info.finished_callback = 0;
1636 drag_info.last_trackview = 0;
1637 drag_info.last_frame_position = 0;
1638 drag_info.grab_frame = 0;
1639 drag_info.last_pointer_frame = 0;
1640 drag_info.current_pointer_frame = 0;
1641 drag_info.brushing = false;
1643 if (drag_info.copied_location) {
1644 delete drag_info.copied_location;
1645 drag_info.copied_location = 0;
1652 Editor::set_edit_cursor (GdkEvent* event)
1654 nframes_t pointer_frame = event_frame (event);
1656 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1657 if (snap_type != SnapToEditCursor) {
1658 snap_to (pointer_frame);
1662 edit_cursor->set_position (pointer_frame);
1663 edit_cursor_clock.set (pointer_frame);
1667 Editor::set_playhead_cursor (GdkEvent* event)
1669 nframes_t pointer_frame = event_frame (event);
1671 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1672 snap_to (pointer_frame);
1676 session->request_locate (pointer_frame, session->transport_rolling());
1681 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1683 drag_info.item = item;
1684 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1685 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1689 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1690 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1694 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1696 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1700 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1702 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1704 nframes_t fade_length;
1706 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1707 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1713 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1717 if (pos < (arv->region()->position() + 64)) {
1718 fade_length = 64; // this should be a minimum defined somewhere
1719 } else if (pos > arv->region()->last_frame()) {
1720 fade_length = arv->region()->length();
1722 fade_length = pos - arv->region()->position();
1724 /* mapover the region selection */
1726 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1728 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1734 tmp->reset_fade_in_shape_width (fade_length);
1737 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1739 drag_info.first_move = false;
1743 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1745 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1747 nframes_t fade_length;
1749 if (drag_info.first_move) return;
1751 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1752 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1757 if (pos < (arv->region()->position() + 64)) {
1758 fade_length = 64; // this should be a minimum defined somewhere
1759 } else if (pos > arv->region()->last_frame()) {
1760 fade_length = arv->region()->length();
1762 fade_length = pos - arv->region()->position();
1765 begin_reversible_command (_("change fade in length"));
1767 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1769 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1775 AutomationList& alist = tmp->audio_region()->fade_in();
1776 XMLNode &before = alist.get_state();
1778 tmp->audio_region()->set_fade_in_length (fade_length);
1780 XMLNode &after = alist.get_state();
1781 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1784 commit_reversible_command ();
1788 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1790 drag_info.item = item;
1791 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1792 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1796 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1797 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1801 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1803 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1807 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1809 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1811 nframes_t fade_length;
1813 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1814 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1820 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1824 if (pos > (arv->region()->last_frame() - 64)) {
1825 fade_length = 64; // this should really be a minimum fade defined somewhere
1827 else if (pos < arv->region()->position()) {
1828 fade_length = arv->region()->length();
1831 fade_length = arv->region()->last_frame() - pos;
1834 /* mapover the region selection */
1836 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1838 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1844 tmp->reset_fade_out_shape_width (fade_length);
1847 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1849 drag_info.first_move = false;
1853 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1855 if (drag_info.first_move) return;
1857 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1859 nframes_t fade_length;
1861 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1862 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1868 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1872 if (pos > (arv->region()->last_frame() - 64)) {
1873 fade_length = 64; // this should really be a minimum fade defined somewhere
1875 else if (pos < arv->region()->position()) {
1876 fade_length = arv->region()->length();
1879 fade_length = arv->region()->last_frame() - pos;
1882 begin_reversible_command (_("change fade out length"));
1884 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1886 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1892 AutomationList& alist = tmp->audio_region()->fade_out();
1893 XMLNode &before = alist.get_state();
1895 tmp->audio_region()->set_fade_out_length (fade_length);
1897 XMLNode &after = alist.get_state();
1898 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1901 commit_reversible_command ();
1905 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1907 drag_info.item = item;
1908 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1909 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1913 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1914 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1918 Cursor* cursor = (Cursor *) drag_info.data;
1920 if (cursor == playhead_cursor) {
1921 _dragging_playhead = true;
1923 if (session && drag_info.was_rolling) {
1924 session->request_stop ();
1928 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1930 show_verbose_time_cursor (cursor->current_frame, 10);
1934 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1936 Cursor* cursor = (Cursor *) drag_info.data;
1937 nframes_t adjusted_frame;
1939 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1940 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1946 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1947 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1948 snap_to (adjusted_frame);
1952 if (adjusted_frame == drag_info.last_pointer_frame) return;
1954 cursor->set_position (adjusted_frame);
1956 if (cursor == edit_cursor) {
1957 edit_cursor_clock.set (cursor->current_frame);
1959 UpdateAllTransportClocks (cursor->current_frame);
1962 show_verbose_time_cursor (cursor->current_frame, 10);
1964 drag_info.last_pointer_frame = adjusted_frame;
1965 drag_info.first_move = false;
1969 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1971 if (drag_info.first_move) return;
1973 cursor_drag_motion_callback (item, event);
1975 _dragging_playhead = false;
1977 if (item == &playhead_cursor->canvas_item) {
1979 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1981 } else if (item == &edit_cursor->canvas_item) {
1982 edit_cursor->set_position (edit_cursor->current_frame);
1983 edit_cursor_clock.set (edit_cursor->current_frame);
1988 Editor::update_marker_drag_item (Location *location)
1990 double x1 = frame_to_pixel (location->start());
1991 double x2 = frame_to_pixel (location->end());
1993 if (location->is_mark()) {
1994 marker_drag_line_points.front().set_x(x1);
1995 marker_drag_line_points.back().set_x(x1);
1996 marker_drag_line->property_points() = marker_drag_line_points;
1999 range_marker_drag_rect->property_x1() = x1;
2000 range_marker_drag_rect->property_x2() = x2;
2005 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2009 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2010 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2016 Location *location = find_location_from_marker (marker, is_start);
2018 drag_info.item = item;
2019 drag_info.data = marker;
2020 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2021 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2025 drag_info.copied_location = new Location (*location);
2026 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2028 update_marker_drag_item (location);
2030 if (location->is_mark()) {
2031 marker_drag_line->show();
2032 marker_drag_line->raise_to_top();
2035 range_marker_drag_rect->show();
2036 range_marker_drag_rect->raise_to_top();
2039 if (is_start) show_verbose_time_cursor (location->start(), 10);
2040 else show_verbose_time_cursor (location->end(), 10);
2044 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2047 Marker* marker = (Marker *) drag_info.data;
2048 Location *real_location;
2049 Location *copy_location;
2051 bool move_both = false;
2055 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2056 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2062 nframes_t next = newframe;
2064 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2065 snap_to (newframe, 0, true);
2068 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2072 /* call this to find out if its the start or end */
2074 real_location = find_location_from_marker (marker, is_start);
2076 /* use the copy that we're "dragging" around */
2078 copy_location = drag_info.copied_location;
2080 f_delta = copy_location->end() - copy_location->start();
2082 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2086 if (copy_location->is_mark()) {
2089 copy_location->set_start (newframe);
2093 if (is_start) { // start-of-range marker
2096 copy_location->set_start (newframe);
2097 copy_location->set_end (newframe + f_delta);
2098 } else if (newframe < copy_location->end()) {
2099 copy_location->set_start (newframe);
2101 snap_to (next, 1, true);
2102 copy_location->set_end (next);
2103 copy_location->set_start (newframe);
2106 } else { // end marker
2109 copy_location->set_end (newframe);
2110 copy_location->set_start (newframe - f_delta);
2111 } else if (newframe > copy_location->start()) {
2112 copy_location->set_end (newframe);
2114 } else if (newframe > 0) {
2115 snap_to (next, -1, true);
2116 copy_location->set_start (next);
2117 copy_location->set_end (newframe);
2122 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2123 drag_info.first_move = false;
2125 update_marker_drag_item (copy_location);
2127 LocationMarkers* lm = find_location_markers (real_location);
2128 lm->set_position (copy_location->start(), copy_location->end());
2130 show_verbose_time_cursor (newframe, 10);
2134 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2136 if (drag_info.first_move) {
2137 marker_drag_motion_callback (item, event);
2141 Marker* marker = (Marker *) drag_info.data;
2145 begin_reversible_command ( _("move marker") );
2146 XMLNode &before = session->locations()->get_state();
2148 Location * location = find_location_from_marker (marker, is_start);
2151 if (location->is_mark()) {
2152 location->set_start (drag_info.copied_location->start());
2154 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2158 XMLNode &after = session->locations()->get_state();
2159 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2160 commit_reversible_command ();
2162 marker_drag_line->hide();
2163 range_marker_drag_rect->hide();
2167 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2170 MeterMarker* meter_marker;
2172 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2173 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2177 meter_marker = dynamic_cast<MeterMarker*> (marker);
2179 MetricSection& section (meter_marker->meter());
2181 if (!section.movable()) {
2185 drag_info.item = item;
2186 drag_info.data = marker;
2187 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2188 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2192 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2194 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2198 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2201 MeterMarker* meter_marker;
2203 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2204 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2208 meter_marker = dynamic_cast<MeterMarker*> (marker);
2210 // create a dummy marker for visual representation of moving the copy.
2211 // The actual copying is not done before we reach the finish callback.
2213 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2214 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2215 *new MeterSection(meter_marker->meter()));
2217 drag_info.item = &new_marker->the_item();
2218 drag_info.copy = true;
2219 drag_info.data = new_marker;
2220 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2221 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2225 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2227 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2231 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2233 MeterMarker* marker = (MeterMarker *) drag_info.data;
2234 nframes_t adjusted_frame;
2236 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2237 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2243 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2244 snap_to (adjusted_frame);
2247 if (adjusted_frame == drag_info.last_pointer_frame) return;
2249 marker->set_position (adjusted_frame);
2252 drag_info.last_pointer_frame = adjusted_frame;
2253 drag_info.first_move = false;
2255 show_verbose_time_cursor (adjusted_frame, 10);
2259 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2261 if (drag_info.first_move) return;
2263 meter_marker_drag_motion_callback (drag_info.item, event);
2265 MeterMarker* marker = (MeterMarker *) drag_info.data;
2268 TempoMap& map (session->tempo_map());
2269 map.bbt_time (drag_info.last_pointer_frame, when);
2271 if (drag_info.copy == true) {
2272 begin_reversible_command (_("copy meter mark"));
2273 XMLNode &before = map.get_state();
2274 map.add_meter (marker->meter(), when);
2275 XMLNode &after = map.get_state();
2276 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2277 commit_reversible_command ();
2279 // delete the dummy marker we used for visual representation of copying.
2280 // a new visual marker will show up automatically.
2283 begin_reversible_command (_("move meter mark"));
2284 XMLNode &before = map.get_state();
2285 map.move_meter (marker->meter(), when);
2286 XMLNode &after = map.get_state();
2287 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2288 commit_reversible_command ();
2293 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2296 TempoMarker* tempo_marker;
2298 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2299 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2303 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2304 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2308 MetricSection& section (tempo_marker->tempo());
2310 if (!section.movable()) {
2314 drag_info.item = item;
2315 drag_info.data = marker;
2316 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2317 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2321 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2322 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2326 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2329 TempoMarker* tempo_marker;
2331 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2332 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2336 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2337 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2341 // create a dummy marker for visual representation of moving the copy.
2342 // The actual copying is not done before we reach the finish callback.
2344 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2345 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2346 *new TempoSection(tempo_marker->tempo()));
2348 drag_info.item = &new_marker->the_item();
2349 drag_info.copy = true;
2350 drag_info.data = new_marker;
2351 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2352 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2356 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2358 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2362 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2364 TempoMarker* marker = (TempoMarker *) drag_info.data;
2365 nframes_t adjusted_frame;
2367 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2368 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2374 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2375 snap_to (adjusted_frame);
2378 if (adjusted_frame == drag_info.last_pointer_frame) return;
2380 /* OK, we've moved far enough to make it worth actually move the thing. */
2382 marker->set_position (adjusted_frame);
2384 show_verbose_time_cursor (adjusted_frame, 10);
2386 drag_info.last_pointer_frame = adjusted_frame;
2387 drag_info.first_move = false;
2391 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2393 if (drag_info.first_move) return;
2395 tempo_marker_drag_motion_callback (drag_info.item, event);
2397 TempoMarker* marker = (TempoMarker *) drag_info.data;
2400 TempoMap& map (session->tempo_map());
2401 map.bbt_time (drag_info.last_pointer_frame, when);
2403 if (drag_info.copy == true) {
2404 begin_reversible_command (_("copy tempo mark"));
2405 XMLNode &before = map.get_state();
2406 map.add_tempo (marker->tempo(), when);
2407 XMLNode &after = map.get_state();
2408 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2409 commit_reversible_command ();
2411 // delete the dummy marker we used for visual representation of copying.
2412 // a new visual marker will show up automatically.
2415 begin_reversible_command (_("move tempo mark"));
2416 XMLNode &before = map.get_state();
2417 map.move_tempo (marker->tempo(), when);
2418 XMLNode &after = map.get_state();
2419 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2420 commit_reversible_command ();
2425 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2427 ControlPoint* control_point;
2429 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2430 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2434 // We shouldn't remove the first or last gain point
2435 if (control_point->line.is_last_point(*control_point) ||
2436 control_point->line.is_first_point(*control_point)) {
2440 control_point->line.remove_point (*control_point);
2444 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2446 ControlPoint* control_point;
2448 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2449 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2453 control_point->line.remove_point (*control_point);
2457 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2459 ControlPoint* control_point;
2461 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2462 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2466 drag_info.item = item;
2467 drag_info.data = control_point;
2468 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2469 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2471 start_grab (event, fader_cursor);
2473 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2475 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2476 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2477 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2479 show_verbose_canvas_cursor ();
2483 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2485 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2487 double cx = drag_info.current_pointer_x;
2488 double cy = drag_info.current_pointer_y;
2490 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2491 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2493 if (drag_info.x_constrained) {
2494 cx = drag_info.grab_x;
2496 if (drag_info.y_constrained) {
2497 cy = drag_info.grab_y;
2500 cp->line.parent_group().w2i (cx, cy);
2504 cy = min ((double) cp->line.height(), cy);
2506 //translate cx to frames
2507 nframes_t cx_frames = unit_to_frame (cx);
2509 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2510 snap_to (cx_frames);
2513 float fraction = 1.0 - (cy / cp->line.height());
2517 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2523 cp->line.point_drag (*cp, cx_frames , fraction, push);
2525 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2527 drag_info.first_move = false;
2531 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2533 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2535 if (drag_info.first_move) {
2539 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2540 reset_point_selection ();
2544 control_point_drag_motion_callback (item, event);
2546 cp->line.end_drag (cp);
2550 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2552 switch (mouse_mode) {
2554 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2555 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2563 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2567 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2568 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2572 start_line_grab (al, event);
2576 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2580 nframes_t frame_within_region;
2582 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2586 cx = event->button.x;
2587 cy = event->button.y;
2588 line->parent_group().w2i (cx, cy);
2589 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2591 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2592 current_line_drag_info.after)) {
2593 /* no adjacent points */
2597 drag_info.item = &line->grab_item();
2598 drag_info.data = line;
2599 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2600 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2602 start_grab (event, fader_cursor);
2604 double fraction = 1.0 - (cy / line->height());
2606 line->start_drag (0, drag_info.grab_frame, fraction);
2608 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2609 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2610 show_verbose_canvas_cursor ();
2614 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2616 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2617 double cx = drag_info.current_pointer_x;
2618 double cy = drag_info.current_pointer_y;
2620 line->parent_group().w2i (cx, cy);
2623 fraction = 1.0 - (cy / line->height());
2627 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2633 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2635 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2639 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2641 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2642 line_drag_motion_callback (item, event);
2647 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2649 if (selection->regions.empty() || clicked_regionview == 0) {
2653 drag_info.copy = false;
2654 drag_info.item = item;
2655 drag_info.data = clicked_regionview;
2656 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2657 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2662 TimeAxisView* tvp = clicked_trackview;
2663 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2665 if (tv && tv->is_audio_track()) {
2666 speed = tv->get_diskstream()->speed();
2669 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2670 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2671 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2672 // we want a move threshold
2673 drag_info.want_move_threshold = true;
2675 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2677 begin_reversible_command (_("move region(s)"));
2681 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2683 if (selection->regions.empty() || clicked_regionview == 0) {
2687 drag_info.copy = true;
2688 drag_info.item = item;
2689 drag_info.data = clicked_regionview;
2693 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2694 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2697 if (atv && atv->is_audio_track()) {
2698 speed = atv->get_diskstream()->speed();
2701 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2702 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2703 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2704 // we want a move threshold
2705 drag_info.want_move_threshold = true;
2706 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2707 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2708 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2712 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2714 if (selection->regions.empty() || clicked_regionview == 0) {
2718 drag_info.copy = false;
2719 drag_info.item = item;
2720 drag_info.data = clicked_regionview;
2721 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2722 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2727 TimeAxisView* tvp = clicked_trackview;
2728 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2730 if (tv && tv->is_audio_track()) {
2731 speed = tv->get_diskstream()->speed();
2734 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2735 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2736 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2737 // we want a move threshold
2738 drag_info.want_move_threshold = true;
2739 drag_info.brushing = true;
2741 begin_reversible_command (_("Drag region brush"));
2745 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2749 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2750 nframes_t pending_region_position = 0;
2751 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2752 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2753 bool clamp_y_axis = false;
2754 vector<int32_t> height_list(512) ;
2755 vector<int32_t>::iterator j;
2757 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2759 drag_info.want_move_threshold = false; // don't copy again
2761 /* duplicate the region(s) */
2763 vector<RegionView*> new_regionviews;
2765 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2768 AudioRegionView* arv;
2773 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
2774 /* XXX handle MIDI here */
2778 nrv = new AudioRegionView (*arv);
2779 nrv->get_canvas_group()->show ();
2781 new_regionviews.push_back (nrv);
2784 if (new_regionviews.empty()) {
2788 /* reset selection to new regionviews */
2790 selection->set (new_regionviews);
2792 /* reset drag_info data to reflect the fact that we are dragging the copies */
2794 drag_info.data = new_regionviews.front();
2796 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2799 /* Which trackview is this ? */
2801 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2802 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2804 /* The region motion is only processed if the pointer is over
2808 if (!tv || !tv->is_audio_track()) {
2809 /* To make sure we hide the verbose canvas cursor when the mouse is
2810 not held over and audiotrack.
2812 hide_verbose_canvas_cursor ();
2816 original_pointer_order = drag_info.last_trackview->order;
2818 /************************************************************
2820 ************************************************************/
2822 if (drag_info.brushing) {
2823 clamp_y_axis = true;
2828 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2830 int32_t children = 0, numtracks = 0;
2831 // XXX hard coding track limit, oh my, so very very bad
2832 bitset <1024> tracks (0x00);
2833 /* get a bitmask representing the visible tracks */
2835 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2836 TimeAxisView *tracklist_timeview;
2837 tracklist_timeview = (*i);
2838 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2839 list<TimeAxisView*> children_list;
2841 /* zeroes are audio tracks. ones are other types. */
2843 if (!atv2->hidden()) {
2845 if (visible_y_high < atv2->order) {
2846 visible_y_high = atv2->order;
2848 if (visible_y_low > atv2->order) {
2849 visible_y_low = atv2->order;
2852 if (!atv2->is_audio_track()) {
2853 tracks = tracks |= (0x01 << atv2->order);
2856 height_list[atv2->order] = (*i)->height;
2858 if ((children_list = atv2->get_child_list()).size() > 0) {
2859 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2860 tracks = tracks |= (0x01 << (atv2->order + children));
2861 height_list[atv2->order + children] = (*j)->height;
2869 /* find the actual span according to the canvas */
2871 canvas_pointer_y_span = pointer_y_span;
2872 if (drag_info.last_trackview->order >= tv->order) {
2874 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2875 if (height_list[y] == 0 ) {
2876 canvas_pointer_y_span--;
2881 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2882 if ( height_list[y] == 0 ) {
2883 canvas_pointer_y_span++;
2888 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2889 RegionView* rv2 = (*i);
2890 double ix1, ix2, iy1, iy2;
2893 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2894 rv2->get_canvas_group()->i2w (ix1, iy1);
2895 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2896 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2898 if (atv2->order != original_pointer_order) {
2899 /* this isn't the pointer track */
2901 if (canvas_pointer_y_span > 0) {
2903 /* moving up the canvas */
2904 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2906 int32_t visible_tracks = 0;
2907 while (visible_tracks < canvas_pointer_y_span ) {
2910 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2911 /* we're passing through a hidden track */
2916 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2917 clamp_y_axis = true;
2921 clamp_y_axis = true;
2924 } else if (canvas_pointer_y_span < 0) {
2926 /*moving down the canvas*/
2928 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2931 int32_t visible_tracks = 0;
2933 while (visible_tracks > canvas_pointer_y_span ) {
2936 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2940 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2941 clamp_y_axis = true;
2946 clamp_y_axis = true;
2952 /* this is the pointer's track */
2953 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2954 clamp_y_axis = true;
2955 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2956 clamp_y_axis = true;
2964 } else if (drag_info.last_trackview == tv) {
2965 clamp_y_axis = true;
2969 if (!clamp_y_axis) {
2970 drag_info.last_trackview = tv;
2973 /************************************************************
2975 ************************************************************/
2977 /* compute the amount of pointer motion in frames, and where
2978 the region would be if we moved it by that much.
2981 if (drag_info.move_threshold_passed) {
2983 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2985 nframes_t sync_frame;
2986 nframes_t sync_offset;
2989 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2991 sync_offset = rv->region()->sync_offset (sync_dir);
2992 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
2994 /* we snap if the snap modifier is not enabled.
2997 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2998 snap_to (sync_frame);
3001 if (sync_frame - sync_offset <= sync_frame) {
3002 pending_region_position = sync_frame - (sync_dir*sync_offset);
3004 pending_region_position = 0;
3008 pending_region_position = 0;
3011 if (pending_region_position > max_frames - rv->region()->length()) {
3012 pending_region_position = drag_info.last_frame_position;
3015 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3017 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3019 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3020 to make it appear at the new location.
3023 if (pending_region_position > drag_info.last_frame_position) {
3024 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3026 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3029 drag_info.last_frame_position = pending_region_position;
3036 /* threshold not passed */
3041 /*************************************************************
3043 ************************************************************/
3045 if (x_delta == 0 && (pointer_y_span == 0)) {
3046 /* haven't reached next snap point, and we're not switching
3047 trackviews. nothing to do.
3053 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3055 RegionView* rv2 = (*i);
3057 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3059 double ix1, ix2, iy1, iy2;
3060 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3061 rv2->get_canvas_group()->i2w (ix1, iy1);
3070 /*************************************************************
3072 ************************************************************/
3076 if (drag_info.first_move) {
3077 if (drag_info.move_threshold_passed) {
3088 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3089 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3091 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3093 RegionView* rv = (*i);
3094 double ix1, ix2, iy1, iy2;
3095 int32_t temp_pointer_y_span = pointer_y_span;
3097 /* get item BBox, which will be relative to parent. so we have
3098 to query on a child, then convert to world coordinates using
3102 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3103 rv->get_canvas_group()->i2w (ix1, iy1);
3104 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3105 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3106 AudioTimeAxisView* temp_atv;
3108 if ((pointer_y_span != 0) && !clamp_y_axis) {
3111 for (j = height_list.begin(); j!= height_list.end(); j++) {
3112 if (x == canvas_atv->order) {
3113 /* we found the track the region is on */
3114 if (x != original_pointer_order) {
3115 /*this isn't from the same track we're dragging from */
3116 temp_pointer_y_span = canvas_pointer_y_span;
3118 while (temp_pointer_y_span > 0) {
3119 /* we're moving up canvas-wise,
3120 so we need to find the next track height
3122 if (j != height_list.begin()) {
3125 if (x != original_pointer_order) {
3126 /* we're not from the dragged track, so ignore hidden tracks. */
3128 temp_pointer_y_span++;
3132 temp_pointer_y_span--;
3134 while (temp_pointer_y_span < 0) {
3136 if (x != original_pointer_order) {
3138 temp_pointer_y_span--;
3142 if (j != height_list.end()) {
3145 temp_pointer_y_span++;
3147 /* find out where we'll be when we move and set height accordingly */
3149 tvp2 = trackview_by_y_position (iy1 + y_delta);
3150 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3151 rv->set_height (temp_atv->height);
3153 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3154 personally, i think this can confuse things, but never mind.
3157 //const GdkColor& col (temp_atv->view->get_region_color());
3158 //rv->set_color (const_cast<GdkColor&>(col));
3165 /* prevent the regionview from being moved to before
3166 the zero position on the canvas.
3171 if (-x_delta > ix1) {
3174 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3175 x_delta = max_frames - rv->region()->last_frame();
3178 if (drag_info.first_move) {
3180 /* hide any dependent views */
3182 rv->get_time_axis_view().hide_dependent_views (*rv);
3184 /* this is subtle. raising the regionview itself won't help,
3185 because raise_to_top() just puts the item on the top of
3186 its parent's stack. so, we need to put the trackview canvas_display group
3187 on the top, since its parent is the whole canvas.
3190 rv->get_canvas_group()->raise_to_top();
3191 rv->get_time_axis_view().canvas_display->raise_to_top();
3192 cursor_group->raise_to_top();
3194 rv->fake_set_opaque (true);
3197 if (drag_info.brushing) {
3198 mouse_brush_insert_region (rv, pending_region_position);
3200 rv->move (x_delta, y_delta);
3203 } /* foreach region */
3207 if (drag_info.first_move && drag_info.move_threshold_passed) {
3208 cursor_group->raise_to_top();
3209 drag_info.first_move = false;
3212 if (x_delta != 0 && !drag_info.brushing) {
3213 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3218 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3221 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3222 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3223 bool nocommit = true;
3225 RouteTimeAxisView* atv;
3226 bool regionview_y_movement;
3227 bool regionview_x_movement;
3229 /* first_move is set to false if the regionview has been moved in the
3233 if (drag_info.first_move) {
3240 /* The regionview has been moved at some stage during the grab so we need
3241 to account for any mouse movement between this event and the last one.
3244 region_drag_motion_callback (item, event);
3246 if (drag_info.brushing) {
3247 /* all changes were made during motion event handlers */
3251 /* adjust for track speed */
3254 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3255 if (atv && atv->get_diskstream()) {
3256 speed = atv->get_diskstream()->speed();
3259 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3260 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3262 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3263 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3267 if (drag_info.copy) {
3268 if (drag_info.x_constrained) {
3269 op_string = _("fixed time region copy");
3271 op_string = _("region copy");
3274 if (drag_info.x_constrained) {
3275 op_string = _("fixed time region drag");
3277 op_string = _("region drag");
3281 begin_reversible_command (op_string);
3283 if (regionview_y_movement) {
3285 /* moved to a different audio track. */
3287 list<RegionView*> new_selection;
3288 new_selection = selection->regions.by_layer();
3289 selection->clear_regions ();
3291 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
3293 RegionView* rv = (*i);
3295 double ix1, ix2, iy1, iy2;
3297 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3298 rv->get_canvas_group()->i2w (ix1, iy1);
3299 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3300 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3302 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3303 boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
3305 where = (nframes_t) (unit_to_frame (ix1) * speed);
3306 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3308 if (!drag_info.copy) {
3310 /* the region that used to be in the old playlist is not
3311 moved to the new one - we make a copy of it. as a result,
3312 any existing editor for the region should no longer be
3316 rv->hide_region_editor();
3317 rv->fake_set_opaque (false);
3319 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3320 from_playlist->remove_region ((rv->region()));
3321 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3324 latest_regionview = 0;
3326 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3327 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3328 to_playlist->add_region (new_region, where);
3329 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3332 if (latest_regionview) {
3333 selection->add (latest_regionview);
3336 if (drag_info.copy) {
3337 // get rid of the copy
3344 /* motion within a single track */
3346 list<RegionView*> regions = selection->regions.by_layer();
3348 if (drag_info.copy) {
3349 selection->clear_regions();
3352 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3356 if (rv->region()->locked()) {
3361 if (regionview_x_movement) {
3362 double ownspeed = 1.0;
3363 atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3365 if (atv && atv->get_diskstream()) {
3366 ownspeed = atv->get_diskstream()->speed();
3369 /* base the new region position on the current position of the regionview.*/
3371 double ix1, ix2, iy1, iy2;
3373 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3374 rv->get_canvas_group()->i2w (ix1, iy1);
3375 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3379 where = rv->region()->position();
3382 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3386 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3388 if (drag_info.copy) {
3390 boost::shared_ptr<Region> newregion;
3391 boost::shared_ptr<Region> ar;
3393 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3394 newregion = RegionFactory::create (ar);
3396 /* XXX MIDI HERE drobilla */
3402 latest_regionview = 0;
3403 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3404 to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
3407 if (latest_regionview) {
3408 atv->reveal_dependent_views (*latest_regionview);
3409 selection->add (latest_regionview);
3412 /* if the original region was locked, we don't care for the new one */
3414 newregion->set_locked (false);
3418 /* just change the model */
3420 rv->region()->set_position (where, (void*) this);
3426 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3428 /* get rid of the copy */
3430 if (drag_info.copy) {
3439 commit_reversible_command ();
3444 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3446 /* Either add to or set the set the region selection, unless
3447 this is an alignment click (control used)
3450 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3451 TimeAxisView* tv = &rv.get_time_axis_view();
3452 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3454 if (atv && atv->is_audio_track()) {
3455 speed = atv->get_diskstream()->speed();
3458 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3460 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3462 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3464 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3468 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3474 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3480 nframes_t frame_rate;
3487 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3488 case AudioClock::BBT:
3489 session->bbt_time (frame, bbt);
3490 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3493 case AudioClock::SMPTE:
3494 session->smpte_time (frame, smpte);
3495 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3498 case AudioClock::MinSec:
3499 /* XXX this is copied from show_verbose_duration_cursor() */
3500 frame_rate = session->frame_rate();
3501 hours = frame / (frame_rate * 3600);
3502 frame = frame % (frame_rate * 3600);
3503 mins = frame / (frame_rate * 60);
3504 frame = frame % (frame_rate * 60);
3505 secs = (float) frame / (float) frame_rate;
3506 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3510 snprintf (buf, sizeof(buf), "%u", frame);
3514 if (xpos >= 0 && ypos >=0) {
3515 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3518 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3520 show_verbose_canvas_cursor ();
3524 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3531 nframes_t distance, frame_rate;
3533 Meter meter_at_start(session->tempo_map().meter_at(start));
3539 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3540 case AudioClock::BBT:
3541 session->bbt_time (start, sbbt);
3542 session->bbt_time (end, ebbt);
3545 /* XXX this computation won't work well if the
3546 user makes a selection that spans any meter changes.
3549 ebbt.bars -= sbbt.bars;
3550 if (ebbt.beats >= sbbt.beats) {
3551 ebbt.beats -= sbbt.beats;
3554 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3556 if (ebbt.ticks >= sbbt.ticks) {
3557 ebbt.ticks -= sbbt.ticks;
3560 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3563 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3566 case AudioClock::SMPTE:
3567 session->smpte_duration (end - start, smpte);
3568 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3571 case AudioClock::MinSec:
3572 /* XXX this stuff should be elsewhere.. */
3573 distance = end - start;
3574 frame_rate = session->frame_rate();
3575 hours = distance / (frame_rate * 3600);
3576 distance = distance % (frame_rate * 3600);
3577 mins = distance / (frame_rate * 60);
3578 distance = distance % (frame_rate * 60);
3579 secs = (float) distance / (float) frame_rate;
3580 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3584 snprintf (buf, sizeof(buf), "%u", end - start);
3588 if (xpos >= 0 && ypos >=0) {
3589 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3592 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3594 show_verbose_canvas_cursor ();
3598 Editor::collect_new_region_view (RegionView* rv)
3600 latest_regionview = rv;
3604 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3606 if (clicked_regionview == 0) {
3610 /* lets try to create new Region for the selection */
3612 vector<boost::shared_ptr<AudioRegion> > new_regions;
3613 create_region_from_selection (new_regions);
3615 if (new_regions.empty()) {
3619 /* XXX fix me one day to use all new regions */
3621 boost::shared_ptr<Region> region (new_regions.front());
3623 /* add it to the current stream/playlist.
3625 tricky: the streamview for the track will add a new regionview. we will
3626 catch the signal it sends when it creates the regionview to
3627 set the regionview we want to then drag.
3630 latest_regionview = 0;
3631 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3633 /* A selection grab currently creates two undo/redo operations, one for
3634 creating the new region and another for moving it.
3637 begin_reversible_command (_("selection grab"));
3639 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3641 XMLNode *before = &(playlist->get_state());
3642 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3643 XMLNode *after = &(playlist->get_state());
3644 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3646 commit_reversible_command ();
3650 if (latest_regionview == 0) {
3651 /* something went wrong */
3655 /* we need to deselect all other regionviews, and select this one
3656 i'm ignoring undo stuff, because the region creation will take care of it */
3657 selection->set (latest_regionview);
3659 drag_info.item = latest_regionview->get_canvas_group();
3660 drag_info.data = latest_regionview;
3661 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3662 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3666 drag_info.last_trackview = clicked_trackview;
3667 drag_info.last_frame_position = latest_regionview->region()->position();
3668 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3670 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3674 Editor::cancel_selection ()
3676 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3677 (*i)->hide_selection ();
3679 begin_reversible_command (_("cancel selection"));
3680 selection->clear ();
3681 clicked_selection = 0;
3682 commit_reversible_command ();
3686 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3688 nframes_t start = 0;
3695 drag_info.item = item;
3696 drag_info.motion_callback = &Editor::drag_selection;
3697 drag_info.finished_callback = &Editor::end_selection_op;
3702 case CreateSelection:
3703 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3704 drag_info.copy = true;
3706 drag_info.copy = false;
3708 start_grab (event, selector_cursor);
3711 case SelectionStartTrim:
3712 if (clicked_trackview) {
3713 clicked_trackview->order_selection_trims (item, true);
3715 start_grab (event, trimmer_cursor);
3716 start = selection->time[clicked_selection].start;
3717 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3720 case SelectionEndTrim:
3721 if (clicked_trackview) {
3722 clicked_trackview->order_selection_trims (item, false);
3724 start_grab (event, trimmer_cursor);
3725 end = selection->time[clicked_selection].end;
3726 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3730 start = selection->time[clicked_selection].start;
3732 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3736 if (selection_op == SelectionMove) {
3737 show_verbose_time_cursor(start, 10);
3739 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3744 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3746 nframes_t start = 0;
3749 nframes_t pending_position;
3751 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3752 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3755 pending_position = 0;
3758 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3759 snap_to (pending_position);
3762 /* only alter selection if the current frame is
3763 different from the last frame position (adjusted)
3766 if (pending_position == drag_info.last_pointer_frame) return;
3768 switch (selection_op) {
3769 case CreateSelection:
3771 if (drag_info.first_move) {
3772 snap_to (drag_info.grab_frame);
3775 if (pending_position < drag_info.grab_frame) {
3776 start = pending_position;
3777 end = drag_info.grab_frame;
3779 end = pending_position;
3780 start = drag_info.grab_frame;
3783 /* first drag: Either add to the selection
3784 or create a new selection->
3787 if (drag_info.first_move) {
3789 begin_reversible_command (_("range selection"));
3791 if (drag_info.copy) {
3792 /* adding to the selection */
3793 clicked_selection = selection->add (start, end);
3794 drag_info.copy = false;
3796 /* new selection-> */
3797 clicked_selection = selection->set (clicked_trackview, start, end);
3802 case SelectionStartTrim:
3804 if (drag_info.first_move) {
3805 begin_reversible_command (_("trim selection start"));
3808 start = selection->time[clicked_selection].start;
3809 end = selection->time[clicked_selection].end;
3811 if (pending_position > end) {
3814 start = pending_position;
3818 case SelectionEndTrim:
3820 if (drag_info.first_move) {
3821 begin_reversible_command (_("trim selection end"));
3824 start = selection->time[clicked_selection].start;
3825 end = selection->time[clicked_selection].end;
3827 if (pending_position < start) {
3830 end = pending_position;
3837 if (drag_info.first_move) {
3838 begin_reversible_command (_("move selection"));
3841 start = selection->time[clicked_selection].start;
3842 end = selection->time[clicked_selection].end;
3844 length = end - start;
3846 start = pending_position;
3849 end = start + length;
3854 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3855 start_canvas_autoscroll (1);
3859 selection->replace (clicked_selection, start, end);
3862 drag_info.last_pointer_frame = pending_position;
3863 drag_info.first_move = false;
3865 if (selection_op == SelectionMove) {
3866 show_verbose_time_cursor(start, 10);
3868 show_verbose_time_cursor(pending_position, 10);
3873 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3875 if (!drag_info.first_move) {
3876 drag_selection (item, event);
3877 /* XXX this is not object-oriented programming at all. ick */
3878 if (selection->time.consolidate()) {
3879 selection->TimeChanged ();
3881 commit_reversible_command ();
3883 /* just a click, no pointer movement.*/
3885 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3887 selection->clear_time();
3892 /* XXX what happens if its a music selection? */
3893 session->set_audio_range (selection->time);
3894 stop_canvas_autoscroll ();
3898 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3901 TimeAxisView* tvp = clicked_trackview;
3902 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3904 if (tv && tv->is_audio_track()) {
3905 speed = tv->get_diskstream()->speed();
3908 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3909 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3910 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3912 //drag_info.item = clicked_regionview->get_name_highlight();
3913 drag_info.item = item;
3914 drag_info.motion_callback = &Editor::trim_motion_callback;
3915 drag_info.finished_callback = &Editor::trim_finished_callback;
3917 start_grab (event, trimmer_cursor);
3919 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3920 trim_op = ContentsTrim;
3922 /* These will get overridden for a point trim.*/
3923 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3924 /* closer to start */
3925 trim_op = StartTrim;
3926 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3934 show_verbose_time_cursor(region_start, 10);
3937 show_verbose_time_cursor(region_end, 10);
3940 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3946 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3948 RegionView* rv = clicked_regionview;
3949 nframes_t frame_delta = 0;
3950 bool left_direction;
3951 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3953 /* snap modifier works differently here..
3954 its' current state has to be passed to the
3955 various trim functions in order to work properly
3959 TimeAxisView* tvp = clicked_trackview;
3960 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3961 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3963 if (tv && tv->is_audio_track()) {
3964 speed = tv->get_diskstream()->speed();
3967 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3968 left_direction = true;
3970 left_direction = false;
3974 snap_to (drag_info.current_pointer_frame);
3977 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3981 if (drag_info.first_move) {
3987 trim_type = "Region start trim";
3990 trim_type = "Region end trim";
3993 trim_type = "Region content trim";
3997 begin_reversible_command (trim_type);
3999 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4000 (*i)->fake_set_opaque(false);
4001 (*i)->region()->freeze ();
4003 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4005 arv->temporarily_hide_envelope ();
4007 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4008 insert_result = motion_frozen_playlists.insert (pl);
4009 if (insert_result.second) {
4010 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4015 if (left_direction) {
4016 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4018 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4023 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4026 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4027 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4033 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4036 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4037 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4044 bool swap_direction = false;
4046 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4047 swap_direction = true;
4050 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4051 i != selection->regions.by_layer().end(); ++i)
4053 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4061 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4064 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4067 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4071 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4072 drag_info.first_move = false;
4076 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4078 boost::shared_ptr<Region> region (rv.region());
4080 if (region->locked()) {
4084 nframes_t new_bound;
4087 TimeAxisView* tvp = clicked_trackview;
4088 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4090 if (tv && tv->is_audio_track()) {
4091 speed = tv->get_diskstream()->speed();
4094 if (left_direction) {
4095 if (swap_direction) {
4096 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4098 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4101 if (swap_direction) {
4102 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4104 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4109 snap_to (new_bound);
4111 region->trim_start ((nframes_t) (new_bound * speed), this);
4112 rv.region_changed (StartChanged);
4116 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4118 boost::shared_ptr<Region> region (rv.region());
4120 if (region->locked()) {
4124 nframes_t new_bound;
4127 TimeAxisView* tvp = clicked_trackview;
4128 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4130 if (tv && tv->is_audio_track()) {
4131 speed = tv->get_diskstream()->speed();
4134 if (left_direction) {
4135 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4137 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4141 snap_to (new_bound, (left_direction ? 0 : 1));
4144 region->trim_front ((nframes_t) (new_bound * speed), this);
4146 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4150 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4152 boost::shared_ptr<Region> region (rv.region());
4154 if (region->locked()) {
4158 nframes_t new_bound;
4161 TimeAxisView* tvp = clicked_trackview;
4162 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4164 if (tv && tv->is_audio_track()) {
4165 speed = tv->get_diskstream()->speed();
4168 if (left_direction) {
4169 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4171 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4175 snap_to (new_bound);
4177 region->trim_end ((nframes_t) (new_bound * speed), this);
4178 rv.region_changed (LengthChanged);
4182 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4184 if (!drag_info.first_move) {
4185 trim_motion_callback (item, event);
4187 if (!clicked_regionview->get_selected()) {
4188 thaw_region_after_trim (*clicked_regionview);
4191 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4192 i != selection->regions.by_layer().end(); ++i)
4194 thaw_region_after_trim (**i);
4195 (*i)->fake_set_opaque (true);
4199 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4201 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4204 motion_frozen_playlists.clear ();
4206 commit_reversible_command();
4208 /* no mouse movement */
4214 Editor::point_trim (GdkEvent* event)
4216 RegionView* rv = clicked_regionview;
4217 nframes_t new_bound = drag_info.current_pointer_frame;
4219 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4220 snap_to (new_bound);
4223 /* Choose action dependant on which button was pressed */
4224 switch (event->button.button) {
4226 trim_op = StartTrim;
4227 begin_reversible_command (_("Start point trim"));
4229 if (rv->get_selected()) {
4231 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4232 i != selection->regions.by_layer().end(); ++i)
4234 if (!(*i)->region()->locked()) {
4235 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4236 XMLNode &before = pl->get_state();
4237 (*i)->region()->trim_front (new_bound, this);
4238 XMLNode &after = pl->get_state();
4239 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4245 if (!rv->region()->locked()) {
4246 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4247 XMLNode &before = pl->get_state();
4248 rv->region()->trim_front (new_bound, this);
4249 XMLNode &after = pl->get_state();
4250 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4254 commit_reversible_command();
4259 begin_reversible_command (_("End point trim"));
4261 if (rv->get_selected()) {
4263 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4265 if (!(*i)->region()->locked()) {
4266 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4267 XMLNode &before = pl->get_state();
4268 (*i)->region()->trim_end (new_bound, this);
4269 XMLNode &after = pl->get_state();
4270 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4276 if (!rv->region()->locked()) {
4277 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4278 XMLNode &before = pl->get_state();
4279 rv->region()->trim_end (new_bound, this);
4280 XMLNode &after = pl->get_state();
4281 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4285 commit_reversible_command();
4294 Editor::thaw_region_after_trim (RegionView& rv)
4296 boost::shared_ptr<Region> region (rv.region());
4298 if (region->locked()) {
4302 region->thaw (_("trimmed region"));
4303 XMLNode &after = region->playlist()->get_state();
4304 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4306 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4308 arv->unhide_envelope ();
4312 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4317 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4318 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4322 Location* location = find_location_from_marker (marker, is_start);
4323 location->set_hidden (true, this);
4328 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4334 drag_info.item = item;
4335 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4336 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4338 range_marker_op = op;
4340 if (!temp_location) {
4341 temp_location = new Location;
4345 case CreateRangeMarker:
4346 case CreateTransportMarker:
4348 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4349 drag_info.copy = true;
4351 drag_info.copy = false;
4353 start_grab (event, selector_cursor);
4357 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4362 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4364 nframes_t start = 0;
4366 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4368 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4369 snap_to (drag_info.current_pointer_frame);
4372 /* only alter selection if the current frame is
4373 different from the last frame position.
4376 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4378 switch (range_marker_op) {
4379 case CreateRangeMarker:
4380 case CreateTransportMarker:
4381 if (drag_info.first_move) {
4382 snap_to (drag_info.grab_frame);
4385 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4386 start = drag_info.current_pointer_frame;
4387 end = drag_info.grab_frame;
4389 end = drag_info.current_pointer_frame;
4390 start = drag_info.grab_frame;
4393 /* first drag: Either add to the selection
4394 or create a new selection.
4397 if (drag_info.first_move) {
4399 temp_location->set (start, end);
4403 update_marker_drag_item (temp_location);
4404 range_marker_drag_rect->show();
4405 range_marker_drag_rect->raise_to_top();
4411 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4412 start_canvas_autoscroll (1);
4416 temp_location->set (start, end);
4418 double x1 = frame_to_pixel (start);
4419 double x2 = frame_to_pixel (end);
4420 crect->property_x1() = x1;
4421 crect->property_x2() = x2;
4423 update_marker_drag_item (temp_location);
4426 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4427 drag_info.first_move = false;
4429 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4434 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4436 Location * newloc = 0;
4439 if (!drag_info.first_move) {
4440 drag_range_markerbar_op (item, event);
4442 switch (range_marker_op) {
4443 case CreateRangeMarker:
4445 begin_reversible_command (_("new range marker"));
4446 XMLNode &before = session->locations()->get_state();
4447 session->locations()->next_available_name(rangename,"unnamed");
4448 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4449 session->locations()->add (newloc, true);
4450 XMLNode &after = session->locations()->get_state();
4451 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4452 commit_reversible_command ();
4454 range_bar_drag_rect->hide();
4455 range_marker_drag_rect->hide();
4459 case CreateTransportMarker:
4460 // popup menu to pick loop or punch
4461 new_transport_marker_context_menu (&event->button, item);
4466 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4468 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4473 start = session->locations()->first_mark_before (drag_info.grab_frame);
4474 end = session->locations()->first_mark_after (drag_info.grab_frame);
4476 if (end == max_frames) {
4477 end = session->current_end_frame ();
4481 start = session->current_start_frame ();
4484 switch (mouse_mode) {
4486 /* find the two markers on either side and then make the selection from it */
4487 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4491 /* find the two markers on either side of the click and make the range out of it */
4492 selection->set (0, start, end);
4501 stop_canvas_autoscroll ();
4507 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4509 drag_info.item = item;
4510 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4511 drag_info.finished_callback = &Editor::end_mouse_zoom;
4513 start_grab (event, zoom_cursor);
4515 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4519 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4524 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4525 snap_to (drag_info.current_pointer_frame);
4527 if (drag_info.first_move) {
4528 snap_to (drag_info.grab_frame);
4532 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4534 /* base start and end on initial click position */
4535 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4536 start = drag_info.current_pointer_frame;
4537 end = drag_info.grab_frame;
4539 end = drag_info.current_pointer_frame;
4540 start = drag_info.grab_frame;
4545 if (drag_info.first_move) {
4547 zoom_rect->raise_to_top();
4550 reposition_zoom_rect(start, end);
4552 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4553 drag_info.first_move = false;
4555 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4560 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4562 if (!drag_info.first_move) {
4563 drag_mouse_zoom (item, event);
4565 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4566 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4568 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4571 temporal_zoom_to_frame (false, drag_info.grab_frame);
4573 temporal_zoom_step (false);
4574 center_screen (drag_info.grab_frame);
4582 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4584 double x1 = frame_to_pixel (start);
4585 double x2 = frame_to_pixel (end);
4586 double y2 = full_canvas_height - 1.0;
4588 zoom_rect->property_x1() = x1;
4589 zoom_rect->property_y1() = 1.0;
4590 zoom_rect->property_x2() = x2;
4591 zoom_rect->property_y2() = y2;
4595 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4597 drag_info.item = item;
4598 drag_info.motion_callback = &Editor::drag_rubberband_select;
4599 drag_info.finished_callback = &Editor::end_rubberband_select;
4601 start_grab (event, cross_hair_cursor);
4603 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4607 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4614 /* use a bigger drag threshold than the default */
4616 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4620 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4621 if (drag_info.first_move) {
4622 snap_to (drag_info.grab_frame);
4624 snap_to (drag_info.current_pointer_frame);
4627 /* base start and end on initial click position */
4629 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4630 start = drag_info.current_pointer_frame;
4631 end = drag_info.grab_frame;
4633 end = drag_info.current_pointer_frame;
4634 start = drag_info.grab_frame;
4637 if (drag_info.current_pointer_y < drag_info.grab_y) {
4638 y1 = drag_info.current_pointer_y;
4639 y2 = drag_info.grab_y;
4641 y2 = drag_info.current_pointer_y;
4642 y1 = drag_info.grab_y;
4646 if (start != end || y1 != y2) {
4648 double x1 = frame_to_pixel (start);
4649 double x2 = frame_to_pixel (end);
4651 rubberband_rect->property_x1() = x1;
4652 rubberband_rect->property_y1() = y1;
4653 rubberband_rect->property_x2() = x2;
4654 rubberband_rect->property_y2() = y2;
4656 rubberband_rect->show();
4657 rubberband_rect->raise_to_top();
4659 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4660 drag_info.first_move = false;
4662 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4667 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4669 if (!drag_info.first_move) {
4671 drag_rubberband_select (item, event);
4674 if (drag_info.current_pointer_y < drag_info.grab_y) {
4675 y1 = drag_info.current_pointer_y;
4676 y2 = drag_info.grab_y;
4679 y2 = drag_info.current_pointer_y;
4680 y1 = drag_info.grab_y;
4684 Selection::Operation op = Keyboard::selection_type (event->button.state);
4687 begin_reversible_command (_("rubberband selection"));
4689 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4690 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4692 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4696 commit_reversible_command ();
4700 selection->clear_tracks();
4701 selection->clear_regions();
4702 selection->clear_points ();
4703 selection->clear_lines ();
4706 rubberband_rect->hide();
4711 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4713 using namespace Gtkmm2ext;
4715 ArdourPrompter prompter (false);
4717 prompter.set_prompt (_("Name for region:"));
4718 prompter.set_initial_text (clicked_regionview->region()->name());
4719 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4720 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4721 prompter.show_all ();
4722 switch (prompter.run ()) {
4723 case Gtk::RESPONSE_ACCEPT:
4725 prompter.get_result(str);
4727 clicked_regionview->region()->set_name (str);
4735 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4737 drag_info.item = item;
4738 drag_info.motion_callback = &Editor::time_fx_motion;
4739 drag_info.finished_callback = &Editor::end_time_fx;
4743 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4747 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4749 RegionView* rv = clicked_regionview;
4751 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4752 snap_to (drag_info.current_pointer_frame);
4755 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4759 if (drag_info.current_pointer_frame > rv->region()->position()) {
4760 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4763 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4764 drag_info.first_move = false;
4766 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4770 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4772 clicked_regionview->get_time_axis_view().hide_timestretch ();
4774 if (drag_info.first_move) {
4778 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4779 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4781 begin_reversible_command (_("timestretch"));
4783 if (run_timestretch (selection->regions, percentage) == 0) {
4784 session->commit_reversible_command ();
4789 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4791 /* no brushing without a useful snap setting */
4794 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4797 switch (snap_mode) {
4799 return; /* can't work because it allows region to be placed anywhere */
4804 switch (snap_type) {
4807 case SnapToEditCursor:
4814 /* don't brush a copy over the original */
4816 if (pos == rv->region()->position()) {
4820 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4822 if (atv == 0 || !atv->is_audio_track()) {
4826 boost::shared_ptr<Playlist> playlist = atv->playlist();
4827 double speed = atv->get_diskstream()->speed();
4829 XMLNode &before = playlist->get_state();
4830 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4831 XMLNode &after = playlist->get_state();
4832 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4834 // playlist is frozen, so we have to update manually
4836 playlist->Modified(); /* EMIT SIGNAL */
4840 Editor::track_height_step_timeout ()
4843 struct timeval delta;
4845 gettimeofday (&now, 0);
4846 timersub (&now, &last_track_height_step_timestamp, &delta);
4848 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4849 current_stepping_trackview = 0;