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 ();
1927 if (session && session->is_auditioning()) {
1928 session->cancel_audition ();
1932 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1934 show_verbose_time_cursor (cursor->current_frame, 10);
1938 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1940 Cursor* cursor = (Cursor *) drag_info.data;
1941 nframes_t adjusted_frame;
1943 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1944 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1950 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1951 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1952 snap_to (adjusted_frame);
1956 if (adjusted_frame == drag_info.last_pointer_frame) return;
1958 cursor->set_position (adjusted_frame);
1960 if (cursor == edit_cursor) {
1961 edit_cursor_clock.set (cursor->current_frame);
1963 UpdateAllTransportClocks (cursor->current_frame);
1966 show_verbose_time_cursor (cursor->current_frame, 10);
1968 drag_info.last_pointer_frame = adjusted_frame;
1969 drag_info.first_move = false;
1973 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1975 if (drag_info.first_move) return;
1977 cursor_drag_motion_callback (item, event);
1979 _dragging_playhead = false;
1981 if (item == &playhead_cursor->canvas_item) {
1983 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1985 } else if (item == &edit_cursor->canvas_item) {
1986 edit_cursor->set_position (edit_cursor->current_frame);
1987 edit_cursor_clock.set (edit_cursor->current_frame);
1992 Editor::update_marker_drag_item (Location *location)
1994 double x1 = frame_to_pixel (location->start());
1995 double x2 = frame_to_pixel (location->end());
1997 if (location->is_mark()) {
1998 marker_drag_line_points.front().set_x(x1);
1999 marker_drag_line_points.back().set_x(x1);
2000 marker_drag_line->property_points() = marker_drag_line_points;
2003 range_marker_drag_rect->property_x1() = x1;
2004 range_marker_drag_rect->property_x2() = x2;
2009 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2013 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2014 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2020 Location *location = find_location_from_marker (marker, is_start);
2022 drag_info.item = item;
2023 drag_info.data = marker;
2024 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2025 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2029 drag_info.copied_location = new Location (*location);
2030 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2032 update_marker_drag_item (location);
2034 if (location->is_mark()) {
2035 marker_drag_line->show();
2036 marker_drag_line->raise_to_top();
2039 range_marker_drag_rect->show();
2040 range_marker_drag_rect->raise_to_top();
2043 if (is_start) show_verbose_time_cursor (location->start(), 10);
2044 else show_verbose_time_cursor (location->end(), 10);
2048 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2051 Marker* marker = (Marker *) drag_info.data;
2052 Location *real_location;
2053 Location *copy_location;
2055 bool move_both = false;
2059 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2060 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2066 nframes_t next = newframe;
2068 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2069 snap_to (newframe, 0, true);
2072 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2076 /* call this to find out if its the start or end */
2078 real_location = find_location_from_marker (marker, is_start);
2080 /* use the copy that we're "dragging" around */
2082 copy_location = drag_info.copied_location;
2084 f_delta = copy_location->end() - copy_location->start();
2086 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2090 if (copy_location->is_mark()) {
2093 copy_location->set_start (newframe);
2097 if (is_start) { // start-of-range marker
2100 copy_location->set_start (newframe);
2101 copy_location->set_end (newframe + f_delta);
2102 } else if (newframe < copy_location->end()) {
2103 copy_location->set_start (newframe);
2105 snap_to (next, 1, true);
2106 copy_location->set_end (next);
2107 copy_location->set_start (newframe);
2110 } else { // end marker
2113 copy_location->set_end (newframe);
2114 copy_location->set_start (newframe - f_delta);
2115 } else if (newframe > copy_location->start()) {
2116 copy_location->set_end (newframe);
2118 } else if (newframe > 0) {
2119 snap_to (next, -1, true);
2120 copy_location->set_start (next);
2121 copy_location->set_end (newframe);
2126 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2127 drag_info.first_move = false;
2129 update_marker_drag_item (copy_location);
2131 LocationMarkers* lm = find_location_markers (real_location);
2132 lm->set_position (copy_location->start(), copy_location->end());
2134 show_verbose_time_cursor (newframe, 10);
2138 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2140 if (drag_info.first_move) {
2141 marker_drag_motion_callback (item, event);
2145 Marker* marker = (Marker *) drag_info.data;
2149 begin_reversible_command ( _("move marker") );
2150 XMLNode &before = session->locations()->get_state();
2152 Location * location = find_location_from_marker (marker, is_start);
2155 if (location->is_mark()) {
2156 location->set_start (drag_info.copied_location->start());
2158 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2162 XMLNode &after = session->locations()->get_state();
2163 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2164 commit_reversible_command ();
2166 marker_drag_line->hide();
2167 range_marker_drag_rect->hide();
2171 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2174 MeterMarker* meter_marker;
2176 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2177 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2181 meter_marker = dynamic_cast<MeterMarker*> (marker);
2183 MetricSection& section (meter_marker->meter());
2185 if (!section.movable()) {
2189 drag_info.item = item;
2190 drag_info.data = marker;
2191 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2192 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2196 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2198 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2202 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2205 MeterMarker* meter_marker;
2207 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2208 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2212 meter_marker = dynamic_cast<MeterMarker*> (marker);
2214 // create a dummy marker for visual representation of moving the copy.
2215 // The actual copying is not done before we reach the finish callback.
2217 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2218 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2219 *new MeterSection(meter_marker->meter()));
2221 drag_info.item = &new_marker->the_item();
2222 drag_info.copy = true;
2223 drag_info.data = new_marker;
2224 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2225 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2229 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2231 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2235 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2237 MeterMarker* marker = (MeterMarker *) drag_info.data;
2238 nframes_t adjusted_frame;
2240 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2241 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2247 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2248 snap_to (adjusted_frame);
2251 if (adjusted_frame == drag_info.last_pointer_frame) return;
2253 marker->set_position (adjusted_frame);
2256 drag_info.last_pointer_frame = adjusted_frame;
2257 drag_info.first_move = false;
2259 show_verbose_time_cursor (adjusted_frame, 10);
2263 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2265 if (drag_info.first_move) return;
2267 meter_marker_drag_motion_callback (drag_info.item, event);
2269 MeterMarker* marker = (MeterMarker *) drag_info.data;
2272 TempoMap& map (session->tempo_map());
2273 map.bbt_time (drag_info.last_pointer_frame, when);
2275 if (drag_info.copy == true) {
2276 begin_reversible_command (_("copy meter mark"));
2277 XMLNode &before = map.get_state();
2278 map.add_meter (marker->meter(), when);
2279 XMLNode &after = map.get_state();
2280 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2281 commit_reversible_command ();
2283 // delete the dummy marker we used for visual representation of copying.
2284 // a new visual marker will show up automatically.
2287 begin_reversible_command (_("move meter mark"));
2288 XMLNode &before = map.get_state();
2289 map.move_meter (marker->meter(), when);
2290 XMLNode &after = map.get_state();
2291 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2292 commit_reversible_command ();
2297 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2300 TempoMarker* tempo_marker;
2302 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2303 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2307 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2308 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2312 MetricSection& section (tempo_marker->tempo());
2314 if (!section.movable()) {
2318 drag_info.item = item;
2319 drag_info.data = marker;
2320 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2321 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2325 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2326 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2330 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2333 TempoMarker* tempo_marker;
2335 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2336 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2340 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2341 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2345 // create a dummy marker for visual representation of moving the copy.
2346 // The actual copying is not done before we reach the finish callback.
2348 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2349 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2350 *new TempoSection(tempo_marker->tempo()));
2352 drag_info.item = &new_marker->the_item();
2353 drag_info.copy = true;
2354 drag_info.data = new_marker;
2355 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2356 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2360 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2362 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2366 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2368 TempoMarker* marker = (TempoMarker *) drag_info.data;
2369 nframes_t adjusted_frame;
2371 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2372 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2378 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2379 snap_to (adjusted_frame);
2382 if (adjusted_frame == drag_info.last_pointer_frame) return;
2384 /* OK, we've moved far enough to make it worth actually move the thing. */
2386 marker->set_position (adjusted_frame);
2388 show_verbose_time_cursor (adjusted_frame, 10);
2390 drag_info.last_pointer_frame = adjusted_frame;
2391 drag_info.first_move = false;
2395 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2397 if (drag_info.first_move) return;
2399 tempo_marker_drag_motion_callback (drag_info.item, event);
2401 TempoMarker* marker = (TempoMarker *) drag_info.data;
2404 TempoMap& map (session->tempo_map());
2405 map.bbt_time (drag_info.last_pointer_frame, when);
2407 if (drag_info.copy == true) {
2408 begin_reversible_command (_("copy tempo mark"));
2409 XMLNode &before = map.get_state();
2410 map.add_tempo (marker->tempo(), when);
2411 XMLNode &after = map.get_state();
2412 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2413 commit_reversible_command ();
2415 // delete the dummy marker we used for visual representation of copying.
2416 // a new visual marker will show up automatically.
2419 begin_reversible_command (_("move tempo mark"));
2420 XMLNode &before = map.get_state();
2421 map.move_tempo (marker->tempo(), when);
2422 XMLNode &after = map.get_state();
2423 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2424 commit_reversible_command ();
2429 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2431 ControlPoint* control_point;
2433 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2434 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2438 // We shouldn't remove the first or last gain point
2439 if (control_point->line.is_last_point(*control_point) ||
2440 control_point->line.is_first_point(*control_point)) {
2444 control_point->line.remove_point (*control_point);
2448 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2450 ControlPoint* control_point;
2452 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2453 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2457 control_point->line.remove_point (*control_point);
2461 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2463 ControlPoint* control_point;
2465 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2466 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2470 drag_info.item = item;
2471 drag_info.data = control_point;
2472 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2473 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2475 start_grab (event, fader_cursor);
2477 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2479 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2480 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2481 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2483 show_verbose_canvas_cursor ();
2487 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2489 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2491 double cx = drag_info.current_pointer_x;
2492 double cy = drag_info.current_pointer_y;
2494 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2495 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2497 if (drag_info.x_constrained) {
2498 cx = drag_info.grab_x;
2500 if (drag_info.y_constrained) {
2501 cy = drag_info.grab_y;
2504 cp->line.parent_group().w2i (cx, cy);
2508 cy = min ((double) cp->line.height(), cy);
2510 //translate cx to frames
2511 nframes_t cx_frames = unit_to_frame (cx);
2513 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2514 snap_to (cx_frames);
2517 float fraction = 1.0 - (cy / cp->line.height());
2521 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2527 cp->line.point_drag (*cp, cx_frames , fraction, push);
2529 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2531 drag_info.first_move = false;
2535 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2537 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2539 if (drag_info.first_move) {
2543 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2544 reset_point_selection ();
2548 control_point_drag_motion_callback (item, event);
2550 cp->line.end_drag (cp);
2554 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2556 switch (mouse_mode) {
2558 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2559 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2567 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2571 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2572 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2576 start_line_grab (al, event);
2580 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2584 nframes_t frame_within_region;
2586 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2590 cx = event->button.x;
2591 cy = event->button.y;
2592 line->parent_group().w2i (cx, cy);
2593 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2595 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2596 current_line_drag_info.after)) {
2597 /* no adjacent points */
2601 drag_info.item = &line->grab_item();
2602 drag_info.data = line;
2603 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2604 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2606 start_grab (event, fader_cursor);
2608 double fraction = 1.0 - (cy / line->height());
2610 line->start_drag (0, drag_info.grab_frame, fraction);
2612 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2613 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2614 show_verbose_canvas_cursor ();
2618 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2620 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2621 double cx = drag_info.current_pointer_x;
2622 double cy = drag_info.current_pointer_y;
2624 line->parent_group().w2i (cx, cy);
2627 fraction = 1.0 - (cy / line->height());
2631 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2637 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2639 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2643 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2645 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2646 line_drag_motion_callback (item, event);
2651 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2653 if (selection->regions.empty() || clicked_regionview == 0) {
2657 drag_info.copy = false;
2658 drag_info.item = item;
2659 drag_info.data = clicked_regionview;
2660 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2661 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2666 TimeAxisView* tvp = clicked_trackview;
2667 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2669 if (tv && tv->is_audio_track()) {
2670 speed = tv->get_diskstream()->speed();
2673 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2674 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2675 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2676 // we want a move threshold
2677 drag_info.want_move_threshold = true;
2679 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2681 begin_reversible_command (_("move region(s)"));
2685 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2687 if (selection->regions.empty() || clicked_regionview == 0) {
2691 drag_info.copy = true;
2692 drag_info.item = item;
2693 drag_info.data = clicked_regionview;
2697 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2698 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2701 if (atv && atv->is_audio_track()) {
2702 speed = atv->get_diskstream()->speed();
2705 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2706 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2707 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2708 // we want a move threshold
2709 drag_info.want_move_threshold = true;
2710 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2711 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2712 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2716 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2718 if (selection->regions.empty() || clicked_regionview == 0) {
2722 drag_info.copy = false;
2723 drag_info.item = item;
2724 drag_info.data = clicked_regionview;
2725 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2726 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2731 TimeAxisView* tvp = clicked_trackview;
2732 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2734 if (tv && tv->is_audio_track()) {
2735 speed = tv->get_diskstream()->speed();
2738 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2739 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2740 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2741 // we want a move threshold
2742 drag_info.want_move_threshold = true;
2743 drag_info.brushing = true;
2745 begin_reversible_command (_("Drag region brush"));
2749 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2753 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2754 nframes_t pending_region_position = 0;
2755 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2756 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2757 bool clamp_y_axis = false;
2758 vector<int32_t> height_list(512) ;
2759 vector<int32_t>::iterator j;
2761 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2763 drag_info.want_move_threshold = false; // don't copy again
2765 /* duplicate the region(s) */
2767 vector<RegionView*> new_regionviews;
2769 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2772 AudioRegionView* arv;
2777 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
2778 /* XXX handle MIDI here */
2782 nrv = new AudioRegionView (*arv);
2783 nrv->get_canvas_group()->show ();
2785 new_regionviews.push_back (nrv);
2788 if (new_regionviews.empty()) {
2792 /* reset selection to new regionviews */
2794 selection->set (new_regionviews);
2796 /* reset drag_info data to reflect the fact that we are dragging the copies */
2798 drag_info.data = new_regionviews.front();
2800 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2803 /* Which trackview is this ? */
2805 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2806 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2808 /* The region motion is only processed if the pointer is over
2812 if (!tv || !tv->is_audio_track()) {
2813 /* To make sure we hide the verbose canvas cursor when the mouse is
2814 not held over and audiotrack.
2816 hide_verbose_canvas_cursor ();
2820 original_pointer_order = drag_info.last_trackview->order;
2822 /************************************************************
2824 ************************************************************/
2826 if (drag_info.brushing) {
2827 clamp_y_axis = true;
2832 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2834 int32_t children = 0, numtracks = 0;
2835 // XXX hard coding track limit, oh my, so very very bad
2836 bitset <1024> tracks (0x00);
2837 /* get a bitmask representing the visible tracks */
2839 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2840 TimeAxisView *tracklist_timeview;
2841 tracklist_timeview = (*i);
2842 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2843 list<TimeAxisView*> children_list;
2845 /* zeroes are audio tracks. ones are other types. */
2847 if (!atv2->hidden()) {
2849 if (visible_y_high < atv2->order) {
2850 visible_y_high = atv2->order;
2852 if (visible_y_low > atv2->order) {
2853 visible_y_low = atv2->order;
2856 if (!atv2->is_audio_track()) {
2857 tracks = tracks |= (0x01 << atv2->order);
2860 height_list[atv2->order] = (*i)->height;
2862 if ((children_list = atv2->get_child_list()).size() > 0) {
2863 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2864 tracks = tracks |= (0x01 << (atv2->order + children));
2865 height_list[atv2->order + children] = (*j)->height;
2873 /* find the actual span according to the canvas */
2875 canvas_pointer_y_span = pointer_y_span;
2876 if (drag_info.last_trackview->order >= tv->order) {
2878 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2879 if (height_list[y] == 0 ) {
2880 canvas_pointer_y_span--;
2885 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2886 if ( height_list[y] == 0 ) {
2887 canvas_pointer_y_span++;
2892 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2893 RegionView* rv2 = (*i);
2894 double ix1, ix2, iy1, iy2;
2897 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2898 rv2->get_canvas_group()->i2w (ix1, iy1);
2899 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2900 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2902 if (atv2->order != original_pointer_order) {
2903 /* this isn't the pointer track */
2905 if (canvas_pointer_y_span > 0) {
2907 /* moving up the canvas */
2908 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2910 int32_t visible_tracks = 0;
2911 while (visible_tracks < canvas_pointer_y_span ) {
2914 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2915 /* we're passing through a hidden track */
2920 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2921 clamp_y_axis = true;
2925 clamp_y_axis = true;
2928 } else if (canvas_pointer_y_span < 0) {
2930 /*moving down the canvas*/
2932 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2935 int32_t visible_tracks = 0;
2937 while (visible_tracks > canvas_pointer_y_span ) {
2940 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2944 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2945 clamp_y_axis = true;
2950 clamp_y_axis = true;
2956 /* this is the pointer's track */
2957 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2958 clamp_y_axis = true;
2959 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2960 clamp_y_axis = true;
2968 } else if (drag_info.last_trackview == tv) {
2969 clamp_y_axis = true;
2973 if (!clamp_y_axis) {
2974 drag_info.last_trackview = tv;
2977 /************************************************************
2979 ************************************************************/
2981 /* compute the amount of pointer motion in frames, and where
2982 the region would be if we moved it by that much.
2985 if (drag_info.move_threshold_passed) {
2987 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2989 nframes_t sync_frame;
2990 nframes_t sync_offset;
2993 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2995 sync_offset = rv->region()->sync_offset (sync_dir);
2996 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
2998 /* we snap if the snap modifier is not enabled.
3001 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3002 snap_to (sync_frame);
3005 if (sync_frame - sync_offset <= sync_frame) {
3006 pending_region_position = sync_frame - (sync_dir*sync_offset);
3008 pending_region_position = 0;
3012 pending_region_position = 0;
3015 if (pending_region_position > max_frames - rv->region()->length()) {
3016 pending_region_position = drag_info.last_frame_position;
3019 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3021 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3023 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3024 to make it appear at the new location.
3027 if (pending_region_position > drag_info.last_frame_position) {
3028 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3030 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3033 drag_info.last_frame_position = pending_region_position;
3040 /* threshold not passed */
3045 /*************************************************************
3047 ************************************************************/
3049 if (x_delta == 0 && (pointer_y_span == 0)) {
3050 /* haven't reached next snap point, and we're not switching
3051 trackviews. nothing to do.
3057 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3059 RegionView* rv2 = (*i);
3061 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3063 double ix1, ix2, iy1, iy2;
3064 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3065 rv2->get_canvas_group()->i2w (ix1, iy1);
3074 /*************************************************************
3076 ************************************************************/
3080 if (drag_info.first_move) {
3081 if (drag_info.move_threshold_passed) {
3092 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3093 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3095 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3097 RegionView* rv = (*i);
3098 double ix1, ix2, iy1, iy2;
3099 int32_t temp_pointer_y_span = pointer_y_span;
3101 /* get item BBox, which will be relative to parent. so we have
3102 to query on a child, then convert to world coordinates using
3106 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3107 rv->get_canvas_group()->i2w (ix1, iy1);
3108 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3109 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3110 AudioTimeAxisView* temp_atv;
3112 if ((pointer_y_span != 0) && !clamp_y_axis) {
3115 for (j = height_list.begin(); j!= height_list.end(); j++) {
3116 if (x == canvas_atv->order) {
3117 /* we found the track the region is on */
3118 if (x != original_pointer_order) {
3119 /*this isn't from the same track we're dragging from */
3120 temp_pointer_y_span = canvas_pointer_y_span;
3122 while (temp_pointer_y_span > 0) {
3123 /* we're moving up canvas-wise,
3124 so we need to find the next track height
3126 if (j != height_list.begin()) {
3129 if (x != original_pointer_order) {
3130 /* we're not from the dragged track, so ignore hidden tracks. */
3132 temp_pointer_y_span++;
3136 temp_pointer_y_span--;
3138 while (temp_pointer_y_span < 0) {
3140 if (x != original_pointer_order) {
3142 temp_pointer_y_span--;
3146 if (j != height_list.end()) {
3149 temp_pointer_y_span++;
3151 /* find out where we'll be when we move and set height accordingly */
3153 tvp2 = trackview_by_y_position (iy1 + y_delta);
3154 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3155 rv->set_height (temp_atv->height);
3157 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3158 personally, i think this can confuse things, but never mind.
3161 //const GdkColor& col (temp_atv->view->get_region_color());
3162 //rv->set_color (const_cast<GdkColor&>(col));
3169 /* prevent the regionview from being moved to before
3170 the zero position on the canvas.
3175 if (-x_delta > ix1) {
3178 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3179 x_delta = max_frames - rv->region()->last_frame();
3182 if (drag_info.first_move) {
3184 /* hide any dependent views */
3186 rv->get_time_axis_view().hide_dependent_views (*rv);
3188 /* this is subtle. raising the regionview itself won't help,
3189 because raise_to_top() just puts the item on the top of
3190 its parent's stack. so, we need to put the trackview canvas_display group
3191 on the top, since its parent is the whole canvas.
3194 rv->get_canvas_group()->raise_to_top();
3195 rv->get_time_axis_view().canvas_display->raise_to_top();
3196 cursor_group->raise_to_top();
3198 rv->fake_set_opaque (true);
3201 if (drag_info.brushing) {
3202 mouse_brush_insert_region (rv, pending_region_position);
3204 rv->move (x_delta, y_delta);
3207 } /* foreach region */
3211 if (drag_info.first_move && drag_info.move_threshold_passed) {
3212 cursor_group->raise_to_top();
3213 drag_info.first_move = false;
3216 if (x_delta != 0 && !drag_info.brushing) {
3217 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3222 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3225 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3226 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3227 bool nocommit = true;
3229 RouteTimeAxisView* atv;
3230 bool regionview_y_movement;
3231 bool regionview_x_movement;
3233 /* first_move is set to false if the regionview has been moved in the
3237 if (drag_info.first_move) {
3244 /* The regionview has been moved at some stage during the grab so we need
3245 to account for any mouse movement between this event and the last one.
3248 region_drag_motion_callback (item, event);
3250 if (drag_info.brushing) {
3251 /* all changes were made during motion event handlers */
3255 /* adjust for track speed */
3258 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3259 if (atv && atv->get_diskstream()) {
3260 speed = atv->get_diskstream()->speed();
3263 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3264 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3266 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3267 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3271 if (drag_info.copy) {
3272 if (drag_info.x_constrained) {
3273 op_string = _("fixed time region copy");
3275 op_string = _("region copy");
3278 if (drag_info.x_constrained) {
3279 op_string = _("fixed time region drag");
3281 op_string = _("region drag");
3285 begin_reversible_command (op_string);
3287 if (regionview_y_movement) {
3289 /* moved to a different audio track. */
3291 list<RegionView*> new_selection;
3292 new_selection = selection->regions.by_layer();
3293 selection->clear_regions ();
3295 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
3297 RegionView* rv = (*i);
3299 double ix1, ix2, iy1, iy2;
3301 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3302 rv->get_canvas_group()->i2w (ix1, iy1);
3303 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3304 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3306 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3307 boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
3309 where = (nframes_t) (unit_to_frame (ix1) * speed);
3310 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3312 if (!drag_info.copy) {
3314 /* the region that used to be in the old playlist is not
3315 moved to the new one - we make a copy of it. as a result,
3316 any existing editor for the region should no longer be
3320 rv->hide_region_editor();
3321 rv->fake_set_opaque (false);
3323 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3324 from_playlist->remove_region ((rv->region()));
3325 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3328 latest_regionview = 0;
3330 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3331 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3332 to_playlist->add_region (new_region, where);
3333 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3336 if (latest_regionview) {
3337 selection->add (latest_regionview);
3340 if (drag_info.copy) {
3341 // get rid of the copy
3348 /* motion within a single track */
3350 list<RegionView*> regions = selection->regions.by_layer();
3352 if (drag_info.copy) {
3353 selection->clear_regions();
3356 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3360 if (rv->region()->locked()) {
3365 if (regionview_x_movement) {
3366 double ownspeed = 1.0;
3367 atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3369 if (atv && atv->get_diskstream()) {
3370 ownspeed = atv->get_diskstream()->speed();
3373 /* base the new region position on the current position of the regionview.*/
3375 double ix1, ix2, iy1, iy2;
3377 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3378 rv->get_canvas_group()->i2w (ix1, iy1);
3379 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3383 where = rv->region()->position();
3386 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3388 assert (to_playlist);
3392 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3394 if (drag_info.copy) {
3396 boost::shared_ptr<Region> newregion;
3397 boost::shared_ptr<Region> ar;
3399 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3400 newregion = RegionFactory::create (ar);
3402 /* XXX MIDI HERE drobilla */
3408 latest_regionview = 0;
3409 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3410 to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
3413 if (latest_regionview) {
3414 atv->reveal_dependent_views (*latest_regionview);
3415 selection->add (latest_regionview);
3418 /* if the original region was locked, we don't care for the new one */
3420 newregion->set_locked (false);
3424 /* just change the model */
3426 rv->region()->set_position (where, (void*) this);
3432 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3434 /* get rid of the copy */
3436 if (drag_info.copy) {
3445 commit_reversible_command ();
3450 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3452 /* Either add to or set the set the region selection, unless
3453 this is an alignment click (control used)
3456 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3457 TimeAxisView* tv = &rv.get_time_axis_view();
3458 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3460 if (atv && atv->is_audio_track()) {
3461 speed = atv->get_diskstream()->speed();
3464 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3466 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3468 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3470 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3474 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3480 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3486 nframes_t frame_rate;
3493 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3494 case AudioClock::BBT:
3495 session->bbt_time (frame, bbt);
3496 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3499 case AudioClock::SMPTE:
3500 session->smpte_time (frame, smpte);
3501 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3504 case AudioClock::MinSec:
3505 /* XXX this is copied from show_verbose_duration_cursor() */
3506 frame_rate = session->frame_rate();
3507 hours = frame / (frame_rate * 3600);
3508 frame = frame % (frame_rate * 3600);
3509 mins = frame / (frame_rate * 60);
3510 frame = frame % (frame_rate * 60);
3511 secs = (float) frame / (float) frame_rate;
3512 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3516 snprintf (buf, sizeof(buf), "%u", frame);
3520 if (xpos >= 0 && ypos >=0) {
3521 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3524 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3526 show_verbose_canvas_cursor ();
3530 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3537 nframes_t distance, frame_rate;
3539 Meter meter_at_start(session->tempo_map().meter_at(start));
3545 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3546 case AudioClock::BBT:
3547 session->bbt_time (start, sbbt);
3548 session->bbt_time (end, ebbt);
3551 /* XXX this computation won't work well if the
3552 user makes a selection that spans any meter changes.
3555 ebbt.bars -= sbbt.bars;
3556 if (ebbt.beats >= sbbt.beats) {
3557 ebbt.beats -= sbbt.beats;
3560 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3562 if (ebbt.ticks >= sbbt.ticks) {
3563 ebbt.ticks -= sbbt.ticks;
3566 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3569 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3572 case AudioClock::SMPTE:
3573 session->smpte_duration (end - start, smpte);
3574 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3577 case AudioClock::MinSec:
3578 /* XXX this stuff should be elsewhere.. */
3579 distance = end - start;
3580 frame_rate = session->frame_rate();
3581 hours = distance / (frame_rate * 3600);
3582 distance = distance % (frame_rate * 3600);
3583 mins = distance / (frame_rate * 60);
3584 distance = distance % (frame_rate * 60);
3585 secs = (float) distance / (float) frame_rate;
3586 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3590 snprintf (buf, sizeof(buf), "%u", end - start);
3594 if (xpos >= 0 && ypos >=0) {
3595 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3598 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3600 show_verbose_canvas_cursor ();
3604 Editor::collect_new_region_view (RegionView* rv)
3606 latest_regionview = rv;
3610 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3612 if (clicked_regionview == 0) {
3616 /* lets try to create new Region for the selection */
3618 vector<boost::shared_ptr<AudioRegion> > new_regions;
3619 create_region_from_selection (new_regions);
3621 if (new_regions.empty()) {
3625 /* XXX fix me one day to use all new regions */
3627 boost::shared_ptr<Region> region (new_regions.front());
3629 /* add it to the current stream/playlist.
3631 tricky: the streamview for the track will add a new regionview. we will
3632 catch the signal it sends when it creates the regionview to
3633 set the regionview we want to then drag.
3636 latest_regionview = 0;
3637 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3639 /* A selection grab currently creates two undo/redo operations, one for
3640 creating the new region and another for moving it.
3643 begin_reversible_command (_("selection grab"));
3645 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3647 XMLNode *before = &(playlist->get_state());
3648 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3649 XMLNode *after = &(playlist->get_state());
3650 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3652 commit_reversible_command ();
3656 if (latest_regionview == 0) {
3657 /* something went wrong */
3661 /* we need to deselect all other regionviews, and select this one
3662 i'm ignoring undo stuff, because the region creation will take care of it */
3663 selection->set (latest_regionview);
3665 drag_info.item = latest_regionview->get_canvas_group();
3666 drag_info.data = latest_regionview;
3667 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3668 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3672 drag_info.last_trackview = clicked_trackview;
3673 drag_info.last_frame_position = latest_regionview->region()->position();
3674 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3676 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3680 Editor::cancel_selection ()
3682 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3683 (*i)->hide_selection ();
3685 begin_reversible_command (_("cancel selection"));
3686 selection->clear ();
3687 clicked_selection = 0;
3688 commit_reversible_command ();
3692 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3694 nframes_t start = 0;
3701 drag_info.item = item;
3702 drag_info.motion_callback = &Editor::drag_selection;
3703 drag_info.finished_callback = &Editor::end_selection_op;
3708 case CreateSelection:
3709 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3710 drag_info.copy = true;
3712 drag_info.copy = false;
3714 start_grab (event, selector_cursor);
3717 case SelectionStartTrim:
3718 if (clicked_trackview) {
3719 clicked_trackview->order_selection_trims (item, true);
3721 start_grab (event, trimmer_cursor);
3722 start = selection->time[clicked_selection].start;
3723 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3726 case SelectionEndTrim:
3727 if (clicked_trackview) {
3728 clicked_trackview->order_selection_trims (item, false);
3730 start_grab (event, trimmer_cursor);
3731 end = selection->time[clicked_selection].end;
3732 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3736 start = selection->time[clicked_selection].start;
3738 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3742 if (selection_op == SelectionMove) {
3743 show_verbose_time_cursor(start, 10);
3745 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3750 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3752 nframes_t start = 0;
3755 nframes_t pending_position;
3757 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3758 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3761 pending_position = 0;
3764 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3765 snap_to (pending_position);
3768 /* only alter selection if the current frame is
3769 different from the last frame position (adjusted)
3772 if (pending_position == drag_info.last_pointer_frame) return;
3774 switch (selection_op) {
3775 case CreateSelection:
3777 if (drag_info.first_move) {
3778 snap_to (drag_info.grab_frame);
3781 if (pending_position < drag_info.grab_frame) {
3782 start = pending_position;
3783 end = drag_info.grab_frame;
3785 end = pending_position;
3786 start = drag_info.grab_frame;
3789 /* first drag: Either add to the selection
3790 or create a new selection->
3793 if (drag_info.first_move) {
3795 begin_reversible_command (_("range selection"));
3797 if (drag_info.copy) {
3798 /* adding to the selection */
3799 clicked_selection = selection->add (start, end);
3800 drag_info.copy = false;
3802 /* new selection-> */
3803 clicked_selection = selection->set (clicked_trackview, start, end);
3808 case SelectionStartTrim:
3810 if (drag_info.first_move) {
3811 begin_reversible_command (_("trim selection start"));
3814 start = selection->time[clicked_selection].start;
3815 end = selection->time[clicked_selection].end;
3817 if (pending_position > end) {
3820 start = pending_position;
3824 case SelectionEndTrim:
3826 if (drag_info.first_move) {
3827 begin_reversible_command (_("trim selection end"));
3830 start = selection->time[clicked_selection].start;
3831 end = selection->time[clicked_selection].end;
3833 if (pending_position < start) {
3836 end = pending_position;
3843 if (drag_info.first_move) {
3844 begin_reversible_command (_("move selection"));
3847 start = selection->time[clicked_selection].start;
3848 end = selection->time[clicked_selection].end;
3850 length = end - start;
3852 start = pending_position;
3855 end = start + length;
3860 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3861 start_canvas_autoscroll (1);
3865 selection->replace (clicked_selection, start, end);
3868 drag_info.last_pointer_frame = pending_position;
3869 drag_info.first_move = false;
3871 if (selection_op == SelectionMove) {
3872 show_verbose_time_cursor(start, 10);
3874 show_verbose_time_cursor(pending_position, 10);
3879 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3881 if (!drag_info.first_move) {
3882 drag_selection (item, event);
3883 /* XXX this is not object-oriented programming at all. ick */
3884 if (selection->time.consolidate()) {
3885 selection->TimeChanged ();
3887 commit_reversible_command ();
3889 /* just a click, no pointer movement.*/
3891 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3893 selection->clear_time();
3898 /* XXX what happens if its a music selection? */
3899 session->set_audio_range (selection->time);
3900 stop_canvas_autoscroll ();
3904 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3907 TimeAxisView* tvp = clicked_trackview;
3908 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3910 if (tv && tv->is_audio_track()) {
3911 speed = tv->get_diskstream()->speed();
3914 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3915 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3916 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3918 //drag_info.item = clicked_regionview->get_name_highlight();
3919 drag_info.item = item;
3920 drag_info.motion_callback = &Editor::trim_motion_callback;
3921 drag_info.finished_callback = &Editor::trim_finished_callback;
3923 start_grab (event, trimmer_cursor);
3925 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3926 trim_op = ContentsTrim;
3928 /* These will get overridden for a point trim.*/
3929 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3930 /* closer to start */
3931 trim_op = StartTrim;
3932 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3940 show_verbose_time_cursor(region_start, 10);
3943 show_verbose_time_cursor(region_end, 10);
3946 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3952 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3954 RegionView* rv = clicked_regionview;
3955 nframes_t frame_delta = 0;
3956 bool left_direction;
3957 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3959 /* snap modifier works differently here..
3960 its' current state has to be passed to the
3961 various trim functions in order to work properly
3965 TimeAxisView* tvp = clicked_trackview;
3966 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3967 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3969 if (tv && tv->is_audio_track()) {
3970 speed = tv->get_diskstream()->speed();
3973 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3974 left_direction = true;
3976 left_direction = false;
3980 snap_to (drag_info.current_pointer_frame);
3983 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3987 if (drag_info.first_move) {
3993 trim_type = "Region start trim";
3996 trim_type = "Region end trim";
3999 trim_type = "Region content trim";
4003 begin_reversible_command (trim_type);
4005 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4006 (*i)->fake_set_opaque(false);
4007 (*i)->region()->freeze ();
4009 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4011 arv->temporarily_hide_envelope ();
4013 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4014 insert_result = motion_frozen_playlists.insert (pl);
4015 if (insert_result.second) {
4016 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4021 if (left_direction) {
4022 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4024 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4029 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4032 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4033 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4039 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4042 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4043 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4050 bool swap_direction = false;
4052 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4053 swap_direction = true;
4056 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4057 i != selection->regions.by_layer().end(); ++i)
4059 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4067 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4070 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4073 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4077 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4078 drag_info.first_move = false;
4082 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4084 boost::shared_ptr<Region> region (rv.region());
4086 if (region->locked()) {
4090 nframes_t new_bound;
4093 TimeAxisView* tvp = clicked_trackview;
4094 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4096 if (tv && tv->is_audio_track()) {
4097 speed = tv->get_diskstream()->speed();
4100 if (left_direction) {
4101 if (swap_direction) {
4102 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4104 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4107 if (swap_direction) {
4108 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4110 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4115 snap_to (new_bound);
4117 region->trim_start ((nframes_t) (new_bound * speed), this);
4118 rv.region_changed (StartChanged);
4122 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4124 boost::shared_ptr<Region> region (rv.region());
4126 if (region->locked()) {
4130 nframes_t new_bound;
4133 TimeAxisView* tvp = clicked_trackview;
4134 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4136 if (tv && tv->is_audio_track()) {
4137 speed = tv->get_diskstream()->speed();
4140 if (left_direction) {
4141 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4143 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4147 snap_to (new_bound, (left_direction ? 0 : 1));
4150 region->trim_front ((nframes_t) (new_bound * speed), this);
4152 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4156 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4158 boost::shared_ptr<Region> region (rv.region());
4160 if (region->locked()) {
4164 nframes_t new_bound;
4167 TimeAxisView* tvp = clicked_trackview;
4168 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4170 if (tv && tv->is_audio_track()) {
4171 speed = tv->get_diskstream()->speed();
4174 if (left_direction) {
4175 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4177 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4181 snap_to (new_bound);
4183 region->trim_end ((nframes_t) (new_bound * speed), this);
4184 rv.region_changed (LengthChanged);
4188 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4190 if (!drag_info.first_move) {
4191 trim_motion_callback (item, event);
4193 if (!clicked_regionview->get_selected()) {
4194 thaw_region_after_trim (*clicked_regionview);
4197 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4198 i != selection->regions.by_layer().end(); ++i)
4200 thaw_region_after_trim (**i);
4201 (*i)->fake_set_opaque (true);
4205 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4207 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4210 motion_frozen_playlists.clear ();
4212 commit_reversible_command();
4214 /* no mouse movement */
4220 Editor::point_trim (GdkEvent* event)
4222 RegionView* rv = clicked_regionview;
4223 nframes_t new_bound = drag_info.current_pointer_frame;
4225 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4226 snap_to (new_bound);
4229 /* Choose action dependant on which button was pressed */
4230 switch (event->button.button) {
4232 trim_op = StartTrim;
4233 begin_reversible_command (_("Start point trim"));
4235 if (rv->get_selected()) {
4237 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4238 i != selection->regions.by_layer().end(); ++i)
4240 if (!(*i)->region()->locked()) {
4241 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4242 XMLNode &before = pl->get_state();
4243 (*i)->region()->trim_front (new_bound, this);
4244 XMLNode &after = pl->get_state();
4245 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4251 if (!rv->region()->locked()) {
4252 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4253 XMLNode &before = pl->get_state();
4254 rv->region()->trim_front (new_bound, this);
4255 XMLNode &after = pl->get_state();
4256 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4260 commit_reversible_command();
4265 begin_reversible_command (_("End point trim"));
4267 if (rv->get_selected()) {
4269 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4271 if (!(*i)->region()->locked()) {
4272 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4273 XMLNode &before = pl->get_state();
4274 (*i)->region()->trim_end (new_bound, this);
4275 XMLNode &after = pl->get_state();
4276 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4282 if (!rv->region()->locked()) {
4283 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4284 XMLNode &before = pl->get_state();
4285 rv->region()->trim_end (new_bound, this);
4286 XMLNode &after = pl->get_state();
4287 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4291 commit_reversible_command();
4300 Editor::thaw_region_after_trim (RegionView& rv)
4302 boost::shared_ptr<Region> region (rv.region());
4304 if (region->locked()) {
4308 region->thaw (_("trimmed region"));
4309 XMLNode &after = region->playlist()->get_state();
4310 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4312 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4314 arv->unhide_envelope ();
4318 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4323 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4324 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4328 Location* location = find_location_from_marker (marker, is_start);
4329 location->set_hidden (true, this);
4334 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4340 drag_info.item = item;
4341 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4342 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4344 range_marker_op = op;
4346 if (!temp_location) {
4347 temp_location = new Location;
4351 case CreateRangeMarker:
4352 case CreateTransportMarker:
4354 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4355 drag_info.copy = true;
4357 drag_info.copy = false;
4359 start_grab (event, selector_cursor);
4363 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4368 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4370 nframes_t start = 0;
4372 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4374 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4375 snap_to (drag_info.current_pointer_frame);
4378 /* only alter selection if the current frame is
4379 different from the last frame position.
4382 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4384 switch (range_marker_op) {
4385 case CreateRangeMarker:
4386 case CreateTransportMarker:
4387 if (drag_info.first_move) {
4388 snap_to (drag_info.grab_frame);
4391 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4392 start = drag_info.current_pointer_frame;
4393 end = drag_info.grab_frame;
4395 end = drag_info.current_pointer_frame;
4396 start = drag_info.grab_frame;
4399 /* first drag: Either add to the selection
4400 or create a new selection.
4403 if (drag_info.first_move) {
4405 temp_location->set (start, end);
4409 update_marker_drag_item (temp_location);
4410 range_marker_drag_rect->show();
4411 range_marker_drag_rect->raise_to_top();
4417 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4418 start_canvas_autoscroll (1);
4422 temp_location->set (start, end);
4424 double x1 = frame_to_pixel (start);
4425 double x2 = frame_to_pixel (end);
4426 crect->property_x1() = x1;
4427 crect->property_x2() = x2;
4429 update_marker_drag_item (temp_location);
4432 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4433 drag_info.first_move = false;
4435 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4440 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4442 Location * newloc = 0;
4445 if (!drag_info.first_move) {
4446 drag_range_markerbar_op (item, event);
4448 switch (range_marker_op) {
4449 case CreateRangeMarker:
4451 begin_reversible_command (_("new range marker"));
4452 XMLNode &before = session->locations()->get_state();
4453 session->locations()->next_available_name(rangename,"unnamed");
4454 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4455 session->locations()->add (newloc, true);
4456 XMLNode &after = session->locations()->get_state();
4457 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4458 commit_reversible_command ();
4460 range_bar_drag_rect->hide();
4461 range_marker_drag_rect->hide();
4465 case CreateTransportMarker:
4466 // popup menu to pick loop or punch
4467 new_transport_marker_context_menu (&event->button, item);
4472 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4474 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4479 start = session->locations()->first_mark_before (drag_info.grab_frame);
4480 end = session->locations()->first_mark_after (drag_info.grab_frame);
4482 if (end == max_frames) {
4483 end = session->current_end_frame ();
4487 start = session->current_start_frame ();
4490 switch (mouse_mode) {
4492 /* find the two markers on either side and then make the selection from it */
4493 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4497 /* find the two markers on either side of the click and make the range out of it */
4498 selection->set (0, start, end);
4507 stop_canvas_autoscroll ();
4513 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4515 drag_info.item = item;
4516 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4517 drag_info.finished_callback = &Editor::end_mouse_zoom;
4519 start_grab (event, zoom_cursor);
4521 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4525 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4530 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4531 snap_to (drag_info.current_pointer_frame);
4533 if (drag_info.first_move) {
4534 snap_to (drag_info.grab_frame);
4538 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4540 /* base start and end on initial click position */
4541 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4542 start = drag_info.current_pointer_frame;
4543 end = drag_info.grab_frame;
4545 end = drag_info.current_pointer_frame;
4546 start = drag_info.grab_frame;
4551 if (drag_info.first_move) {
4553 zoom_rect->raise_to_top();
4556 reposition_zoom_rect(start, end);
4558 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4559 drag_info.first_move = false;
4561 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4566 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4568 if (!drag_info.first_move) {
4569 drag_mouse_zoom (item, event);
4571 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4572 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4574 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4577 temporal_zoom_to_frame (false, drag_info.grab_frame);
4579 temporal_zoom_step (false);
4580 center_screen (drag_info.grab_frame);
4588 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4590 double x1 = frame_to_pixel (start);
4591 double x2 = frame_to_pixel (end);
4592 double y2 = full_canvas_height - 1.0;
4594 zoom_rect->property_x1() = x1;
4595 zoom_rect->property_y1() = 1.0;
4596 zoom_rect->property_x2() = x2;
4597 zoom_rect->property_y2() = y2;
4601 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4603 drag_info.item = item;
4604 drag_info.motion_callback = &Editor::drag_rubberband_select;
4605 drag_info.finished_callback = &Editor::end_rubberband_select;
4607 start_grab (event, cross_hair_cursor);
4609 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4613 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4620 /* use a bigger drag threshold than the default */
4622 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4626 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4627 if (drag_info.first_move) {
4628 snap_to (drag_info.grab_frame);
4630 snap_to (drag_info.current_pointer_frame);
4633 /* base start and end on initial click position */
4635 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4636 start = drag_info.current_pointer_frame;
4637 end = drag_info.grab_frame;
4639 end = drag_info.current_pointer_frame;
4640 start = drag_info.grab_frame;
4643 if (drag_info.current_pointer_y < drag_info.grab_y) {
4644 y1 = drag_info.current_pointer_y;
4645 y2 = drag_info.grab_y;
4647 y2 = drag_info.current_pointer_y;
4648 y1 = drag_info.grab_y;
4652 if (start != end || y1 != y2) {
4654 double x1 = frame_to_pixel (start);
4655 double x2 = frame_to_pixel (end);
4657 rubberband_rect->property_x1() = x1;
4658 rubberband_rect->property_y1() = y1;
4659 rubberband_rect->property_x2() = x2;
4660 rubberband_rect->property_y2() = y2;
4662 rubberband_rect->show();
4663 rubberband_rect->raise_to_top();
4665 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4666 drag_info.first_move = false;
4668 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4673 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4675 if (!drag_info.first_move) {
4677 drag_rubberband_select (item, event);
4680 if (drag_info.current_pointer_y < drag_info.grab_y) {
4681 y1 = drag_info.current_pointer_y;
4682 y2 = drag_info.grab_y;
4685 y2 = drag_info.current_pointer_y;
4686 y1 = drag_info.grab_y;
4690 Selection::Operation op = Keyboard::selection_type (event->button.state);
4693 begin_reversible_command (_("rubberband selection"));
4695 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4696 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4698 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4702 commit_reversible_command ();
4706 selection->clear_tracks();
4707 selection->clear_regions();
4708 selection->clear_points ();
4709 selection->clear_lines ();
4712 rubberband_rect->hide();
4717 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4719 using namespace Gtkmm2ext;
4721 ArdourPrompter prompter (false);
4723 prompter.set_prompt (_("Name for region:"));
4724 prompter.set_initial_text (clicked_regionview->region()->name());
4725 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4726 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4727 prompter.show_all ();
4728 switch (prompter.run ()) {
4729 case Gtk::RESPONSE_ACCEPT:
4731 prompter.get_result(str);
4733 clicked_regionview->region()->set_name (str);
4741 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4743 drag_info.item = item;
4744 drag_info.motion_callback = &Editor::time_fx_motion;
4745 drag_info.finished_callback = &Editor::end_time_fx;
4749 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4753 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4755 RegionView* rv = clicked_regionview;
4757 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4758 snap_to (drag_info.current_pointer_frame);
4761 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4765 if (drag_info.current_pointer_frame > rv->region()->position()) {
4766 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4769 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4770 drag_info.first_move = false;
4772 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4776 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4778 clicked_regionview->get_time_axis_view().hide_timestretch ();
4780 if (drag_info.first_move) {
4784 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4785 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4787 begin_reversible_command (_("timestretch"));
4789 if (run_timestretch (selection->regions, percentage) == 0) {
4790 session->commit_reversible_command ();
4795 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4797 /* no brushing without a useful snap setting */
4800 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4803 switch (snap_mode) {
4805 return; /* can't work because it allows region to be placed anywhere */
4810 switch (snap_type) {
4813 case SnapToEditCursor:
4820 /* don't brush a copy over the original */
4822 if (pos == rv->region()->position()) {
4826 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4828 if (atv == 0 || !atv->is_audio_track()) {
4832 boost::shared_ptr<Playlist> playlist = atv->playlist();
4833 double speed = atv->get_diskstream()->speed();
4835 XMLNode &before = playlist->get_state();
4836 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4837 XMLNode &after = playlist->get_state();
4838 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4840 // playlist is frozen, so we have to update manually
4842 playlist->Modified(); /* EMIT SIGNAL */
4846 Editor::track_height_step_timeout ()
4849 struct timeval delta;
4851 gettimeofday (&now, 0);
4852 timersub (&now, &last_track_height_step_timestamp, &delta);
4854 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4855 current_stepping_trackview = 0;