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"
37 #include "midi_region_view.h"
39 #include "streamview.h"
40 #include "region_gain_line.h"
41 #include "automation_time_axis.h"
42 #include "control_point.h"
45 #include "selection.h"
48 #include "rgb_macros.h"
50 #include <ardour/types.h>
51 #include <ardour/profile.h>
52 #include <ardour/route.h>
53 #include <ardour/audio_track.h>
54 #include <ardour/audio_diskstream.h>
55 #include <ardour/playlist.h>
56 #include <ardour/audioplaylist.h>
57 #include <ardour/audioregion.h>
58 #include <ardour/midi_region.h>
59 #include <ardour/dB.h>
60 #include <ardour/utils.h>
61 #include <ardour/region_factory.h>
68 using namespace ARDOUR;
72 using namespace Editing;
75 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
89 switch (event->type) {
90 case GDK_BUTTON_RELEASE:
91 case GDK_BUTTON_PRESS:
92 case GDK_2BUTTON_PRESS:
93 case GDK_3BUTTON_PRESS:
94 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
96 case GDK_MOTION_NOTIFY:
97 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
99 case GDK_ENTER_NOTIFY:
100 case GDK_LEAVE_NOTIFY:
101 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
104 case GDK_KEY_RELEASE:
105 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
108 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
112 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
113 position is negative (as can be the case with motion events in particular),
114 the frame location is always positive.
117 return pixel_to_frame (*pcx);
121 Editor::mouse_mode_toggled (MouseMode m)
123 if (ignore_mouse_mode_toggle) {
129 if (mouse_select_button.get_active()) {
135 if (mouse_move_button.get_active()) {
141 if (mouse_gain_button.get_active()) {
147 if (mouse_zoom_button.get_active()) {
153 if (mouse_timefx_button.get_active()) {
159 if (mouse_audition_button.get_active()) {
165 if (mouse_note_button.get_active()) {
176 Editor::set_mouse_mode (MouseMode m, bool force)
178 if (drag_info.item) {
182 if (!force && m == mouse_mode) {
190 if (mouse_mode != MouseRange) {
192 /* in all modes except range, hide the range selection,
193 show the object (region) selection.
196 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
197 (*i)->set_should_show_selection (true);
199 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
200 (*i)->hide_selection ();
206 in range mode,show the range selection.
209 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
210 if ((*i)->get_selected()) {
211 (*i)->show_selection (selection->time);
216 /* XXX the hack of unsetting all other buttons should go
217 away once GTK2 allows us to use regular radio buttons drawn like
218 normal buttons, rather than my silly GroupedButton hack.
221 ignore_mouse_mode_toggle = true;
223 switch (mouse_mode) {
225 mouse_select_button.set_active (true);
226 current_canvas_cursor = selector_cursor;
230 mouse_move_button.set_active (true);
231 current_canvas_cursor = grabber_cursor;
235 mouse_gain_button.set_active (true);
236 current_canvas_cursor = cross_hair_cursor;
240 mouse_zoom_button.set_active (true);
241 current_canvas_cursor = zoom_cursor;
245 mouse_timefx_button.set_active (true);
246 current_canvas_cursor = time_fx_cursor; // just use playhead
250 mouse_audition_button.set_active (true);
251 current_canvas_cursor = speaker_cursor;
255 mouse_note_button.set_active (true);
256 set_midi_edit_cursor (current_midi_edit_mode());
260 if (mouse_mode == MouseNote)
261 midi_toolbar_frame.show();
263 midi_toolbar_frame.hide();
265 ignore_mouse_mode_toggle = false;
268 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
273 Editor::step_mouse_mode (bool next)
275 switch (current_mouse_mode()) {
277 if (next) set_mouse_mode (MouseRange);
278 else set_mouse_mode (MouseTimeFX);
282 if (next) set_mouse_mode (MouseZoom);
283 else set_mouse_mode (MouseObject);
287 if (next) set_mouse_mode (MouseGain);
288 else set_mouse_mode (MouseRange);
292 if (next) set_mouse_mode (MouseTimeFX);
293 else set_mouse_mode (MouseZoom);
297 if (next) set_mouse_mode (MouseAudition);
298 else set_mouse_mode (MouseGain);
302 if (next) set_mouse_mode (MouseObject);
303 else set_mouse_mode (MouseTimeFX);
307 if (next) set_mouse_mode (MouseObject);
308 else set_mouse_mode (MouseAudition);
314 Editor::midi_edit_mode_toggled (MidiEditMode m)
316 if (ignore_midi_edit_mode_toggle) {
322 if (midi_tool_select_button.get_active()) {
323 set_midi_edit_mode (m);
328 if (midi_tool_pencil_button.get_active()) {
329 set_midi_edit_mode (m);
334 if (midi_tool_erase_button.get_active()) {
335 set_midi_edit_mode (m);
343 set_midi_edit_cursor(m);
348 Editor::set_midi_edit_mode (MidiEditMode m, bool force)
350 if (drag_info.item) {
354 if (!force && m == midi_edit_mode) {
362 ignore_midi_edit_mode_toggle = true;
364 switch (midi_edit_mode) {
366 midi_tool_select_button.set_active (true);
370 midi_tool_pencil_button.set_active (true);
374 midi_tool_erase_button.set_active (true);
378 ignore_midi_edit_mode_toggle = false;
380 set_midi_edit_cursor (current_midi_edit_mode());
383 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
388 Editor::set_midi_edit_cursor (MidiEditMode m)
390 switch (midi_edit_mode) {
392 current_canvas_cursor = midi_select_cursor;
395 current_canvas_cursor = midi_pencil_cursor;
398 current_canvas_cursor = midi_erase_cursor;
404 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
408 /* in object/audition/timefx mode, any button press sets
409 the selection if the object can be selected. this is a
410 bit of hack, because we want to avoid this if the
411 mouse operation is a region alignment.
413 note: not dbl-click or triple-click
416 if (((mouse_mode != MouseObject) &&
417 (mouse_mode != MouseAudition || item_type != RegionItem) &&
418 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
419 (mouse_mode != MouseRange)) ||
421 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
426 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
428 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
430 /* almost no selection action on modified button-2 or button-3 events */
432 if (item_type != RegionItem && event->button.button != 2) {
438 Selection::Operation op = Keyboard::selection_type (event->button.state);
439 bool press = (event->type == GDK_BUTTON_PRESS);
441 // begin_reversible_command (_("select on click"));
445 case RegionViewNameHighlight:
447 case FadeInHandleItem:
449 case FadeOutHandleItem:
451 if (mouse_mode != MouseRange) {
452 commit = set_selected_regionview_from_click (press, op, true);
453 } else if (event->type == GDK_BUTTON_PRESS) {
454 commit = set_selected_track_from_click (press, op, false);
458 case CrossfadeViewItem:
459 commit = set_selected_track_from_click (press, op, false);
462 case ControlPointItem:
463 commit = set_selected_track_from_click (press, op, true);
464 if (mouse_mode != MouseRange) {
465 commit |= set_selected_control_point_from_click (op, false);
470 /* for context click or range selection, select track */
471 if (event->button.button == 3) {
472 commit = set_selected_track_from_click (press, op, true);
473 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
474 commit = set_selected_track_from_click (press, op, false);
478 case AutomationTrackItem:
479 commit = set_selected_track_from_click (press, op, true);
487 // commit_reversible_command ();
492 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
494 track_canvas.grab_focus();
496 if (session && session->actively_recording()) {
500 button_selection (item, event, item_type);
502 if (drag_info.item == 0 &&
503 (Keyboard::is_delete_event (&event->button) ||
504 Keyboard::is_context_menu_event (&event->button) ||
505 Keyboard::is_edit_event (&event->button))) {
507 /* handled by button release */
511 switch (event->button.button) {
514 if (event->type == GDK_BUTTON_PRESS) {
516 if (drag_info.item) {
517 drag_info.item->ungrab (event->button.time);
520 /* single mouse clicks on any of these item types operate
521 independent of mouse mode, mostly because they are
522 not on the main track canvas or because we want
528 case PlayheadCursorItem:
529 start_cursor_grab (item, event);
533 if (Keyboard::modifier_state_equals (event->button.state,
534 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
535 hide_marker (item, event);
537 start_marker_grab (item, event);
541 case TempoMarkerItem:
542 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
543 start_tempo_marker_copy_grab (item, event);
545 start_tempo_marker_grab (item, event);
549 case MeterMarkerItem:
550 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
551 start_meter_marker_copy_grab (item, event);
553 start_meter_marker_grab (item, event);
563 case RangeMarkerBarItem:
564 start_range_markerbar_op (item, event, CreateRangeMarker);
568 case TransportMarkerBarItem:
569 start_range_markerbar_op (item, event, CreateTransportMarker);
578 switch (mouse_mode) {
581 case StartSelectionTrimItem:
582 start_selection_op (item, event, SelectionStartTrim);
585 case EndSelectionTrimItem:
586 start_selection_op (item, event, SelectionEndTrim);
590 if (Keyboard::modifier_state_contains
591 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
592 // contains and not equals because I can't use alt as a modifier alone.
593 start_selection_grab (item, event);
594 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
595 /* grab selection for moving */
596 start_selection_op (item, event, SelectionMove);
599 /* this was debated, but decided the more common action was to
600 make a new selection */
601 start_selection_op (item, event, CreateSelection);
606 start_selection_op (item, event, CreateSelection);
612 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
613 event->type == GDK_BUTTON_PRESS) {
615 start_rubberband_select (item, event);
617 } else if (event->type == GDK_BUTTON_PRESS) {
620 case FadeInHandleItem:
621 start_fade_in_grab (item, event);
624 case FadeOutHandleItem:
625 start_fade_out_grab (item, event);
629 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
630 start_region_copy_grab (item, event);
631 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
632 start_region_brush_grab (item, event);
634 start_region_grab (item, event);
638 case RegionViewNameHighlight:
639 start_trim (item, event);
644 /* rename happens on edit clicks */
645 start_trim (clicked_regionview->get_name_highlight(), event);
649 case ControlPointItem:
650 start_control_point_grab (item, event);
654 case AutomationLineItem:
655 start_line_grab_from_line (item, event);
660 case AutomationTrackItem:
661 start_rubberband_select (item, event);
665 case ImageFrameHandleStartItem:
666 imageframe_start_handle_op(item, event) ;
669 case ImageFrameHandleEndItem:
670 imageframe_end_handle_op(item, event) ;
673 case MarkerViewHandleStartItem:
674 markerview_item_start_handle_op(item, event) ;
677 case MarkerViewHandleEndItem:
678 markerview_item_end_handle_op(item, event) ;
682 start_markerview_grab(item, event) ;
685 start_imageframe_grab(item, event) ;
703 // start_line_grab_from_regionview (item, event);
707 start_line_grab_from_line (item, event);
710 case ControlPointItem:
711 start_control_point_grab (item, event);
722 case ControlPointItem:
723 start_control_point_grab (item, event);
726 case AutomationLineItem:
727 start_line_grab_from_line (item, event);
731 // XXX need automation mode to identify which
733 // start_line_grab_from_regionview (item, event);
743 if (event->type == GDK_BUTTON_PRESS) {
744 start_mouse_zoom (item, event);
751 if (item_type == RegionItem) {
752 start_time_fx (item, event);
757 /* handled in release */
766 switch (mouse_mode) {
768 if (event->type == GDK_BUTTON_PRESS) {
771 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
772 start_region_copy_grab (item, event);
774 start_region_grab (item, event);
778 case ControlPointItem:
779 start_control_point_grab (item, event);
790 case RegionViewNameHighlight:
791 start_trim (item, event);
796 start_trim (clicked_regionview->get_name_highlight(), event);
807 if (event->type == GDK_BUTTON_PRESS) {
808 /* relax till release */
815 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
816 temporal_zoom_session();
818 temporal_zoom_to_frame (true, event_frame(event));
841 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
843 nframes_t where = event_frame (event, 0, 0);
845 /* no action if we're recording */
847 if (session && session->actively_recording()) {
851 /* first, see if we're finishing a drag ... */
853 if (drag_info.item) {
854 if (end_grab (item, event)) {
855 /* grab dragged, so do nothing else */
860 button_selection (item, event, item_type);
862 /* edit events get handled here */
864 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
870 case TempoMarkerItem:
871 edit_tempo_marker (item);
874 case MeterMarkerItem:
875 edit_meter_marker (item);
879 if (clicked_regionview->name_active()) {
880 return mouse_rename_region (item, event);
890 /* context menu events get handled here */
892 if (Keyboard::is_context_menu_event (&event->button)) {
894 if (drag_info.item == 0) {
896 /* no matter which button pops up the context menu, tell the menu
897 widget to use button 1 to drive menu selection.
902 case FadeInHandleItem:
904 case FadeOutHandleItem:
905 popup_fade_context_menu (1, event->button.time, item, item_type);
910 case RegionViewNameHighlight:
913 case AutomationTrackItem:
914 case CrossfadeViewItem:
915 popup_track_context_menu (1, event->button.time, where);
919 case RangeMarkerBarItem:
920 case TransportMarkerBarItem:
923 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
927 marker_context_menu (&event->button, item);
930 case TempoMarkerItem:
931 tm_marker_context_menu (&event->button, item);
934 case MeterMarkerItem:
935 tm_marker_context_menu (&event->button, item);
940 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
942 case ImageFrameTimeAxisItem:
943 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
946 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
948 case MarkerTimeAxisItem:
949 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
961 /* delete events get handled here */
963 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
966 case TempoMarkerItem:
967 remove_tempo_marker (item);
970 case MeterMarkerItem:
971 remove_meter_marker (item);
975 remove_marker (*item, event);
979 if (mouse_mode == MouseObject) {
980 remove_clicked_region ();
984 case ControlPointItem:
985 if (mouse_mode == MouseGain) {
986 remove_gain_control_point (item, event);
988 remove_control_point (item, event);
998 switch (event->button.button) {
1001 switch (item_type) {
1002 /* see comments in button_press_handler */
1003 case EditCursorItem:
1004 case PlayheadCursorItem:
1007 case AutomationLineItem:
1008 case StartSelectionTrimItem:
1009 case EndSelectionTrimItem:
1013 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1014 snap_to (where, 0, true);
1016 mouse_add_new_marker (where);
1020 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1023 mouse_add_new_tempo_event (where);
1027 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1035 switch (mouse_mode) {
1037 switch (item_type) {
1038 case AutomationTrackItem:
1039 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->add_automation_event
1053 // Gain only makes sense for audio regions
1055 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1059 switch (item_type) {
1061 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1065 case AutomationTrackItem:
1066 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1067 add_automation_event (item, event, where, event->button.y);
1076 switch (item_type) {
1078 audition_selected_region ();
1095 switch (mouse_mode) {
1099 // x_style_paste (where, 1.0);
1119 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1125 switch (item_type) {
1126 case ControlPointItem:
1127 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1128 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1129 cp->set_visible (true);
1133 at_y = cp->get_y ();
1134 cp->item()->i2w (at_x, at_y);
1138 fraction = 1.0 - (cp->get_y() / cp->line().height());
1140 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1141 show_verbose_canvas_cursor ();
1143 if (is_drawable()) {
1144 track_canvas.get_window()->set_cursor (*fader_cursor);
1150 if (mouse_mode == MouseGain) {
1151 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1153 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1154 if (is_drawable()) {
1155 track_canvas.get_window()->set_cursor (*fader_cursor);
1160 case AutomationLineItem:
1161 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1163 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1165 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1167 if (is_drawable()) {
1168 track_canvas.get_window()->set_cursor (*fader_cursor);
1173 case RegionViewNameHighlight:
1174 if (is_drawable() && mouse_mode == MouseObject) {
1175 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1179 case StartSelectionTrimItem:
1180 case EndSelectionTrimItem:
1183 case ImageFrameHandleStartItem:
1184 case ImageFrameHandleEndItem:
1185 case MarkerViewHandleStartItem:
1186 case MarkerViewHandleEndItem:
1189 if (is_drawable()) {
1190 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1194 case EditCursorItem:
1195 case PlayheadCursorItem:
1196 if (is_drawable()) {
1197 track_canvas.get_window()->set_cursor (*grabber_cursor);
1201 case RegionViewName:
1203 /* when the name is not an active item, the entire name highlight is for trimming */
1205 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1206 if (mouse_mode == MouseObject && is_drawable()) {
1207 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1213 case AutomationTrackItem:
1214 if (is_drawable()) {
1215 Gdk::Cursor *cursor;
1216 switch (mouse_mode) {
1218 cursor = selector_cursor;
1221 cursor = zoom_cursor;
1224 cursor = cross_hair_cursor;
1228 track_canvas.get_window()->set_cursor (*cursor);
1230 AutomationTimeAxisView* atv;
1231 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1232 clear_entered_track = false;
1233 set_entered_track (atv);
1239 case RangeMarkerBarItem:
1240 case TransportMarkerBarItem:
1243 if (is_drawable()) {
1244 time_canvas.get_window()->set_cursor (*timebar_cursor);
1249 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1252 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1254 case MeterMarkerItem:
1255 case TempoMarkerItem:
1256 if (is_drawable()) {
1257 time_canvas.get_window()->set_cursor (*timebar_cursor);
1260 case FadeInHandleItem:
1261 case FadeOutHandleItem:
1262 if (mouse_mode == MouseObject) {
1263 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1265 rect->property_fill_color_rgba() = 0;
1266 rect->property_outline_pixels() = 1;
1275 /* second pass to handle entered track status in a comprehensible way.
1278 switch (item_type) {
1280 case AutomationLineItem:
1281 case ControlPointItem:
1282 /* these do not affect the current entered track state */
1283 clear_entered_track = false;
1286 case AutomationTrackItem:
1287 /* handled above already */
1291 set_entered_track (0);
1299 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1308 switch (item_type) {
1309 case ControlPointItem:
1310 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1311 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1312 if (cp->line().npoints() > 1 && !cp->selected()) {
1313 cp->set_visible (false);
1317 if (is_drawable()) {
1318 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1321 hide_verbose_canvas_cursor ();
1324 case RegionViewNameHighlight:
1325 case StartSelectionTrimItem:
1326 case EndSelectionTrimItem:
1327 case EditCursorItem:
1328 case PlayheadCursorItem:
1331 case ImageFrameHandleStartItem:
1332 case ImageFrameHandleEndItem:
1333 case MarkerViewHandleStartItem:
1334 case MarkerViewHandleEndItem:
1337 if (is_drawable()) {
1338 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1343 case AutomationLineItem:
1344 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1346 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1348 line->property_fill_color_rgba() = al->get_line_color();
1350 if (is_drawable()) {
1351 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1355 case RegionViewName:
1356 /* see enter_handler() for notes */
1357 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1358 if (is_drawable() && mouse_mode == MouseObject) {
1359 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1364 case RangeMarkerBarItem:
1365 case TransportMarkerBarItem:
1369 if (is_drawable()) {
1370 time_canvas.get_window()->set_cursor (*timebar_cursor);
1375 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1378 loc = find_location_from_marker (marker, is_start);
1379 if (loc) location_flags_changed (loc, this);
1381 case MeterMarkerItem:
1382 case TempoMarkerItem:
1384 if (is_drawable()) {
1385 time_canvas.get_window()->set_cursor (*timebar_cursor);
1390 case FadeInHandleItem:
1391 case FadeOutHandleItem:
1392 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1394 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1396 rect->property_fill_color_rgba() = rv->get_fill_color();
1397 rect->property_outline_pixels() = 0;
1402 case AutomationTrackItem:
1403 if (is_drawable()) {
1404 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1405 clear_entered_track = true;
1406 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1418 Editor::left_automation_track ()
1420 if (clear_entered_track) {
1421 set_entered_track (0);
1422 clear_entered_track = false;
1428 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1432 /* We call this so that MOTION_NOTIFY events continue to be
1433 delivered to the canvas. We need to do this because we set
1434 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1435 the density of the events, at the expense of a round-trip
1436 to the server. Given that this will mostly occur on cases
1437 where DISPLAY = :0.0, and given the cost of what the motion
1438 event might do, its a good tradeoff.
1441 track_canvas.get_pointer (x, y);
1443 if (current_stepping_trackview) {
1444 /* don't keep the persistent stepped trackview if the mouse moves */
1445 current_stepping_trackview = 0;
1446 step_timeout.disconnect ();
1449 if (session && session->actively_recording()) {
1450 /* Sorry. no dragging stuff around while we record */
1454 drag_info.item_type = item_type;
1455 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1456 &drag_info.current_pointer_y);
1458 if (!from_autoscroll && drag_info.item) {
1459 /* item != 0 is the best test i can think of for dragging.
1461 if (!drag_info.move_threshold_passed) {
1463 bool x_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1464 bool y_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1466 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1468 // and change the initial grab loc/frame if this drag info wants us to
1470 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1471 drag_info.grab_frame = drag_info.current_pointer_frame;
1472 drag_info.grab_x = drag_info.current_pointer_x;
1473 drag_info.grab_y = drag_info.current_pointer_y;
1474 drag_info.last_pointer_frame = drag_info.grab_frame;
1475 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1480 switch (item_type) {
1481 case PlayheadCursorItem:
1482 case EditCursorItem:
1484 case ControlPointItem:
1485 case TempoMarkerItem:
1486 case MeterMarkerItem:
1487 case RegionViewNameHighlight:
1488 case StartSelectionTrimItem:
1489 case EndSelectionTrimItem:
1492 case AutomationLineItem:
1493 case FadeInHandleItem:
1494 case FadeOutHandleItem:
1497 case ImageFrameHandleStartItem:
1498 case ImageFrameHandleEndItem:
1499 case MarkerViewHandleStartItem:
1500 case MarkerViewHandleEndItem:
1503 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1504 (event->motion.state & Gdk::BUTTON2_MASK))) {
1505 if (!from_autoscroll) {
1506 maybe_autoscroll (event);
1508 (this->*(drag_info.motion_callback)) (item, event);
1517 switch (mouse_mode) {
1522 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1523 (event->motion.state & GDK_BUTTON2_MASK))) {
1524 if (!from_autoscroll) {
1525 maybe_autoscroll (event);
1527 (this->*(drag_info.motion_callback)) (item, event);
1538 track_canvas_motion (event);
1539 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1547 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1549 if (drag_info.item == 0) {
1550 fatal << _("programming error: start_grab called without drag item") << endmsg;
1556 cursor = grabber_cursor;
1559 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1561 if (event->button.button == 2) {
1562 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1563 drag_info.y_constrained = true;
1564 drag_info.x_constrained = false;
1566 drag_info.y_constrained = false;
1567 drag_info.x_constrained = true;
1570 drag_info.x_constrained = false;
1571 drag_info.y_constrained = false;
1574 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1575 drag_info.last_pointer_frame = drag_info.grab_frame;
1576 drag_info.current_pointer_frame = drag_info.grab_frame;
1577 drag_info.current_pointer_x = drag_info.grab_x;
1578 drag_info.current_pointer_y = drag_info.grab_y;
1579 drag_info.cumulative_x_drag = 0;
1580 drag_info.cumulative_y_drag = 0;
1581 drag_info.first_move = true;
1582 drag_info.move_threshold_passed = false;
1583 drag_info.want_move_threshold = false;
1584 drag_info.pointer_frame_offset = 0;
1585 drag_info.brushing = false;
1586 drag_info.copied_location = 0;
1588 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1590 event->button.time);
1592 if (session && session->transport_rolling()) {
1593 drag_info.was_rolling = true;
1595 drag_info.was_rolling = false;
1598 switch (snap_type) {
1599 case SnapToRegionStart:
1600 case SnapToRegionEnd:
1601 case SnapToRegionSync:
1602 case SnapToRegionBoundary:
1603 build_region_boundary_cache ();
1611 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1613 drag_info.item->ungrab (0);
1614 drag_info.item = new_item;
1617 cursor = grabber_cursor;
1620 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1624 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1626 bool did_drag = false;
1628 stop_canvas_autoscroll ();
1630 if (drag_info.item == 0) {
1634 drag_info.item->ungrab (event->button.time);
1636 if (drag_info.finished_callback) {
1637 (this->*(drag_info.finished_callback)) (item, event);
1640 did_drag = !drag_info.first_move;
1642 hide_verbose_canvas_cursor();
1645 drag_info.copy = false;
1646 drag_info.motion_callback = 0;
1647 drag_info.finished_callback = 0;
1648 drag_info.last_trackview = 0;
1649 drag_info.last_frame_position = 0;
1650 drag_info.grab_frame = 0;
1651 drag_info.last_pointer_frame = 0;
1652 drag_info.current_pointer_frame = 0;
1653 drag_info.brushing = false;
1655 if (drag_info.copied_location) {
1656 delete drag_info.copied_location;
1657 drag_info.copied_location = 0;
1664 Editor::set_edit_cursor (GdkEvent* event)
1666 nframes_t pointer_frame = event_frame (event);
1668 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1669 if (snap_type != SnapToEditCursor) {
1670 snap_to (pointer_frame);
1674 edit_cursor->set_position (pointer_frame);
1675 edit_cursor_clock.set (pointer_frame);
1679 Editor::set_playhead_cursor (GdkEvent* event)
1681 nframes_t pointer_frame = event_frame (event);
1683 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1684 snap_to (pointer_frame);
1688 session->request_locate (pointer_frame, session->transport_rolling());
1693 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1695 drag_info.item = item;
1696 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1697 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1701 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1702 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1706 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1708 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
1712 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1714 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1716 nframes_t fade_length;
1718 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1719 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1725 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1729 if (pos < (arv->region()->position() + 64)) {
1730 fade_length = 64; // this should be a minimum defined somewhere
1731 } else if (pos > arv->region()->last_frame()) {
1732 fade_length = arv->region()->length();
1734 fade_length = pos - arv->region()->position();
1736 /* mapover the region selection */
1738 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1740 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1746 tmp->reset_fade_in_shape_width (fade_length);
1749 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1751 drag_info.first_move = false;
1755 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1757 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1759 nframes_t fade_length;
1761 if (drag_info.first_move) return;
1763 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1764 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1769 if (pos < (arv->region()->position() + 64)) {
1770 fade_length = 64; // this should be a minimum defined somewhere
1771 } else if (pos > arv->region()->last_frame()) {
1772 fade_length = arv->region()->length();
1774 fade_length = pos - arv->region()->position();
1777 begin_reversible_command (_("change fade in length"));
1779 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1781 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1787 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
1788 XMLNode &before = alist->get_state();
1790 tmp->audio_region()->set_fade_in_length (fade_length);
1792 XMLNode &after = alist->get_state();
1793 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1796 commit_reversible_command ();
1800 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1802 drag_info.item = item;
1803 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1804 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1808 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1809 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1813 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1815 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
1819 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1821 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1823 nframes_t fade_length;
1825 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1826 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1831 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1835 if (pos > (arv->region()->last_frame() - 64)) {
1836 fade_length = 64; // this should really be a minimum fade defined somewhere
1838 else if (pos < arv->region()->position()) {
1839 fade_length = arv->region()->length();
1842 fade_length = arv->region()->last_frame() - pos;
1845 /* mapover the region selection */
1847 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1849 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1855 tmp->reset_fade_out_shape_width (fade_length);
1858 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1860 drag_info.first_move = false;
1864 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1866 if (drag_info.first_move) return;
1868 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1870 nframes_t fade_length;
1872 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1873 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1879 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1883 if (pos > (arv->region()->last_frame() - 64)) {
1884 fade_length = 64; // this should really be a minimum fade defined somewhere
1886 else if (pos < arv->region()->position()) {
1887 fade_length = arv->region()->length();
1890 fade_length = arv->region()->last_frame() - pos;
1893 begin_reversible_command (_("change fade out length"));
1895 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1897 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1903 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
1904 XMLNode &before = alist->get_state();
1906 tmp->audio_region()->set_fade_out_length (fade_length);
1908 XMLNode &after = alist->get_state();
1909 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1912 commit_reversible_command ();
1916 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1918 drag_info.item = item;
1919 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1920 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1924 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1925 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1929 Cursor* cursor = (Cursor *) drag_info.data;
1931 if (cursor == playhead_cursor) {
1932 _dragging_playhead = true;
1934 if (session && drag_info.was_rolling) {
1935 session->request_stop ();
1938 if (session && session->is_auditioning()) {
1939 session->cancel_audition ();
1943 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1945 show_verbose_time_cursor (cursor->current_frame, 10);
1949 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1951 Cursor* cursor = (Cursor *) drag_info.data;
1952 nframes_t adjusted_frame;
1954 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1955 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1961 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1962 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1963 snap_to (adjusted_frame);
1967 if (adjusted_frame == drag_info.last_pointer_frame) return;
1969 cursor->set_position (adjusted_frame);
1971 if (cursor == edit_cursor) {
1972 edit_cursor_clock.set (cursor->current_frame);
1974 UpdateAllTransportClocks (cursor->current_frame);
1977 show_verbose_time_cursor (cursor->current_frame, 10);
1979 drag_info.last_pointer_frame = adjusted_frame;
1980 drag_info.first_move = false;
1984 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1986 if (drag_info.first_move) return;
1988 cursor_drag_motion_callback (item, event);
1990 _dragging_playhead = false;
1992 if (item == &playhead_cursor->canvas_item) {
1994 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1996 } else if (item == &edit_cursor->canvas_item) {
1997 edit_cursor->set_position (edit_cursor->current_frame);
1998 edit_cursor_clock.set (edit_cursor->current_frame);
2003 Editor::update_marker_drag_item (Location *location)
2005 double x1 = frame_to_pixel (location->start());
2006 double x2 = frame_to_pixel (location->end());
2008 if (location->is_mark()) {
2009 marker_drag_line_points.front().set_x(x1);
2010 marker_drag_line_points.back().set_x(x1);
2011 marker_drag_line->property_points() = marker_drag_line_points;
2014 range_marker_drag_rect->property_x1() = x1;
2015 range_marker_drag_rect->property_x2() = x2;
2020 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2024 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2025 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2031 Location *location = find_location_from_marker (marker, is_start);
2033 drag_info.item = item;
2034 drag_info.data = marker;
2035 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2036 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2040 drag_info.copied_location = new Location (*location);
2041 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2043 update_marker_drag_item (location);
2045 if (location->is_mark()) {
2046 marker_drag_line->show();
2047 marker_drag_line->raise_to_top();
2050 range_marker_drag_rect->show();
2051 range_marker_drag_rect->raise_to_top();
2054 if (is_start) show_verbose_time_cursor (location->start(), 10);
2055 else show_verbose_time_cursor (location->end(), 10);
2059 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2062 Marker* marker = (Marker *) drag_info.data;
2063 Location *real_location;
2064 Location *copy_location;
2066 bool move_both = false;
2070 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2071 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2076 nframes_t next = newframe;
2078 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2079 snap_to (newframe, 0, true);
2082 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2086 /* call this to find out if its the start or end */
2088 real_location = find_location_from_marker (marker, is_start);
2090 /* use the copy that we're "dragging" around */
2092 copy_location = drag_info.copied_location;
2094 f_delta = copy_location->end() - copy_location->start();
2096 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2100 if (copy_location->is_mark()) {
2103 copy_location->set_start (newframe);
2107 if (is_start) { // start-of-range marker
2110 copy_location->set_start (newframe);
2111 copy_location->set_end (newframe + f_delta);
2112 } else if (newframe < copy_location->end()) {
2113 copy_location->set_start (newframe);
2115 snap_to (next, 1, true);
2116 copy_location->set_end (next);
2117 copy_location->set_start (newframe);
2120 } else { // end marker
2123 copy_location->set_end (newframe);
2124 copy_location->set_start (newframe - f_delta);
2125 } else if (newframe > copy_location->start()) {
2126 copy_location->set_end (newframe);
2128 } else if (newframe > 0) {
2129 snap_to (next, -1, true);
2130 copy_location->set_start (next);
2131 copy_location->set_end (newframe);
2136 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2137 drag_info.first_move = false;
2139 update_marker_drag_item (copy_location);
2141 LocationMarkers* lm = find_location_markers (real_location);
2142 lm->set_position (copy_location->start(), copy_location->end());
2144 show_verbose_time_cursor (newframe, 10);
2148 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2150 if (drag_info.first_move) {
2151 marker_drag_motion_callback (item, event);
2155 Marker* marker = (Marker *) drag_info.data;
2159 begin_reversible_command ( _("move marker") );
2160 XMLNode &before = session->locations()->get_state();
2162 Location * location = find_location_from_marker (marker, is_start);
2165 if (location->is_mark()) {
2166 location->set_start (drag_info.copied_location->start());
2168 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2172 XMLNode &after = session->locations()->get_state();
2173 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2174 commit_reversible_command ();
2176 marker_drag_line->hide();
2177 range_marker_drag_rect->hide();
2181 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2184 MeterMarker* meter_marker;
2186 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2187 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2191 meter_marker = dynamic_cast<MeterMarker*> (marker);
2193 MetricSection& section (meter_marker->meter());
2195 if (!section.movable()) {
2199 drag_info.item = item;
2200 drag_info.data = marker;
2201 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2202 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2206 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2208 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2212 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2215 MeterMarker* meter_marker;
2217 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2218 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2222 meter_marker = dynamic_cast<MeterMarker*> (marker);
2224 // create a dummy marker for visual representation of moving the copy.
2225 // The actual copying is not done before we reach the finish callback.
2227 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2228 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2229 *new MeterSection(meter_marker->meter()));
2231 drag_info.item = &new_marker->the_item();
2232 drag_info.copy = true;
2233 drag_info.data = new_marker;
2234 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2235 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2239 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2241 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2245 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2247 MeterMarker* marker = (MeterMarker *) drag_info.data;
2248 nframes_t adjusted_frame;
2250 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2251 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2257 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2258 snap_to (adjusted_frame);
2261 if (adjusted_frame == drag_info.last_pointer_frame) return;
2263 marker->set_position (adjusted_frame);
2266 drag_info.last_pointer_frame = adjusted_frame;
2267 drag_info.first_move = false;
2269 show_verbose_time_cursor (adjusted_frame, 10);
2273 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2275 if (drag_info.first_move) return;
2277 meter_marker_drag_motion_callback (drag_info.item, event);
2279 MeterMarker* marker = (MeterMarker *) drag_info.data;
2282 TempoMap& map (session->tempo_map());
2283 map.bbt_time (drag_info.last_pointer_frame, when);
2285 if (drag_info.copy == true) {
2286 begin_reversible_command (_("copy meter mark"));
2287 XMLNode &before = map.get_state();
2288 map.add_meter (marker->meter(), when);
2289 XMLNode &after = map.get_state();
2290 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2291 commit_reversible_command ();
2293 // delete the dummy marker we used for visual representation of copying.
2294 // a new visual marker will show up automatically.
2297 begin_reversible_command (_("move meter mark"));
2298 XMLNode &before = map.get_state();
2299 map.move_meter (marker->meter(), when);
2300 XMLNode &after = map.get_state();
2301 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2302 commit_reversible_command ();
2307 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2310 TempoMarker* tempo_marker;
2312 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2313 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2317 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2318 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2322 MetricSection& section (tempo_marker->tempo());
2324 if (!section.movable()) {
2328 drag_info.item = item;
2329 drag_info.data = marker;
2330 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2331 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2335 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2336 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2340 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2343 TempoMarker* tempo_marker;
2345 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2346 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2350 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2351 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2355 // create a dummy marker for visual representation of moving the copy.
2356 // The actual copying is not done before we reach the finish callback.
2358 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2359 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2360 *new TempoSection(tempo_marker->tempo()));
2362 drag_info.item = &new_marker->the_item();
2363 drag_info.copy = true;
2364 drag_info.data = new_marker;
2365 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2366 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2370 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2372 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2376 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2378 TempoMarker* marker = (TempoMarker *) drag_info.data;
2379 nframes_t adjusted_frame;
2381 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2382 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2388 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2389 snap_to (adjusted_frame);
2392 if (adjusted_frame == drag_info.last_pointer_frame) return;
2394 /* OK, we've moved far enough to make it worth actually move the thing. */
2396 marker->set_position (adjusted_frame);
2398 show_verbose_time_cursor (adjusted_frame, 10);
2400 drag_info.last_pointer_frame = adjusted_frame;
2401 drag_info.first_move = false;
2405 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2407 if (drag_info.first_move) return;
2409 tempo_marker_drag_motion_callback (drag_info.item, event);
2411 TempoMarker* marker = (TempoMarker *) drag_info.data;
2414 TempoMap& map (session->tempo_map());
2415 map.bbt_time (drag_info.last_pointer_frame, when);
2417 if (drag_info.copy == true) {
2418 begin_reversible_command (_("copy tempo mark"));
2419 XMLNode &before = map.get_state();
2420 map.add_tempo (marker->tempo(), when);
2421 XMLNode &after = map.get_state();
2422 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2423 commit_reversible_command ();
2425 // delete the dummy marker we used for visual representation of copying.
2426 // a new visual marker will show up automatically.
2429 begin_reversible_command (_("move tempo mark"));
2430 XMLNode &before = map.get_state();
2431 map.move_tempo (marker->tempo(), when);
2432 XMLNode &after = map.get_state();
2433 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2434 commit_reversible_command ();
2439 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2441 ControlPoint* control_point;
2443 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2444 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2448 // We shouldn't remove the first or last gain point
2449 if (control_point->line().is_last_point(*control_point) ||
2450 control_point->line().is_first_point(*control_point)) {
2454 control_point->line().remove_point (*control_point);
2458 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
2460 ControlPoint* control_point;
2462 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2463 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2467 control_point->line().remove_point (*control_point);
2471 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2473 ControlPoint* control_point;
2475 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2476 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2480 drag_info.item = item;
2481 drag_info.data = control_point;
2482 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2483 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2485 start_grab (event, fader_cursor);
2487 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
2489 double fraction = 1.0 - ((control_point->get_y() - control_point->line().y_position()) / (double)control_point->line().height());
2490 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
2491 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2493 show_verbose_canvas_cursor ();
2497 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2499 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2501 double cx = drag_info.current_pointer_x;
2502 double cy = drag_info.current_pointer_y;
2504 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2505 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2507 if (drag_info.x_constrained) {
2508 cx = drag_info.grab_x;
2510 if (drag_info.y_constrained) {
2511 cy = drag_info.grab_y;
2514 cp->line().parent_group().w2i (cx, cy);
2518 cy = min ((double) (cp->line().y_position() + cp->line().height()), cy);
2520 //translate cx to frames
2521 nframes_t cx_frames = unit_to_frame (cx);
2523 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2524 snap_to (cx_frames);
2527 const double fraction = 1.0 - ((cy - cp->line().y_position()) / (double)cp->line().height());
2531 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2537 cp->line().point_drag (*cp, cx_frames , fraction, push);
2539 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
2541 drag_info.first_move = false;
2545 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2547 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2549 if (drag_info.first_move) {
2553 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2554 reset_point_selection ();
2558 control_point_drag_motion_callback (item, event);
2560 cp->line().end_drag (cp);
2564 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2566 switch (mouse_mode) {
2568 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2569 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2577 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2581 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2582 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2586 start_line_grab (al, event);
2590 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2594 nframes_t frame_within_region;
2596 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2600 cx = event->button.x;
2601 cy = event->button.y;
2602 line->parent_group().w2i (cx, cy);
2603 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2605 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2606 current_line_drag_info.after)) {
2607 /* no adjacent points */
2611 drag_info.item = &line->grab_item();
2612 drag_info.data = line;
2613 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2614 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2616 start_grab (event, fader_cursor);
2618 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2620 line->start_drag (0, drag_info.grab_frame, fraction);
2622 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2623 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2624 show_verbose_canvas_cursor ();
2628 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2630 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2631 double cx = drag_info.current_pointer_x;
2632 double cy = drag_info.current_pointer_y;
2634 line->parent_group().w2i (cx, cy);
2636 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2640 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2646 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2648 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2652 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2654 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2655 line_drag_motion_callback (item, event);
2660 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2662 if (selection->regions.empty() || clicked_regionview == 0) {
2666 drag_info.copy = false;
2667 drag_info.item = item;
2668 drag_info.data = clicked_regionview;
2669 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2670 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2675 TimeAxisView* tvp = clicked_axisview;
2676 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2678 if (tv && tv->is_track()) {
2679 speed = tv->get_diskstream()->speed();
2682 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2683 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2684 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2685 // we want a move threshold
2686 drag_info.want_move_threshold = true;
2688 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2690 begin_reversible_command (_("move region(s)"));
2694 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2696 if (selection->regions.empty() || clicked_regionview == 0) {
2700 drag_info.copy = true;
2701 drag_info.item = item;
2702 drag_info.data = clicked_regionview;
2706 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2707 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2710 if (rtv && rtv->is_track()) {
2711 speed = rtv->get_diskstream()->speed();
2714 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2715 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2716 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2717 // we want a move threshold
2718 drag_info.want_move_threshold = true;
2719 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2720 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2721 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2725 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2727 if (selection->regions.empty() || clicked_regionview == 0) {
2731 drag_info.copy = false;
2732 drag_info.item = item;
2733 drag_info.data = clicked_regionview;
2734 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2735 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2740 TimeAxisView* tvp = clicked_axisview;
2741 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2743 if (tv && tv->is_track()) {
2744 speed = tv->get_diskstream()->speed();
2747 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2748 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2749 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2750 // we want a move threshold
2751 drag_info.want_move_threshold = true;
2752 drag_info.brushing = true;
2754 begin_reversible_command (_("Drag region brush"));
2758 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2762 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2763 nframes_t pending_region_position = 0;
2764 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2765 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2766 bool clamp_y_axis = false;
2767 vector<int32_t> height_list(512) ;
2768 vector<int32_t>::iterator j;
2770 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2772 drag_info.want_move_threshold = false; // don't copy again
2774 /* duplicate the region(s) */
2776 vector<RegionView*> new_regionviews;
2778 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2784 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2785 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2788 nrv = new AudioRegionView (*arv);
2790 nrv = new MidiRegionView (*mrv);
2795 nrv->get_canvas_group()->show ();
2797 new_regionviews.push_back (nrv);
2800 if (new_regionviews.empty()) {
2804 /* reset selection to new regionviews */
2806 selection->set (new_regionviews);
2808 /* reset drag_info data to reflect the fact that we are dragging the copies */
2810 drag_info.data = new_regionviews.front();
2812 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2815 /* Which trackview is this ? */
2817 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2818 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2820 /* The region motion is only processed if the pointer is over
2824 if (!tv || !tv->is_track()) {
2825 /* To make sure we hide the verbose canvas cursor when the mouse is
2826 not held over a track.
2828 hide_verbose_canvas_cursor ();
2832 original_pointer_order = drag_info.last_trackview->order;
2834 /************************************************************
2836 ************************************************************/
2838 if (drag_info.brushing) {
2839 clamp_y_axis = true;
2844 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2846 int32_t children = 0, numtracks = 0;
2847 // XXX hard coding track limit, oh my, so very very bad
2848 bitset <1024> tracks (0x00);
2849 /* get a bitmask representing the visible tracks */
2851 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2852 TimeAxisView *tracklist_timeview;
2853 tracklist_timeview = (*i);
2854 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2855 TimeAxisView::Children children_list;
2857 /* zeroes are audio tracks. ones are other types. */
2859 if (!rtv2->hidden()) {
2861 if (visible_y_high < rtv2->order) {
2862 visible_y_high = rtv2->order;
2864 if (visible_y_low > rtv2->order) {
2865 visible_y_low = rtv2->order;
2868 if (!rtv2->is_track()) {
2869 tracks = tracks |= (0x01 << rtv2->order);
2872 height_list[rtv2->order] = (*i)->height;
2874 if ((children_list = rtv2->get_child_list()).size() > 0) {
2875 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2876 tracks = tracks |= (0x01 << (rtv2->order + children));
2877 height_list[rtv2->order + children] = (*j)->height;
2885 /* find the actual span according to the canvas */
2887 canvas_pointer_y_span = pointer_y_span;
2888 if (drag_info.last_trackview->order >= tv->order) {
2890 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2891 if (height_list[y] == 0 ) {
2892 canvas_pointer_y_span--;
2897 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2898 if ( height_list[y] == 0 ) {
2899 canvas_pointer_y_span++;
2904 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2905 RegionView* rv2 = (*i);
2906 double ix1, ix2, iy1, iy2;
2909 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2910 rv2->get_canvas_group()->i2w (ix1, iy1);
2911 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2912 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2914 if (rtv2->order != original_pointer_order) {
2915 /* this isn't the pointer track */
2917 if (canvas_pointer_y_span > 0) {
2919 /* moving up the canvas */
2920 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
2922 int32_t visible_tracks = 0;
2923 while (visible_tracks < canvas_pointer_y_span ) {
2926 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2927 /* we're passing through a hidden track */
2932 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2933 clamp_y_axis = true;
2937 clamp_y_axis = true;
2940 } else if (canvas_pointer_y_span < 0) {
2942 /*moving down the canvas*/
2944 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2947 int32_t visible_tracks = 0;
2949 while (visible_tracks > canvas_pointer_y_span ) {
2952 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2956 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2957 clamp_y_axis = true;
2962 clamp_y_axis = true;
2968 /* this is the pointer's track */
2969 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2970 clamp_y_axis = true;
2971 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2972 clamp_y_axis = true;
2980 } else if (drag_info.last_trackview == tv) {
2981 clamp_y_axis = true;
2985 if (!clamp_y_axis) {
2986 drag_info.last_trackview = tv;
2989 /************************************************************
2991 ************************************************************/
2993 /* compute the amount of pointer motion in frames, and where
2994 the region would be if we moved it by that much.
2997 if (drag_info.move_threshold_passed) {
2999 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3001 nframes_t sync_frame;
3002 nframes_t sync_offset;
3005 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3007 sync_offset = rv->region()->sync_offset (sync_dir);
3008 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3010 /* we snap if the snap modifier is not enabled.
3013 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3014 snap_to (sync_frame);
3017 if (sync_frame - sync_offset <= sync_frame) {
3018 pending_region_position = sync_frame - (sync_dir*sync_offset);
3020 pending_region_position = 0;
3024 pending_region_position = 0;
3027 if (pending_region_position > max_frames - rv->region()->length()) {
3028 pending_region_position = drag_info.last_frame_position;
3031 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3033 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3035 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3036 to make it appear at the new location.
3039 if (pending_region_position > drag_info.last_frame_position) {
3040 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3042 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3045 drag_info.last_frame_position = pending_region_position;
3052 /* threshold not passed */
3057 /*************************************************************
3059 ************************************************************/
3061 if (x_delta == 0 && (pointer_y_span == 0)) {
3062 /* haven't reached next snap point, and we're not switching
3063 trackviews. nothing to do.
3070 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3072 RegionView* rv2 = (*i);
3074 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3076 double ix1, ix2, iy1, iy2;
3077 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3078 rv2->get_canvas_group()->i2w (ix1, iy1);
3087 /*************************************************************
3089 ************************************************************/
3093 if (drag_info.first_move) {
3094 if (drag_info.move_threshold_passed) {
3105 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3106 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3108 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3110 RegionView* rv = (*i);
3111 double ix1, ix2, iy1, iy2;
3112 int32_t temp_pointer_y_span = pointer_y_span;
3114 /* get item BBox, which will be relative to parent. so we have
3115 to query on a child, then convert to world coordinates using
3119 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3120 rv->get_canvas_group()->i2w (ix1, iy1);
3121 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3122 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3123 RouteTimeAxisView* temp_rtv;
3125 if ((pointer_y_span != 0) && !clamp_y_axis) {
3128 for (j = height_list.begin(); j!= height_list.end(); j++) {
3129 if (x == canvas_rtv->order) {
3130 /* we found the track the region is on */
3131 if (x != original_pointer_order) {
3132 /*this isn't from the same track we're dragging from */
3133 temp_pointer_y_span = canvas_pointer_y_span;
3135 while (temp_pointer_y_span > 0) {
3136 /* we're moving up canvas-wise,
3137 so we need to find the next track height
3139 if (j != height_list.begin()) {
3142 if (x != original_pointer_order) {
3143 /* we're not from the dragged track, so ignore hidden tracks. */
3145 temp_pointer_y_span++;
3149 temp_pointer_y_span--;
3151 while (temp_pointer_y_span < 0) {
3153 if (x != original_pointer_order) {
3155 temp_pointer_y_span--;
3159 if (j != height_list.end()) {
3162 temp_pointer_y_span++;
3164 /* find out where we'll be when we move and set height accordingly */
3166 tvp2 = trackview_by_y_position (iy1 + y_delta);
3167 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3168 rv->set_y_position_and_height (0, temp_rtv->height);
3170 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3171 personally, i think this can confuse things, but never mind.
3174 //const GdkColor& col (temp_rtv->view->get_region_color());
3175 //rv->set_color (const_cast<GdkColor&>(col));
3182 /* prevent the regionview from being moved to before
3183 the zero position on the canvas.
3188 if (-x_delta > ix1) {
3191 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3192 x_delta = max_frames - rv->region()->last_frame();
3196 if (drag_info.first_move) {
3198 /* hide any dependent views */
3200 rv->get_time_axis_view().hide_dependent_views (*rv);
3202 /* this is subtle. raising the regionview itself won't help,
3203 because raise_to_top() just puts the item on the top of
3204 its parent's stack. so, we need to put the trackview canvas_display group
3205 on the top, since its parent is the whole canvas.
3208 rv->get_canvas_group()->raise_to_top();
3209 rv->get_time_axis_view().canvas_display->raise_to_top();
3210 cursor_group->raise_to_top();
3212 rv->fake_set_opaque (true);
3215 if (drag_info.brushing) {
3216 mouse_brush_insert_region (rv, pending_region_position);
3218 rv->move (x_delta, y_delta);
3221 } /* foreach region */
3225 if (drag_info.first_move && drag_info.move_threshold_passed) {
3226 cursor_group->raise_to_top();
3227 drag_info.first_move = false;
3230 if (x_delta != 0 && !drag_info.brushing) {
3231 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3236 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3239 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3240 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3241 bool nocommit = true;
3243 RouteTimeAxisView* rtv;
3244 bool regionview_y_movement;
3245 bool regionview_x_movement;
3246 vector<RegionView*> copies;
3248 /* first_move is set to false if the regionview has been moved in the
3252 if (drag_info.first_move) {
3259 /* The regionview has been moved at some stage during the grab so we need
3260 to account for any mouse movement between this event and the last one.
3263 region_drag_motion_callback (item, event);
3265 if (drag_info.brushing) {
3266 /* all changes were made during motion event handlers */
3268 if (drag_info.copy) {
3269 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3270 copies.push_back (*i);
3277 /* adjust for track speed */
3280 rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3281 if (rtv && rtv->get_diskstream()) {
3282 speed = rtv->get_diskstream()->speed();
3285 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3286 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3288 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3289 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3293 if (drag_info.copy) {
3294 if (drag_info.x_constrained) {
3295 op_string = _("fixed time region copy");
3297 op_string = _("region copy");
3300 if (drag_info.x_constrained) {
3301 op_string = _("fixed time region drag");
3303 op_string = _("region drag");
3307 begin_reversible_command (op_string);
3309 if (regionview_y_movement) {
3311 /* moved to a different audio track. */
3313 vector<RegionView*> new_selection;
3315 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3317 RegionView* rv = (*i);
3319 double ix1, ix2, iy1, iy2;
3321 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3322 rv->get_canvas_group()->i2w (ix1, iy1);
3323 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3324 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3326 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3327 boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3329 where = (nframes_t) (unit_to_frame (ix1) * speed);
3330 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3332 /* undo the previous hide_dependent_views so that xfades don't
3333 disappear on copying regions
3336 rv->get_time_axis_view().reveal_dependent_views (*rv);
3338 if (!drag_info.copy) {
3340 /* the region that used to be in the old playlist is not
3341 moved to the new one - we make a copy of it. as a result,
3342 any existing editor for the region should no longer be
3346 rv->hide_region_editor();
3347 rv->fake_set_opaque (false);
3349 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3350 from_playlist->remove_region ((rv->region()));
3351 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3355 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3357 copies.push_back (rv);
3360 latest_regionview = 0;
3362 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3363 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3364 to_playlist->add_region (new_region, where);
3365 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3368 if (latest_regionview) {
3369 new_selection.push_back (latest_regionview);
3372 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3373 was selected in all of them, then removing it from the playlist will have removed all
3374 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3375 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3376 corresponding regionview, and the selection is now empty).
3378 this could have invalidated any and all iterators into the region selection.
3380 the heuristic we use here is: if the region selection is empty, break out of the loop
3381 here. if the region selection is not empty, then restart the loop because we know that
3382 we must have removed at least the region(view) we've just been working on as well as any
3383 that we processed on previous iterations.
3385 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3386 we can just iterate.
3389 if (drag_info.copy) {
3392 if (selection->regions.empty()) {
3395 i = selection->regions.by_layer().begin();
3400 selection->set (new_selection);
3404 /* motion within a single track */
3406 list<RegionView*> regions = selection->regions.by_layer();
3408 if (drag_info.copy) {
3409 selection->clear_regions();
3412 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3416 if (!rv->region()->can_move()) {
3420 if (regionview_x_movement) {
3421 double ownspeed = 1.0;
3422 rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3424 if (rtv && rtv->get_diskstream()) {
3425 ownspeed = rtv->get_diskstream()->speed();
3428 /* base the new region position on the current position of the regionview.*/
3430 double ix1, ix2, iy1, iy2;
3432 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3433 rv->get_canvas_group()->i2w (ix1, iy1);
3434 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3438 where = rv->region()->position();
3441 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3443 assert (to_playlist);
3447 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3449 if (drag_info.copy) {
3451 boost::shared_ptr<Region> newregion;
3452 boost::shared_ptr<Region> ar;
3453 boost::shared_ptr<Region> mr;
3455 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3456 newregion = RegionFactory::create (ar);
3457 } else if ((mr = boost::dynamic_pointer_cast<MidiRegion>(rv->region())) != 0) {
3458 newregion = RegionFactory::create (mr);
3463 latest_regionview = 0;
3464 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3465 to_playlist->add_region (newregion, (nframes_t) (where * rtv->get_diskstream()->speed()));
3468 if (latest_regionview) {
3469 rtv->reveal_dependent_views (*latest_regionview);
3470 selection->add (latest_regionview);
3475 /* just change the model */
3477 rv->region()->set_position (where, (void*) this);
3483 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3485 if (drag_info.copy) {
3486 copies.push_back (rv);
3494 commit_reversible_command ();
3497 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3503 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3505 /* Either add to or set the set the region selection, unless
3506 this is an alignment click (control used)
3509 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3510 TimeAxisView* tv = &rv.get_time_axis_view();
3511 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3513 if (rtv && rtv->is_track()) {
3514 speed = rtv->get_diskstream()->speed();
3517 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3519 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3521 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3523 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3527 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3533 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3539 nframes_t frame_rate;
3546 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3547 case AudioClock::BBT:
3548 session->bbt_time (frame, bbt);
3549 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3552 case AudioClock::SMPTE:
3553 session->smpte_time (frame, smpte);
3554 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3557 case AudioClock::MinSec:
3558 /* XXX this is copied from show_verbose_duration_cursor() */
3559 frame_rate = session->frame_rate();
3560 hours = frame / (frame_rate * 3600);
3561 frame = frame % (frame_rate * 3600);
3562 mins = frame / (frame_rate * 60);
3563 frame = frame % (frame_rate * 60);
3564 secs = (float) frame / (float) frame_rate;
3565 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3569 snprintf (buf, sizeof(buf), "%u", frame);
3573 if (xpos >= 0 && ypos >=0) {
3574 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3577 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3579 show_verbose_canvas_cursor ();
3583 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3590 nframes_t distance, frame_rate;
3592 Meter meter_at_start(session->tempo_map().meter_at(start));
3598 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3599 case AudioClock::BBT:
3600 session->bbt_time (start, sbbt);
3601 session->bbt_time (end, ebbt);
3604 /* XXX this computation won't work well if the
3605 user makes a selection that spans any meter changes.
3608 ebbt.bars -= sbbt.bars;
3609 if (ebbt.beats >= sbbt.beats) {
3610 ebbt.beats -= sbbt.beats;
3613 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3615 if (ebbt.ticks >= sbbt.ticks) {
3616 ebbt.ticks -= sbbt.ticks;
3619 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3622 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3625 case AudioClock::SMPTE:
3626 session->smpte_duration (end - start, smpte);
3627 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3630 case AudioClock::MinSec:
3631 /* XXX this stuff should be elsewhere.. */
3632 distance = end - start;
3633 frame_rate = session->frame_rate();
3634 hours = distance / (frame_rate * 3600);
3635 distance = distance % (frame_rate * 3600);
3636 mins = distance / (frame_rate * 60);
3637 distance = distance % (frame_rate * 60);
3638 secs = (float) distance / (float) frame_rate;
3639 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3643 snprintf (buf, sizeof(buf), "%u", end - start);
3647 if (xpos >= 0 && ypos >=0) {
3648 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3651 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3653 show_verbose_canvas_cursor ();
3657 Editor::collect_new_region_view (RegionView* rv)
3659 latest_regionview = rv;
3663 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3665 if (clicked_regionview == 0) {
3669 /* lets try to create new Region for the selection */
3671 vector<boost::shared_ptr<AudioRegion> > new_regions;
3672 create_region_from_selection (new_regions);
3674 if (new_regions.empty()) {
3678 /* XXX fix me one day to use all new regions */
3680 boost::shared_ptr<Region> region (new_regions.front());
3682 /* add it to the current stream/playlist.
3684 tricky: the streamview for the track will add a new regionview. we will
3685 catch the signal it sends when it creates the regionview to
3686 set the regionview we want to then drag.
3689 latest_regionview = 0;
3690 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3692 /* A selection grab currently creates two undo/redo operations, one for
3693 creating the new region and another for moving it.
3696 begin_reversible_command (_("selection grab"));
3698 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3700 XMLNode *before = &(playlist->get_state());
3701 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3702 XMLNode *after = &(playlist->get_state());
3703 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3705 commit_reversible_command ();
3709 if (latest_regionview == 0) {
3710 /* something went wrong */
3714 /* we need to deselect all other regionviews, and select this one
3715 i'm ignoring undo stuff, because the region creation will take care of it */
3716 selection->set (latest_regionview);
3718 drag_info.item = latest_regionview->get_canvas_group();
3719 drag_info.data = latest_regionview;
3720 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3721 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3725 drag_info.last_trackview = clicked_axisview;
3726 drag_info.last_frame_position = latest_regionview->region()->position();
3727 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3729 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3733 Editor::cancel_selection ()
3735 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3736 (*i)->hide_selection ();
3738 begin_reversible_command (_("cancel selection"));
3739 selection->clear ();
3740 clicked_selection = 0;
3741 commit_reversible_command ();
3745 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3747 nframes_t start = 0;
3754 drag_info.item = item;
3755 drag_info.motion_callback = &Editor::drag_selection;
3756 drag_info.finished_callback = &Editor::end_selection_op;
3761 case CreateSelection:
3762 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3763 drag_info.copy = true;
3765 drag_info.copy = false;
3767 start_grab (event, selector_cursor);
3770 case SelectionStartTrim:
3771 if (clicked_axisview) {
3772 clicked_axisview->order_selection_trims (item, true);
3774 start_grab (event, trimmer_cursor);
3775 start = selection->time[clicked_selection].start;
3776 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3779 case SelectionEndTrim:
3780 if (clicked_axisview) {
3781 clicked_axisview->order_selection_trims (item, false);
3783 start_grab (event, trimmer_cursor);
3784 end = selection->time[clicked_selection].end;
3785 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3789 start = selection->time[clicked_selection].start;
3791 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3795 if (selection_op == SelectionMove) {
3796 show_verbose_time_cursor(start, 10);
3798 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3803 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3805 nframes_t start = 0;
3808 nframes_t pending_position;
3810 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3811 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3813 pending_position = 0;
3816 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3817 snap_to (pending_position);
3820 /* only alter selection if the current frame is
3821 different from the last frame position (adjusted)
3824 if (pending_position == drag_info.last_pointer_frame) return;
3826 switch (selection_op) {
3827 case CreateSelection:
3829 if (drag_info.first_move) {
3830 snap_to (drag_info.grab_frame);
3833 if (pending_position < drag_info.grab_frame) {
3834 start = pending_position;
3835 end = drag_info.grab_frame;
3837 end = pending_position;
3838 start = drag_info.grab_frame;
3841 /* first drag: Either add to the selection
3842 or create a new selection->
3845 if (drag_info.first_move) {
3847 begin_reversible_command (_("range selection"));
3849 if (drag_info.copy) {
3850 /* adding to the selection */
3851 clicked_selection = selection->add (start, end);
3852 drag_info.copy = false;
3854 /* new selection-> */
3855 clicked_selection = selection->set (clicked_axisview, start, end);
3860 case SelectionStartTrim:
3862 if (drag_info.first_move) {
3863 begin_reversible_command (_("trim selection start"));
3866 start = selection->time[clicked_selection].start;
3867 end = selection->time[clicked_selection].end;
3869 if (pending_position > end) {
3872 start = pending_position;
3876 case SelectionEndTrim:
3878 if (drag_info.first_move) {
3879 begin_reversible_command (_("trim selection end"));
3882 start = selection->time[clicked_selection].start;
3883 end = selection->time[clicked_selection].end;
3885 if (pending_position < start) {
3888 end = pending_position;
3895 if (drag_info.first_move) {
3896 begin_reversible_command (_("move selection"));
3899 start = selection->time[clicked_selection].start;
3900 end = selection->time[clicked_selection].end;
3902 length = end - start;
3904 start = pending_position;
3907 end = start + length;
3912 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3913 start_canvas_autoscroll (1);
3917 selection->replace (clicked_selection, start, end);
3920 drag_info.last_pointer_frame = pending_position;
3921 drag_info.first_move = false;
3923 if (selection_op == SelectionMove) {
3924 show_verbose_time_cursor(start, 10);
3926 show_verbose_time_cursor(pending_position, 10);
3931 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3933 if (!drag_info.first_move) {
3934 drag_selection (item, event);
3935 /* XXX this is not object-oriented programming at all. ick */
3936 if (selection->time.consolidate()) {
3937 selection->TimeChanged ();
3939 commit_reversible_command ();
3941 /* just a click, no pointer movement.*/
3943 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3945 selection->clear_time();
3950 /* XXX what happens if its a music selection? */
3951 session->set_audio_range (selection->time);
3952 stop_canvas_autoscroll ();
3956 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3959 TimeAxisView* tvp = clicked_axisview;
3960 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3962 if (tv && tv->is_track()) {
3963 speed = tv->get_diskstream()->speed();
3966 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3967 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3968 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3970 //drag_info.item = clicked_regionview->get_name_highlight();
3971 drag_info.item = item;
3972 drag_info.motion_callback = &Editor::trim_motion_callback;
3973 drag_info.finished_callback = &Editor::trim_finished_callback;
3975 start_grab (event, trimmer_cursor);
3977 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3978 trim_op = ContentsTrim;
3980 /* These will get overridden for a point trim.*/
3981 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3982 /* closer to start */
3983 trim_op = StartTrim;
3984 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3992 show_verbose_time_cursor(region_start, 10);
3995 show_verbose_time_cursor(region_end, 10);
3998 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4004 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4006 RegionView* rv = clicked_regionview;
4007 nframes_t frame_delta = 0;
4008 bool left_direction;
4009 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4011 /* snap modifier works differently here..
4012 its' current state has to be passed to the
4013 various trim functions in order to work properly
4017 TimeAxisView* tvp = clicked_axisview;
4018 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4019 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4021 if (tv && tv->is_track()) {
4022 speed = tv->get_diskstream()->speed();
4025 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4026 left_direction = true;
4028 left_direction = false;
4032 snap_to (drag_info.current_pointer_frame);
4035 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4039 if (drag_info.first_move) {
4045 trim_type = "Region start trim";
4048 trim_type = "Region end trim";
4051 trim_type = "Region content trim";
4055 begin_reversible_command (trim_type);
4057 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4058 (*i)->fake_set_opaque(false);
4059 (*i)->region()->freeze ();
4061 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4063 arv->temporarily_hide_envelope ();
4065 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4066 insert_result = motion_frozen_playlists.insert (pl);
4067 if (insert_result.second) {
4068 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4073 if (left_direction) {
4074 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4076 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4081 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4084 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4085 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4091 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4094 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4095 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4102 bool swap_direction = false;
4104 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4105 swap_direction = true;
4108 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4109 i != selection->regions.by_layer().end(); ++i)
4111 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4119 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4122 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4125 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4129 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4130 drag_info.first_move = false;
4134 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4136 boost::shared_ptr<Region> region (rv.region());
4138 if (region->locked()) {
4142 nframes_t new_bound;
4145 TimeAxisView* tvp = clicked_axisview;
4146 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4148 if (tv && tv->is_track()) {
4149 speed = tv->get_diskstream()->speed();
4152 if (left_direction) {
4153 if (swap_direction) {
4154 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4156 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4159 if (swap_direction) {
4160 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4162 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4167 snap_to (new_bound);
4169 region->trim_start ((nframes_t) (new_bound * speed), this);
4170 rv.region_changed (StartChanged);
4174 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4176 boost::shared_ptr<Region> region (rv.region());
4178 if (region->locked()) {
4182 nframes_t new_bound;
4185 TimeAxisView* tvp = clicked_axisview;
4186 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4188 if (tv && tv->is_track()) {
4189 speed = tv->get_diskstream()->speed();
4192 if (left_direction) {
4193 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4195 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4199 snap_to (new_bound, (left_direction ? 0 : 1));
4202 region->trim_front ((nframes_t) (new_bound * speed), this);
4204 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4208 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4210 boost::shared_ptr<Region> region (rv.region());
4212 if (region->locked()) {
4216 nframes_t new_bound;
4219 TimeAxisView* tvp = clicked_axisview;
4220 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4222 if (tv && tv->is_track()) {
4223 speed = tv->get_diskstream()->speed();
4226 if (left_direction) {
4227 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4229 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4233 snap_to (new_bound);
4235 region->trim_end ((nframes_t) (new_bound * speed), this);
4236 rv.region_changed (LengthChanged);
4240 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4242 if (!drag_info.first_move) {
4243 trim_motion_callback (item, event);
4245 if (!clicked_regionview->get_selected()) {
4246 thaw_region_after_trim (*clicked_regionview);
4249 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4250 i != selection->regions.by_layer().end(); ++i)
4252 thaw_region_after_trim (**i);
4253 (*i)->fake_set_opaque (true);
4257 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4259 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4262 motion_frozen_playlists.clear ();
4264 commit_reversible_command();
4266 /* no mouse movement */
4272 Editor::point_trim (GdkEvent* event)
4274 RegionView* rv = clicked_regionview;
4275 nframes_t new_bound = drag_info.current_pointer_frame;
4277 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4278 snap_to (new_bound);
4281 /* Choose action dependant on which button was pressed */
4282 switch (event->button.button) {
4284 trim_op = StartTrim;
4285 begin_reversible_command (_("Start point trim"));
4287 if (rv->get_selected()) {
4289 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4290 i != selection->regions.by_layer().end(); ++i)
4292 if (!(*i)->region()->locked()) {
4293 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4294 XMLNode &before = pl->get_state();
4295 (*i)->region()->trim_front (new_bound, this);
4296 XMLNode &after = pl->get_state();
4297 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4303 if (!rv->region()->locked()) {
4304 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4305 XMLNode &before = pl->get_state();
4306 rv->region()->trim_front (new_bound, this);
4307 XMLNode &after = pl->get_state();
4308 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4312 commit_reversible_command();
4317 begin_reversible_command (_("End point trim"));
4319 if (rv->get_selected()) {
4321 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4323 if (!(*i)->region()->locked()) {
4324 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4325 XMLNode &before = pl->get_state();
4326 (*i)->region()->trim_end (new_bound, this);
4327 XMLNode &after = pl->get_state();
4328 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4334 if (!rv->region()->locked()) {
4335 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4336 XMLNode &before = pl->get_state();
4337 rv->region()->trim_end (new_bound, this);
4338 XMLNode &after = pl->get_state();
4339 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4343 commit_reversible_command();
4352 Editor::thaw_region_after_trim (RegionView& rv)
4354 boost::shared_ptr<Region> region (rv.region());
4356 if (region->locked()) {
4360 region->thaw (_("trimmed region"));
4361 XMLNode &after = region->playlist()->get_state();
4362 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4364 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4366 arv->unhide_envelope ();
4370 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4375 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4376 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4380 Location* location = find_location_from_marker (marker, is_start);
4381 location->set_hidden (true, this);
4386 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4392 drag_info.item = item;
4393 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4394 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4396 range_marker_op = op;
4398 if (!temp_location) {
4399 temp_location = new Location;
4403 case CreateRangeMarker:
4404 case CreateTransportMarker:
4406 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4407 drag_info.copy = true;
4409 drag_info.copy = false;
4411 start_grab (event, selector_cursor);
4415 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4420 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4422 nframes_t start = 0;
4424 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4426 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4427 snap_to (drag_info.current_pointer_frame);
4430 /* only alter selection if the current frame is
4431 different from the last frame position.
4434 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4436 switch (range_marker_op) {
4437 case CreateRangeMarker:
4438 case CreateTransportMarker:
4439 if (drag_info.first_move) {
4440 snap_to (drag_info.grab_frame);
4443 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4444 start = drag_info.current_pointer_frame;
4445 end = drag_info.grab_frame;
4447 end = drag_info.current_pointer_frame;
4448 start = drag_info.grab_frame;
4451 /* first drag: Either add to the selection
4452 or create a new selection.
4455 if (drag_info.first_move) {
4457 temp_location->set (start, end);
4461 update_marker_drag_item (temp_location);
4462 range_marker_drag_rect->show();
4463 range_marker_drag_rect->raise_to_top();
4469 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4470 start_canvas_autoscroll (1);
4474 temp_location->set (start, end);
4476 double x1 = frame_to_pixel (start);
4477 double x2 = frame_to_pixel (end);
4478 crect->property_x1() = x1;
4479 crect->property_x2() = x2;
4481 update_marker_drag_item (temp_location);
4484 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4485 drag_info.first_move = false;
4487 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4492 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4494 Location * newloc = 0;
4497 if (!drag_info.first_move) {
4498 drag_range_markerbar_op (item, event);
4500 switch (range_marker_op) {
4501 case CreateRangeMarker:
4503 begin_reversible_command (_("new range marker"));
4504 XMLNode &before = session->locations()->get_state();
4505 session->locations()->next_available_name(rangename,"unnamed");
4506 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4507 session->locations()->add (newloc, true);
4508 XMLNode &after = session->locations()->get_state();
4509 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4510 commit_reversible_command ();
4512 range_bar_drag_rect->hide();
4513 range_marker_drag_rect->hide();
4517 case CreateTransportMarker:
4518 // popup menu to pick loop or punch
4519 new_transport_marker_context_menu (&event->button, item);
4524 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4526 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4531 start = session->locations()->first_mark_before (drag_info.grab_frame);
4532 end = session->locations()->first_mark_after (drag_info.grab_frame);
4534 if (end == max_frames) {
4535 end = session->current_end_frame ();
4539 start = session->current_start_frame ();
4542 switch (mouse_mode) {
4544 /* find the two markers on either side and then make the selection from it */
4545 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4549 /* find the two markers on either side of the click and make the range out of it */
4550 selection->set (0, start, end);
4559 stop_canvas_autoscroll ();
4565 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4567 drag_info.item = item;
4568 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4569 drag_info.finished_callback = &Editor::end_mouse_zoom;
4571 start_grab (event, zoom_cursor);
4573 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4577 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4582 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4583 snap_to (drag_info.current_pointer_frame);
4585 if (drag_info.first_move) {
4586 snap_to (drag_info.grab_frame);
4590 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4592 /* base start and end on initial click position */
4593 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4594 start = drag_info.current_pointer_frame;
4595 end = drag_info.grab_frame;
4597 end = drag_info.current_pointer_frame;
4598 start = drag_info.grab_frame;
4603 if (drag_info.first_move) {
4605 zoom_rect->raise_to_top();
4608 reposition_zoom_rect(start, end);
4610 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4611 drag_info.first_move = false;
4613 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4618 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4620 if (!drag_info.first_move) {
4621 drag_mouse_zoom (item, event);
4623 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4624 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4626 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4629 temporal_zoom_to_frame (false, drag_info.grab_frame);
4631 temporal_zoom_step (false);
4632 center_screen (drag_info.grab_frame);
4640 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4642 double x1 = frame_to_pixel (start);
4643 double x2 = frame_to_pixel (end);
4644 double y2 = full_canvas_height - 1.0;
4646 zoom_rect->property_x1() = x1;
4647 zoom_rect->property_y1() = 1.0;
4648 zoom_rect->property_x2() = x2;
4649 zoom_rect->property_y2() = y2;
4653 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4655 drag_info.item = item;
4656 drag_info.motion_callback = &Editor::drag_rubberband_select;
4657 drag_info.finished_callback = &Editor::end_rubberband_select;
4659 start_grab (event, cross_hair_cursor);
4661 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4665 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4672 /* use a bigger drag threshold than the default */
4674 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4678 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4679 if (drag_info.first_move) {
4680 snap_to (drag_info.grab_frame);
4682 snap_to (drag_info.current_pointer_frame);
4685 /* base start and end on initial click position */
4687 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4688 start = drag_info.current_pointer_frame;
4689 end = drag_info.grab_frame;
4691 end = drag_info.current_pointer_frame;
4692 start = drag_info.grab_frame;
4695 if (drag_info.current_pointer_y < drag_info.grab_y) {
4696 y1 = drag_info.current_pointer_y;
4697 y2 = drag_info.grab_y;
4699 y2 = drag_info.current_pointer_y;
4700 y1 = drag_info.grab_y;
4704 if (start != end || y1 != y2) {
4706 double x1 = frame_to_pixel (start);
4707 double x2 = frame_to_pixel (end);
4709 rubberband_rect->property_x1() = x1;
4710 rubberband_rect->property_y1() = y1;
4711 rubberband_rect->property_x2() = x2;
4712 rubberband_rect->property_y2() = y2;
4714 rubberband_rect->show();
4715 rubberband_rect->raise_to_top();
4717 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4718 drag_info.first_move = false;
4720 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4725 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4727 if (!drag_info.first_move) {
4729 drag_rubberband_select (item, event);
4732 if (drag_info.current_pointer_y < drag_info.grab_y) {
4733 y1 = drag_info.current_pointer_y;
4734 y2 = drag_info.grab_y;
4737 y2 = drag_info.current_pointer_y;
4738 y1 = drag_info.grab_y;
4742 Selection::Operation op = Keyboard::selection_type (event->button.state);
4745 begin_reversible_command (_("rubberband selection"));
4747 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4748 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4750 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4754 commit_reversible_command ();
4758 selection->clear_tracks();
4759 selection->clear_regions();
4760 selection->clear_points ();
4761 selection->clear_lines ();
4764 rubberband_rect->hide();
4769 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4771 using namespace Gtkmm2ext;
4773 ArdourPrompter prompter (false);
4775 prompter.set_prompt (_("Name for region:"));
4776 prompter.set_initial_text (clicked_regionview->region()->name());
4777 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4778 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4779 prompter.show_all ();
4780 switch (prompter.run ()) {
4781 case Gtk::RESPONSE_ACCEPT:
4783 prompter.get_result(str);
4785 clicked_regionview->region()->set_name (str);
4793 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4795 drag_info.item = item;
4796 drag_info.motion_callback = &Editor::time_fx_motion;
4797 drag_info.finished_callback = &Editor::end_time_fx;
4801 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4805 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4807 RegionView* rv = clicked_regionview;
4809 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4810 snap_to (drag_info.current_pointer_frame);
4813 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4817 if (drag_info.current_pointer_frame > rv->region()->position()) {
4818 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4821 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4822 drag_info.first_move = false;
4824 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4828 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4830 clicked_regionview->get_time_axis_view().hide_timestretch ();
4832 if (drag_info.first_move) {
4836 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4837 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4839 begin_reversible_command (_("timestretch"));
4841 if (run_timestretch (selection->regions, percentage) == 0) {
4842 session->commit_reversible_command ();
4847 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4849 /* no brushing without a useful snap setting */
4852 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4855 switch (snap_mode) {
4857 return; /* can't work because it allows region to be placed anywhere */
4862 switch (snap_type) {
4865 case SnapToEditCursor:
4872 /* don't brush a copy over the original */
4874 if (pos == rv->region()->position()) {
4878 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4880 if (rtv == 0 || !rtv->is_track()) {
4884 boost::shared_ptr<Playlist> playlist = rtv->playlist();
4885 double speed = rtv->get_diskstream()->speed();
4887 XMLNode &before = playlist->get_state();
4888 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4889 XMLNode &after = playlist->get_state();
4890 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4892 // playlist is frozen, so we have to update manually
4894 playlist->Modified(); /* EMIT SIGNAL */
4898 Editor::track_height_step_timeout ()
4901 struct timeval delta;
4903 gettimeofday (&now, 0);
4904 timersub (&now, &last_track_height_step_timestamp, &delta);
4906 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4907 current_stepping_trackview = 0;