2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <pbd/error.h>
30 #include <gtkmm2ext/utils.h>
32 #include "ardour_ui.h"
34 #include "time_axis_view.h"
35 #include "audio_time_axis.h"
36 #include "audio_regionview.h"
38 #include "streamview.h"
39 #include "region_gain_line.h"
40 #include "automation_time_axis.h"
43 #include "selection.h"
46 #include "rgb_macros.h"
48 #include <ardour/types.h>
49 #include <ardour/route.h>
50 #include <ardour/audio_track.h>
51 #include <ardour/audio_diskstream.h>
52 #include <ardour/playlist.h>
53 #include <ardour/audioplaylist.h>
54 #include <ardour/audioregion.h>
55 #include <ardour/midi_region.h>
56 #include <ardour/dB.h>
57 #include <ardour/utils.h>
58 #include <ardour/region_factory.h>
65 using namespace ARDOUR;
69 using namespace Editing;
72 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
86 switch (event->type) {
87 case GDK_BUTTON_RELEASE:
88 case GDK_BUTTON_PRESS:
89 case GDK_2BUTTON_PRESS:
90 case GDK_3BUTTON_PRESS:
91 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
93 case GDK_MOTION_NOTIFY:
94 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
96 case GDK_ENTER_NOTIFY:
97 case GDK_LEAVE_NOTIFY:
98 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
101 case GDK_KEY_RELEASE:
102 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
105 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
109 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
110 position is negative (as can be the case with motion events in particular),
111 the frame location is always positive.
114 return pixel_to_frame (*pcx);
118 Editor::mouse_mode_toggled (MouseMode m)
120 if (ignore_mouse_mode_toggle) {
126 if (mouse_select_button.get_active()) {
132 if (mouse_move_button.get_active()) {
138 if (mouse_gain_button.get_active()) {
144 if (mouse_zoom_button.get_active()) {
150 if (mouse_timefx_button.get_active()) {
156 if (mouse_audition_button.get_active()) {
167 Editor::set_mouse_mode (MouseMode m, bool force)
169 if (drag_info.item) {
173 if (!force && m == mouse_mode) {
181 if (mouse_mode != MouseRange) {
183 /* in all modes except range, hide the range selection,
184 show the object (region) selection.
187 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
188 (*i)->set_should_show_selection (true);
190 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
191 (*i)->hide_selection ();
197 in range mode,show the range selection.
200 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
201 if ((*i)->get_selected()) {
202 (*i)->show_selection (selection->time);
207 /* XXX the hack of unsetting all other buttongs should go
208 away once GTK2 allows us to use regular radio buttons drawn like
209 normal buttons, rather than my silly GroupedButton hack.
212 ignore_mouse_mode_toggle = true;
214 switch (mouse_mode) {
216 mouse_select_button.set_active (true);
217 current_canvas_cursor = selector_cursor;
221 mouse_move_button.set_active (true);
222 current_canvas_cursor = grabber_cursor;
226 mouse_gain_button.set_active (true);
227 current_canvas_cursor = cross_hair_cursor;
231 mouse_zoom_button.set_active (true);
232 current_canvas_cursor = zoom_cursor;
236 mouse_timefx_button.set_active (true);
237 current_canvas_cursor = time_fx_cursor; // just use playhead
241 mouse_audition_button.set_active (true);
242 current_canvas_cursor = speaker_cursor;
246 ignore_mouse_mode_toggle = false;
249 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
254 Editor::step_mouse_mode (bool next)
256 switch (current_mouse_mode()) {
258 if (next) set_mouse_mode (MouseRange);
259 else set_mouse_mode (MouseTimeFX);
263 if (next) set_mouse_mode (MouseZoom);
264 else set_mouse_mode (MouseObject);
268 if (next) set_mouse_mode (MouseGain);
269 else set_mouse_mode (MouseRange);
273 if (next) set_mouse_mode (MouseTimeFX);
274 else set_mouse_mode (MouseZoom);
278 if (next) set_mouse_mode (MouseAudition);
279 else set_mouse_mode (MouseGain);
283 if (next) set_mouse_mode (MouseObject);
284 else set_mouse_mode (MouseTimeFX);
290 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
296 /* in object/audition/timefx mode, any button press sets
297 the selection if the object can be selected. this is a
298 bit of hack, because we want to avoid this if the
299 mouse operation is a region alignment.
301 note: not dbl-click or triple-click
304 if (((mouse_mode != MouseObject) &&
305 (mouse_mode != MouseAudition || item_type != RegionItem) &&
306 (mouse_mode != MouseTimeFX || item_type != RegionItem)) ||
307 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
312 Selection::Operation op = Keyboard::selection_type (event->button.state);
313 bool press = (event->type == GDK_BUTTON_PRESS);
315 begin_reversible_command (_("select on click"));
319 c1 = set_selected_track_from_click (press, op, true, true);
320 c2 = set_selected_regionview_from_click (press, op, true);
324 case RegionViewNameHighlight:
326 c1 = set_selected_track_from_click (press, op, true, true);
327 c2 = set_selected_regionview_from_click (press, op, true);
331 case GainAutomationControlPointItem:
332 case PanAutomationControlPointItem:
333 case RedirectAutomationControlPointItem:
334 c1 = set_selected_track_from_click (press, op, true, true);
335 c2 = set_selected_control_point_from_click (press, op, false);
340 commit = set_selected_track_from_click (press, op, true, true);
343 case AutomationTrackItem:
344 commit = set_selected_track_from_click (press, op, true, true);
351 #define SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
352 #ifdef SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
353 /* in range mode, button 1/2/3 press potentially selects a track */
355 if (mouse_mode == MouseRange &&
356 event->type == GDK_BUTTON_PRESS &&
357 event->button.button <= 3) {
362 case AutomationTrackItem:
363 commit = set_selected_track_from_click (press, op, true, true);
372 commit_reversible_command ();
377 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
379 jack_nframes_t where = event_frame (event, 0, 0);
381 track_canvas.grab_focus();
383 if (session && session->actively_recording()) {
387 button_selection (item, event, item_type);
389 if (drag_info.item == 0 &&
390 (Keyboard::is_delete_event (&event->button) ||
391 Keyboard::is_context_menu_event (&event->button) ||
392 Keyboard::is_edit_event (&event->button))) {
394 /* handled by button release */
398 switch (event->button.button) {
401 if (event->type == GDK_BUTTON_PRESS) {
403 if (drag_info.item) {
404 drag_info.item->ungrab (event->button.time);
407 /* single mouse clicks on any of these item types operate
408 independent of mouse mode, mostly because they are
409 not on the main track canvas or because we want
415 case PlayheadCursorItem:
416 start_cursor_grab (item, event);
420 if (Keyboard::modifier_state_equals (event->button.state,
421 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
422 hide_marker (item, event);
424 start_marker_grab (item, event);
428 case TempoMarkerItem:
429 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
430 start_tempo_marker_copy_grab (item, event);
432 start_tempo_marker_grab (item, event);
436 case MeterMarkerItem:
437 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
438 start_meter_marker_copy_grab (item, event);
440 start_meter_marker_grab (item, event);
450 case RangeMarkerBarItem:
451 start_range_markerbar_op (item, event, CreateRangeMarker);
455 case TransportMarkerBarItem:
456 start_range_markerbar_op (item, event, CreateTransportMarker);
465 switch (mouse_mode) {
468 case StartSelectionTrimItem:
469 start_selection_op (item, event, SelectionStartTrim);
472 case EndSelectionTrimItem:
473 start_selection_op (item, event, SelectionEndTrim);
477 if (Keyboard::modifier_state_contains
478 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
479 // contains and not equals because I can't use alt as a modifier alone.
480 start_selection_grab (item, event);
481 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
482 /* grab selection for moving */
483 start_selection_op (item, event, SelectionMove);
486 /* this was debated, but decided the more common action was to
487 make a new selection */
488 start_selection_op (item, event, CreateSelection);
493 start_selection_op (item, event, CreateSelection);
499 if (Keyboard::modifier_state_contains (event->button.state,
500 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt))
501 && event->type == GDK_BUTTON_PRESS) {
503 start_rubberband_select (item, event);
505 } else if (event->type == GDK_BUTTON_PRESS) {
508 case FadeInHandleItem:
509 start_fade_in_grab (item, event);
512 case FadeOutHandleItem:
513 start_fade_out_grab (item, event);
517 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
518 start_region_copy_grab (item, event);
519 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
520 start_region_brush_grab (item, event);
522 start_region_grab (item, event);
526 case RegionViewNameHighlight:
527 start_trim (item, event);
532 /* rename happens on edit clicks */
533 start_trim (clicked_regionview->get_name_highlight(), event);
537 case GainAutomationControlPointItem:
538 case PanAutomationControlPointItem:
539 case RedirectAutomationControlPointItem:
540 start_control_point_grab (item, event);
544 case GainAutomationLineItem:
545 case PanAutomationLineItem:
546 case RedirectAutomationLineItem:
547 start_line_grab_from_line (item, event);
552 case AutomationTrackItem:
553 start_rubberband_select (item, event);
556 /* <CMT Additions> */
557 case ImageFrameHandleStartItem:
558 imageframe_start_handle_op(item, event) ;
561 case ImageFrameHandleEndItem:
562 imageframe_end_handle_op(item, event) ;
565 case MarkerViewHandleStartItem:
566 markerview_item_start_handle_op(item, event) ;
569 case MarkerViewHandleEndItem:
570 markerview_item_end_handle_op(item, event) ;
573 /* </CMT Additions> */
575 /* <CMT Additions> */
577 start_markerview_grab(item, event) ;
580 start_imageframe_grab(item, event) ;
582 /* </CMT Additions> */
598 // start_line_grab_from_regionview (item, event);
601 case GainControlPointItem:
602 start_control_point_grab (item, event);
606 start_line_grab_from_line (item, event);
609 case GainAutomationControlPointItem:
610 case PanAutomationControlPointItem:
611 case RedirectAutomationControlPointItem:
612 start_control_point_grab (item, event);
623 case GainAutomationControlPointItem:
624 case PanAutomationControlPointItem:
625 case RedirectAutomationControlPointItem:
626 start_control_point_grab (item, event);
629 case GainAutomationLineItem:
630 case PanAutomationLineItem:
631 case RedirectAutomationLineItem:
632 start_line_grab_from_line (item, event);
636 // XXX need automation mode to identify which
638 // start_line_grab_from_regionview (item, event);
648 if (event->type == GDK_BUTTON_PRESS) {
649 start_mouse_zoom (item, event);
656 if (item_type == RegionItem) {
657 start_time_fx (item, event);
662 /* handled in release */
671 switch (mouse_mode) {
673 if (event->type == GDK_BUTTON_PRESS) {
676 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
677 start_region_copy_grab (item, event);
679 start_region_grab (item, event);
683 case GainAutomationControlPointItem:
684 case PanAutomationControlPointItem:
685 case RedirectAutomationControlPointItem:
686 start_control_point_grab (item, event);
697 case RegionViewNameHighlight:
698 start_trim (item, event);
703 start_trim (clicked_regionview->get_name_highlight(), event);
714 if (event->type == GDK_BUTTON_PRESS) {
715 /* relax till release */
722 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
723 temporal_zoom_session();
725 temporal_zoom_to_frame (true, event_frame(event));
740 switch (mouse_mode) {
742 //temporal_zoom_to_frame (true, where);
743 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
744 temporal_zoom_to_frame (true, where);
747 temporal_zoom_step (true);
752 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
753 scroll_backward (0.6f);
756 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
757 scroll_tracks_up_line ();
759 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
760 if (clicked_trackview) {
761 if (!current_stepping_trackview) {
762 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
763 current_stepping_trackview = clicked_trackview;
765 gettimeofday (&last_track_height_step_timestamp, 0);
766 current_stepping_trackview->step_height (true);
769 else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
770 temporal_zoom_to_frame (true, where);
777 switch (mouse_mode) {
779 // temporal_zoom_to_frame (false, where);
780 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
781 temporal_zoom_to_frame (false, where);
784 temporal_zoom_step (false);
789 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
790 scroll_forward (0.6f);
793 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
794 scroll_tracks_down_line ();
796 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
797 if (clicked_trackview) {
798 if (!current_stepping_trackview) {
799 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
800 current_stepping_trackview = clicked_trackview;
802 gettimeofday (&last_track_height_step_timestamp, 0);
803 current_stepping_trackview->step_height (false);
805 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
806 temporal_zoom_to_frame (false, where);
821 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
823 jack_nframes_t where = event_frame (event, 0, 0);
825 /* no action if we're recording */
827 if (session && session->actively_recording()) {
831 /* first, see if we're finishing a drag ... */
833 if (drag_info.item) {
834 if (end_grab (item, event)) {
835 /* grab dragged, so do nothing else */
840 button_selection (item, event, item_type);
842 /* edit events get handled here */
844 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
850 case TempoMarkerItem:
851 edit_tempo_marker (item);
854 case MeterMarkerItem:
855 edit_meter_marker (item);
859 if (clicked_regionview->name_active()) {
860 return mouse_rename_region (item, event);
870 /* context menu events get handled here */
872 if (Keyboard::is_context_menu_event (&event->button)) {
874 if (drag_info.item == 0) {
876 /* no matter which button pops up the context menu, tell the menu
877 widget to use button 1 to drive menu selection.
882 case FadeInHandleItem:
884 case FadeOutHandleItem:
885 popup_fade_context_menu (1, event->button.time, item, item_type);
889 popup_track_context_menu (1, event->button.time, item_type, false, where);
893 case RegionViewNameHighlight:
895 popup_track_context_menu (1, event->button.time, item_type, false, where);
899 popup_track_context_menu (1, event->button.time, item_type, true, where);
902 case AutomationTrackItem:
903 popup_track_context_menu (1, event->button.time, item_type, false, where);
907 case RangeMarkerBarItem:
908 case TransportMarkerBarItem:
911 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
915 marker_context_menu (&event->button, item);
918 case TempoMarkerItem:
919 tm_marker_context_menu (&event->button, item);
922 case MeterMarkerItem:
923 tm_marker_context_menu (&event->button, item);
926 case CrossfadeViewItem:
927 popup_track_context_menu (1, event->button.time, item_type, false, where);
930 /* <CMT Additions> */
932 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
934 case ImageFrameTimeAxisItem:
935 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
938 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
940 case MarkerTimeAxisItem:
941 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
943 /* <CMT Additions> */
954 /* delete events get handled here */
956 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
959 case TempoMarkerItem:
960 remove_tempo_marker (item);
963 case MeterMarkerItem:
964 remove_meter_marker (item);
968 remove_marker (*item, event);
972 if (mouse_mode == MouseObject) {
973 remove_clicked_region ();
977 case GainControlPointItem:
978 if (mouse_mode == MouseGain) {
979 remove_gain_control_point (item, event);
983 case GainAutomationControlPointItem:
984 case PanAutomationControlPointItem:
985 case RedirectAutomationControlPointItem:
986 remove_control_point (item, event);
995 switch (event->button.button) {
999 /* see comments in button_press_handler */
1000 case EditCursorItem:
1001 case PlayheadCursorItem:
1004 case GainAutomationLineItem:
1005 case PanAutomationLineItem:
1006 case RedirectAutomationLineItem:
1007 case StartSelectionTrimItem:
1008 case EndSelectionTrimItem:
1012 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1013 snap_to (where, 0, true);
1015 mouse_add_new_marker (where);
1019 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1022 mouse_add_new_tempo_event (where);
1026 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1034 switch (mouse_mode) {
1036 switch (item_type) {
1037 case AutomationTrackItem:
1038 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1053 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
1054 switch (item_type) {
1056 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1060 case AutomationTrackItem:
1061 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1062 add_automation_event (item, event, where, event->button.y);
1071 switch (item_type) {
1073 audition_selected_region ();
1090 switch (mouse_mode) {
1093 switch (item_type) {
1095 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1097 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1100 // Button2 click is unused
1113 // x_style_paste (where, 1.0);
1133 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1139 switch (item_type) {
1140 case GainControlPointItem:
1141 if (mouse_mode == MouseGain) {
1142 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1143 cp->set_visible (true);
1147 at_y = cp->get_y ();
1148 cp->item->i2w (at_x, at_y);
1152 fraction = 1.0 - (cp->get_y() / cp->line.height());
1154 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1155 show_verbose_canvas_cursor ();
1157 if (is_drawable()) {
1158 track_canvas.get_window()->set_cursor (*fader_cursor);
1163 case GainAutomationControlPointItem:
1164 case PanAutomationControlPointItem:
1165 case RedirectAutomationControlPointItem:
1166 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1167 cp->set_visible (true);
1171 at_y = cp->get_y ();
1172 cp->item->i2w (at_x, at_y);
1176 fraction = 1.0 - (cp->get_y() / cp->line.height());
1178 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1179 show_verbose_canvas_cursor ();
1181 if (is_drawable()) {
1182 track_canvas.get_window()->set_cursor (*fader_cursor);
1187 if (mouse_mode == MouseGain) {
1188 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1190 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1191 if (is_drawable()) {
1192 track_canvas.get_window()->set_cursor (*fader_cursor);
1197 case GainAutomationLineItem:
1198 case RedirectAutomationLineItem:
1199 case PanAutomationLineItem:
1201 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1203 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1205 if (is_drawable()) {
1206 track_canvas.get_window()->set_cursor (*fader_cursor);
1210 case RegionViewNameHighlight:
1211 if (is_drawable() && mouse_mode == MouseObject) {
1212 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1216 case StartSelectionTrimItem:
1217 case EndSelectionTrimItem:
1218 /* <CMT Additions> */
1219 case ImageFrameHandleStartItem:
1220 case ImageFrameHandleEndItem:
1221 case MarkerViewHandleStartItem:
1222 case MarkerViewHandleEndItem:
1223 /* </CMT Additions> */
1225 if (is_drawable()) {
1226 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1230 case EditCursorItem:
1231 case PlayheadCursorItem:
1232 if (is_drawable()) {
1233 track_canvas.get_window()->set_cursor (*grabber_cursor);
1237 case RegionViewName:
1239 /* when the name is not an active item, the entire name highlight is for trimming */
1241 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1242 if (mouse_mode == MouseObject && is_drawable()) {
1243 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1249 case AutomationTrackItem:
1250 if (is_drawable()) {
1251 Gdk::Cursor *cursor;
1252 switch (mouse_mode) {
1254 cursor = selector_cursor;
1257 cursor = zoom_cursor;
1260 cursor = cross_hair_cursor;
1264 track_canvas.get_window()->set_cursor (*cursor);
1266 AutomationTimeAxisView* atv;
1267 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1268 clear_entered_track = false;
1269 set_entered_track (atv);
1275 case RangeMarkerBarItem:
1276 case TransportMarkerBarItem:
1279 if (is_drawable()) {
1280 time_canvas.get_window()->set_cursor (*timebar_cursor);
1285 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1288 marker->set_color_rgba (color_map[cEnteredMarker]);
1290 case MeterMarkerItem:
1291 case TempoMarkerItem:
1292 if (is_drawable()) {
1293 time_canvas.get_window()->set_cursor (*timebar_cursor);
1296 case FadeInHandleItem:
1297 case FadeOutHandleItem:
1298 if (mouse_mode == MouseObject) {
1299 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1301 rect->property_fill_color_rgba() = 0;
1302 rect->property_outline_pixels() = 1;
1311 /* second pass to handle entered track status in a comprehensible way.
1314 switch (item_type) {
1316 case GainAutomationLineItem:
1317 case RedirectAutomationLineItem:
1318 case PanAutomationLineItem:
1319 case GainControlPointItem:
1320 case GainAutomationControlPointItem:
1321 case PanAutomationControlPointItem:
1322 case RedirectAutomationControlPointItem:
1323 /* these do not affect the current entered track state */
1324 clear_entered_track = false;
1327 case AutomationTrackItem:
1328 /* handled above already */
1332 set_entered_track (0);
1340 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1349 switch (item_type) {
1350 case GainControlPointItem:
1351 case GainAutomationControlPointItem:
1352 case PanAutomationControlPointItem:
1353 case RedirectAutomationControlPointItem:
1354 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1355 if (cp->line.npoints() > 1) {
1356 if (!cp->selected) {
1357 cp->set_visible (false);
1361 if (is_drawable()) {
1362 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1365 hide_verbose_canvas_cursor ();
1368 case RegionViewNameHighlight:
1369 case StartSelectionTrimItem:
1370 case EndSelectionTrimItem:
1371 case EditCursorItem:
1372 case PlayheadCursorItem:
1373 /* <CMT Additions> */
1374 case ImageFrameHandleStartItem:
1375 case ImageFrameHandleEndItem:
1376 case MarkerViewHandleStartItem:
1377 case MarkerViewHandleEndItem:
1378 /* </CMT Additions> */
1379 if (is_drawable()) {
1380 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1385 case GainAutomationLineItem:
1386 case RedirectAutomationLineItem:
1387 case PanAutomationLineItem:
1388 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1390 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1392 line->property_fill_color_rgba() = al->get_line_color();
1394 if (is_drawable()) {
1395 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1399 case RegionViewName:
1400 /* see enter_handler() for notes */
1401 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1402 if (is_drawable() && mouse_mode == MouseObject) {
1403 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1408 case RangeMarkerBarItem:
1409 case TransportMarkerBarItem:
1413 if (is_drawable()) {
1414 time_canvas.get_window()->set_cursor (*timebar_cursor);
1419 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1422 loc = find_location_from_marker (marker, is_start);
1423 if (loc) location_flags_changed (loc, this);
1425 case MeterMarkerItem:
1426 case TempoMarkerItem:
1428 if (is_drawable()) {
1429 time_canvas.get_window()->set_cursor (*timebar_cursor);
1434 case FadeInHandleItem:
1435 case FadeOutHandleItem:
1436 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1438 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1440 rect->property_fill_color_rgba() = rv->get_fill_color();
1441 rect->property_outline_pixels() = 0;
1446 case AutomationTrackItem:
1447 if (is_drawable()) {
1448 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1449 clear_entered_track = true;
1450 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1462 Editor::left_automation_track ()
1464 if (clear_entered_track) {
1465 set_entered_track (0);
1466 clear_entered_track = false;
1472 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1476 /* We call this so that MOTION_NOTIFY events continue to be
1477 delivered to the canvas. We need to do this because we set
1478 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1479 the density of the events, at the expense of a round-trip
1480 to the server. Given that this will mostly occur on cases
1481 where DISPLAY = :0.0, and given the cost of what the motion
1482 event might do, its a good tradeoff.
1485 track_canvas.get_pointer (x, y);
1487 if (current_stepping_trackview) {
1488 /* don't keep the persistent stepped trackview if the mouse moves */
1489 current_stepping_trackview = 0;
1490 step_timeout.disconnect ();
1493 if (session && session->actively_recording()) {
1494 /* Sorry. no dragging stuff around while we record */
1498 drag_info.item_type = item_type;
1499 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1500 &drag_info.current_pointer_y);
1502 if (!from_autoscroll && drag_info.item) {
1503 /* item != 0 is the best test i can think of for dragging.
1505 if (!drag_info.move_threshold_passed) {
1507 drag_info.move_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1509 // and change the initial grab loc/frame if this drag info wants us to
1511 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1512 drag_info.grab_frame = drag_info.current_pointer_frame;
1513 drag_info.grab_x = drag_info.current_pointer_x;
1514 drag_info.grab_y = drag_info.current_pointer_y;
1515 drag_info.last_pointer_frame = drag_info.grab_frame;
1516 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1521 switch (item_type) {
1522 case PlayheadCursorItem:
1523 case EditCursorItem:
1525 case GainControlPointItem:
1526 case RedirectAutomationControlPointItem:
1527 case GainAutomationControlPointItem:
1528 case PanAutomationControlPointItem:
1529 case TempoMarkerItem:
1530 case MeterMarkerItem:
1531 case RegionViewNameHighlight:
1532 case StartSelectionTrimItem:
1533 case EndSelectionTrimItem:
1536 case RedirectAutomationLineItem:
1537 case GainAutomationLineItem:
1538 case PanAutomationLineItem:
1539 case FadeInHandleItem:
1540 case FadeOutHandleItem:
1541 /* <CMT Additions> */
1542 case ImageFrameHandleStartItem:
1543 case ImageFrameHandleEndItem:
1544 case MarkerViewHandleStartItem:
1545 case MarkerViewHandleEndItem:
1546 /* </CMT Additions> */
1547 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1548 (event->motion.state & Gdk::BUTTON2_MASK))) {
1549 if (!from_autoscroll) {
1550 maybe_autoscroll (event);
1552 (this->*(drag_info.motion_callback)) (item, event);
1561 switch (mouse_mode) {
1566 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1567 (event->motion.state & GDK_BUTTON2_MASK))) {
1568 if (!from_autoscroll) {
1569 maybe_autoscroll (event);
1571 (this->*(drag_info.motion_callback)) (item, event);
1582 track_canvas_motion (event);
1583 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1591 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1593 if (drag_info.item == 0) {
1594 fatal << _("programming error: start_grab called without drag item") << endmsg;
1600 cursor = grabber_cursor;
1603 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1605 if (event->button.button == 2) {
1606 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1607 drag_info.y_constrained = true;
1608 drag_info.x_constrained = false;
1610 drag_info.y_constrained = false;
1611 drag_info.x_constrained = true;
1614 drag_info.x_constrained = false;
1615 drag_info.y_constrained = false;
1618 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1619 drag_info.last_pointer_frame = drag_info.grab_frame;
1620 drag_info.current_pointer_frame = drag_info.grab_frame;
1621 drag_info.current_pointer_x = drag_info.grab_x;
1622 drag_info.current_pointer_y = drag_info.grab_y;
1623 drag_info.cumulative_x_drag = 0;
1624 drag_info.cumulative_y_drag = 0;
1625 drag_info.first_move = true;
1626 drag_info.move_threshold_passed = false;
1627 drag_info.want_move_threshold = false;
1628 drag_info.pointer_frame_offset = 0;
1629 drag_info.brushing = false;
1630 drag_info.copied_location = 0;
1632 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1634 event->button.time);
1636 if (session && session->transport_rolling()) {
1637 drag_info.was_rolling = true;
1639 drag_info.was_rolling = false;
1642 switch (snap_type) {
1643 case SnapToRegionStart:
1644 case SnapToRegionEnd:
1645 case SnapToRegionSync:
1646 case SnapToRegionBoundary:
1647 build_region_boundary_cache ();
1655 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1657 drag_info.item->ungrab (0);
1658 drag_info.item = new_item;
1661 cursor = grabber_cursor;
1664 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1668 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1670 bool did_drag = false;
1672 stop_canvas_autoscroll ();
1674 if (drag_info.item == 0) {
1678 drag_info.item->ungrab (event->button.time);
1680 if (drag_info.finished_callback) {
1681 (this->*(drag_info.finished_callback)) (item, event);
1684 did_drag = !drag_info.first_move;
1686 hide_verbose_canvas_cursor();
1689 drag_info.copy = false;
1690 drag_info.motion_callback = 0;
1691 drag_info.finished_callback = 0;
1692 drag_info.last_trackview = 0;
1693 drag_info.last_frame_position = 0;
1694 drag_info.grab_frame = 0;
1695 drag_info.last_pointer_frame = 0;
1696 drag_info.current_pointer_frame = 0;
1697 drag_info.brushing = false;
1699 if (drag_info.copied_location) {
1700 delete drag_info.copied_location;
1701 drag_info.copied_location = 0;
1708 Editor::set_edit_cursor (GdkEvent* event)
1710 jack_nframes_t pointer_frame = event_frame (event);
1712 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1713 if (snap_type != SnapToEditCursor) {
1714 snap_to (pointer_frame);
1718 edit_cursor->set_position (pointer_frame);
1719 edit_cursor_clock.set (pointer_frame);
1723 Editor::set_playhead_cursor (GdkEvent* event)
1725 jack_nframes_t pointer_frame = event_frame (event);
1727 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1728 snap_to (pointer_frame);
1732 session->request_locate (pointer_frame, session->transport_rolling());
1737 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1739 drag_info.item = item;
1740 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1741 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1745 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1746 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1750 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1752 drag_info.pointer_frame_offset = drag_info.grab_frame - ((jack_nframes_t) arv->audio_region().fade_in().back()->when + arv->region().position());
1756 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1758 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1760 jack_nframes_t fade_length;
1762 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1763 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1769 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1773 if (pos < (arv->region().position() + 64)) {
1774 fade_length = 64; // this should be a minimum defined somewhere
1775 } else if (pos > arv->region().last_frame()) {
1776 fade_length = arv->region().length();
1778 fade_length = pos - arv->region().position();
1781 arv->reset_fade_in_shape_width (fade_length);
1783 show_verbose_duration_cursor (arv->region().position(), arv->region().position() + fade_length, 10);
1785 drag_info.first_move = false;
1789 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1791 if (drag_info.first_move) return;
1793 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1795 jack_nframes_t fade_length;
1797 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1798 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1804 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1808 if (pos < (arv->region().position() + 64)) {
1809 fade_length = 64; // this should be a minimum defined somewhere
1811 else if (pos > arv->region().last_frame()) {
1812 fade_length = arv->region().length();
1815 fade_length = pos - arv->region().position();
1818 begin_reversible_command (_("change fade in length"));
1819 session->add_undo (arv->region().get_memento());
1820 arv->audio_region().set_fade_in_length (fade_length);
1821 session->add_redo_no_execute (arv->region().get_memento());
1822 commit_reversible_command ();
1823 fade_in_drag_motion_callback (item, event);
1827 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1829 drag_info.item = item;
1830 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1831 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1835 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1836 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1840 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1842 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region().length() - (jack_nframes_t) arv->audio_region().fade_out().back()->when + arv->region().position());
1846 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1848 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1850 jack_nframes_t fade_length;
1852 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1853 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1859 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1863 if (pos > (arv->region().last_frame() - 64)) {
1864 fade_length = 64; // this should really be a minimum fade defined somewhere
1866 else if (pos < arv->region().position()) {
1867 fade_length = arv->region().length();
1870 fade_length = arv->region().last_frame() - pos;
1873 arv->reset_fade_out_shape_width (fade_length);
1875 show_verbose_duration_cursor (arv->region().last_frame() - fade_length, arv->region().last_frame(), 10);
1877 drag_info.first_move = false;
1881 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1883 if (drag_info.first_move) return;
1885 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1887 jack_nframes_t fade_length;
1889 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1890 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1896 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1900 if (pos > (arv->region().last_frame() - 64)) {
1901 fade_length = 64; // this should really be a minimum fade defined somewhere
1903 else if (pos < arv->region().position()) {
1904 fade_length = arv->region().length();
1907 fade_length = arv->region().last_frame() - pos;
1910 begin_reversible_command (_("change fade out length"));
1911 session->add_undo (arv->region().get_memento());
1912 arv->audio_region().set_fade_out_length (fade_length);
1913 session->add_redo_no_execute (arv->region().get_memento());
1914 commit_reversible_command ();
1916 fade_out_drag_motion_callback (item, event);
1920 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1922 drag_info.item = item;
1923 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1924 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1928 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1929 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1933 Cursor* cursor = (Cursor *) drag_info.data;
1935 if (session && cursor == playhead_cursor) {
1936 if (drag_info.was_rolling) {
1937 session->request_stop ();
1941 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1943 show_verbose_time_cursor (cursor->current_frame, 10);
1947 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1949 Cursor* cursor = (Cursor *) drag_info.data;
1950 jack_nframes_t adjusted_frame;
1952 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1953 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1959 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1960 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1961 snap_to (adjusted_frame);
1965 if (adjusted_frame == drag_info.last_pointer_frame) return;
1967 cursor->set_position (adjusted_frame);
1969 if (cursor == edit_cursor) {
1970 edit_cursor_clock.set (cursor->current_frame);
1973 show_verbose_time_cursor (cursor->current_frame, 10);
1975 drag_info.last_pointer_frame = adjusted_frame;
1976 drag_info.first_move = false;
1980 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1982 if (drag_info.first_move) return;
1984 cursor_drag_motion_callback (item, event);
1986 if (item == &playhead_cursor->canvas_item) {
1988 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1990 } else if (item == &edit_cursor->canvas_item) {
1991 edit_cursor->set_position (edit_cursor->current_frame);
1992 edit_cursor_clock.set (edit_cursor->current_frame);
1997 Editor::update_marker_drag_item (Location *location)
1999 double x1 = frame_to_pixel (location->start());
2000 double x2 = frame_to_pixel (location->end());
2002 if (location->is_mark()) {
2003 marker_drag_line_points.front().set_x(x1);
2004 marker_drag_line_points.back().set_x(x1);
2005 marker_drag_line->property_points() = marker_drag_line_points;
2008 range_marker_drag_rect->property_x1() = x1;
2009 range_marker_drag_rect->property_x2() = x2;
2014 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2018 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2019 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2025 Location *location = find_location_from_marker (marker, is_start);
2027 drag_info.item = item;
2028 drag_info.data = marker;
2029 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2030 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2034 drag_info.copied_location = new Location (*location);
2035 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2037 update_marker_drag_item (location);
2039 if (location->is_mark()) {
2040 marker_drag_line->show();
2041 marker_drag_line->raise_to_top();
2044 range_marker_drag_rect->show();
2045 range_marker_drag_rect->raise_to_top();
2048 if (is_start) show_verbose_time_cursor (location->start(), 10);
2049 else show_verbose_time_cursor (location->end(), 10);
2053 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2055 jack_nframes_t f_delta;
2056 Marker* marker = (Marker *) drag_info.data;
2057 Location *real_location;
2058 Location *copy_location;
2060 bool move_both = false;
2063 jack_nframes_t newframe;
2064 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2065 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2071 jack_nframes_t next = newframe;
2073 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2074 snap_to (newframe, 0, true);
2077 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2081 /* call this to find out if its the start or end */
2083 real_location = find_location_from_marker (marker, is_start);
2085 /* use the copy that we're "dragging" around */
2087 copy_location = drag_info.copied_location;
2089 f_delta = copy_location->end() - copy_location->start();
2091 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2095 if (copy_location->is_mark()) {
2098 copy_location->set_start (newframe);
2102 if (is_start) { // start-of-range marker
2105 copy_location->set_start (newframe);
2106 copy_location->set_end (newframe + f_delta);
2107 } else if (newframe < copy_location->end()) {
2108 copy_location->set_start (newframe);
2110 snap_to (next, 1, true);
2111 copy_location->set_end (next);
2112 copy_location->set_start (newframe);
2115 } else { // end marker
2118 copy_location->set_end (newframe);
2119 copy_location->set_start (newframe - f_delta);
2120 } else if (newframe > copy_location->start()) {
2121 copy_location->set_end (newframe);
2123 } else if (newframe > 0) {
2124 snap_to (next, -1, true);
2125 copy_location->set_start (next);
2126 copy_location->set_end (newframe);
2131 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2132 drag_info.first_move = false;
2134 update_marker_drag_item (copy_location);
2136 LocationMarkers* lm = find_location_markers (real_location);
2137 lm->set_position (copy_location->start(), copy_location->end());
2139 show_verbose_time_cursor (newframe, 10);
2143 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2145 if (drag_info.first_move) {
2146 marker_drag_motion_callback (item, event);
2150 Marker* marker = (Marker *) drag_info.data;
2154 begin_reversible_command ( _("move marker") );
2155 session->add_undo( session->locations()->get_memento() );
2157 Location * location = find_location_from_marker (marker, is_start);
2160 if (location->is_mark()) {
2161 location->set_start (drag_info.copied_location->start());
2163 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2167 session->add_redo_no_execute( session->locations()->get_memento() );
2168 commit_reversible_command ();
2170 marker_drag_line->hide();
2171 range_marker_drag_rect->hide();
2175 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2178 MeterMarker* meter_marker;
2180 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2181 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2185 meter_marker = dynamic_cast<MeterMarker*> (marker);
2187 MetricSection& section (meter_marker->meter());
2189 if (!section.movable()) {
2193 drag_info.item = item;
2194 drag_info.data = marker;
2195 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2196 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2200 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2202 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2206 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2209 MeterMarker* meter_marker;
2211 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2212 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2216 meter_marker = dynamic_cast<MeterMarker*> (marker);
2218 // create a dummy marker for visual representation of moving the copy.
2219 // The actual copying is not done before we reach the finish callback.
2221 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2222 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2223 *new MeterSection(meter_marker->meter()));
2225 drag_info.item = &new_marker->the_item();
2226 drag_info.copy = true;
2227 drag_info.data = new_marker;
2228 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2229 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2233 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2235 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2239 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2241 MeterMarker* marker = (MeterMarker *) drag_info.data;
2242 jack_nframes_t adjusted_frame;
2244 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2245 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2251 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2252 snap_to (adjusted_frame);
2255 if (adjusted_frame == drag_info.last_pointer_frame) return;
2257 marker->set_position (adjusted_frame);
2260 drag_info.last_pointer_frame = adjusted_frame;
2261 drag_info.first_move = false;
2263 show_verbose_time_cursor (adjusted_frame, 10);
2267 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2269 if (drag_info.first_move) return;
2271 meter_marker_drag_motion_callback (drag_info.item, event);
2273 MeterMarker* marker = (MeterMarker *) drag_info.data;
2276 TempoMap& map (session->tempo_map());
2277 map.bbt_time (drag_info.last_pointer_frame, when);
2279 if (drag_info.copy == true) {
2280 begin_reversible_command (_("copy meter mark"));
2281 session->add_undo (map.get_memento());
2282 map.add_meter (marker->meter(), when);
2283 session->add_redo_no_execute (map.get_memento());
2284 commit_reversible_command ();
2286 // delete the dummy marker we used for visual representation of copying.
2287 // a new visual marker will show up automatically.
2290 begin_reversible_command (_("move meter mark"));
2291 session->add_undo (map.get_memento());
2292 map.move_meter (marker->meter(), when);
2293 session->add_redo_no_execute (map.get_memento());
2294 commit_reversible_command ();
2299 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2302 TempoMarker* tempo_marker;
2304 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2305 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2309 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2310 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2314 MetricSection& section (tempo_marker->tempo());
2316 if (!section.movable()) {
2320 drag_info.item = item;
2321 drag_info.data = marker;
2322 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2323 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2327 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2328 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2332 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2335 TempoMarker* tempo_marker;
2337 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2338 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2342 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2343 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2347 // create a dummy marker for visual representation of moving the copy.
2348 // The actual copying is not done before we reach the finish callback.
2350 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2351 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2352 *new TempoSection(tempo_marker->tempo()));
2354 drag_info.item = &new_marker->the_item();
2355 drag_info.copy = true;
2356 drag_info.data = new_marker;
2357 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2358 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2362 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2364 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2368 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2370 TempoMarker* marker = (TempoMarker *) drag_info.data;
2371 jack_nframes_t adjusted_frame;
2373 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2374 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2380 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2381 snap_to (adjusted_frame);
2384 if (adjusted_frame == drag_info.last_pointer_frame) return;
2386 /* OK, we've moved far enough to make it worth actually move the thing. */
2388 marker->set_position (adjusted_frame);
2390 show_verbose_time_cursor (adjusted_frame, 10);
2392 drag_info.last_pointer_frame = adjusted_frame;
2393 drag_info.first_move = false;
2397 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2399 if (drag_info.first_move) return;
2401 tempo_marker_drag_motion_callback (drag_info.item, event);
2403 TempoMarker* marker = (TempoMarker *) drag_info.data;
2406 TempoMap& map (session->tempo_map());
2407 map.bbt_time (drag_info.last_pointer_frame, when);
2409 if (drag_info.copy == true) {
2410 begin_reversible_command (_("copy tempo mark"));
2411 session->add_undo (map.get_memento());
2412 map.add_tempo (marker->tempo(), when);
2413 session->add_redo_no_execute (map.get_memento());
2414 commit_reversible_command ();
2416 // delete the dummy marker we used for visual representation of copying.
2417 // a new visual marker will show up automatically.
2420 begin_reversible_command (_("move tempo mark"));
2421 session->add_undo (map.get_memento());
2422 map.move_tempo (marker->tempo(), when);
2423 session->add_redo_no_execute (map.get_memento());
2424 commit_reversible_command ();
2429 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2431 ControlPoint* control_point;
2433 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2434 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2438 // We shouldn't remove the first or last gain point
2439 if (control_point->line.is_last_point(*control_point) ||
2440 control_point->line.is_first_point(*control_point)) {
2444 control_point->line.remove_point (*control_point);
2448 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2450 ControlPoint* control_point;
2452 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2453 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2457 control_point->line.remove_point (*control_point);
2461 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2463 ControlPoint* control_point;
2465 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2466 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2470 drag_info.item = item;
2471 drag_info.data = control_point;
2472 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2473 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2475 start_grab (event, fader_cursor);
2477 control_point->line.start_drag (control_point, 0);
2479 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2480 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2481 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2483 show_verbose_canvas_cursor ();
2487 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2489 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2491 double cx = drag_info.current_pointer_x;
2492 double cy = drag_info.current_pointer_y;
2494 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2495 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2497 if (drag_info.x_constrained) {
2498 cx = drag_info.grab_x;
2500 if (drag_info.y_constrained) {
2501 cy = drag_info.grab_y;
2504 cp->line.parent_group().w2i (cx, cy);
2508 cy = min ((double) cp->line.height(), cy);
2510 //translate cx to frames
2511 jack_nframes_t cx_frames = unit_to_frame (cx);
2513 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2514 snap_to (cx_frames);
2517 float fraction = 1.0 - (cy / cp->line.height());
2521 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2527 cp->line.point_drag (*cp, cx_frames , fraction, push);
2529 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2531 drag_info.first_move = false;
2535 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2537 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2539 if (drag_info.first_move) {
2543 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2544 reset_point_selection ();
2548 control_point_drag_motion_callback (item, event);
2550 cp->line.end_drag (cp);
2554 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2556 switch (mouse_mode) {
2558 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2559 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2567 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2571 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2572 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2576 start_line_grab (al, event);
2580 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2584 jack_nframes_t frame_within_region;
2586 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2590 cx = event->button.x;
2591 cy = event->button.y;
2592 line->parent_group().w2i (cx, cy);
2593 frame_within_region = (jack_nframes_t) floor (cx * frames_per_unit);
2595 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2596 current_line_drag_info.after)) {
2597 /* no adjacent points */
2601 drag_info.item = &line->grab_item();
2602 drag_info.data = line;
2603 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2604 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2606 start_grab (event, fader_cursor);
2608 double fraction = 1.0 - (cy / line->height());
2610 line->start_drag (0, fraction);
2612 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2613 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2614 show_verbose_canvas_cursor ();
2618 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2620 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2621 double cx = drag_info.current_pointer_x;
2622 double cy = drag_info.current_pointer_y;
2624 line->parent_group().w2i (cx, cy);
2627 fraction = 1.0 - (cy / line->height());
2631 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2637 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2639 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2643 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2645 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2646 line_drag_motion_callback (item, event);
2651 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2653 if (selection->regions.empty() || clicked_regionview == 0) {
2657 drag_info.copy = false;
2658 drag_info.item = item;
2659 drag_info.data = clicked_regionview;
2660 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2661 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2666 TimeAxisView* tvp = clicked_trackview;
2667 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2669 if (tv && tv->is_audio_track()) {
2670 speed = tv->get_diskstream()->speed();
2673 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region().position() / speed);
2674 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2675 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2676 // we want a move threshold
2677 drag_info.want_move_threshold = true;
2679 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2681 begin_reversible_command (_("move region(s)"));
2685 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2687 if (selection->regions.empty() || clicked_regionview == 0) {
2691 drag_info.copy = true;
2692 drag_info.item = item;
2693 drag_info.data = clicked_regionview;
2697 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2698 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2701 if (atv && atv->is_audio_track()) {
2702 speed = atv->get_diskstream()->speed();
2705 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2706 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region().position() / speed);
2707 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2708 // we want a move threshold
2709 drag_info.want_move_threshold = true;
2710 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2711 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2715 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2717 if (selection->regions.empty() || clicked_regionview == 0) {
2721 drag_info.copy = false;
2722 drag_info.item = item;
2723 drag_info.data = clicked_regionview;
2724 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2725 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2730 TimeAxisView* tvp = clicked_trackview;
2731 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2733 if (tv && tv->is_audio_track()) {
2734 speed = tv->get_diskstream()->speed();
2737 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region().position() / speed);
2738 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2739 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2740 // we want a move threshold
2741 drag_info.want_move_threshold = true;
2742 drag_info.brushing = true;
2744 begin_reversible_command (_("Drag region brush"));
2748 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2750 /* FIXME: type specific (audio only) */
2754 AudioRegionView *rv = reinterpret_cast<AudioRegionView*> (drag_info.data);
2756 jack_nframes_t pending_region_position = 0;
2757 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2758 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2759 bool clamp_y_axis = false;
2760 vector<int32_t> height_list(512) ;
2761 vector<int32_t>::iterator j;
2763 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2765 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2767 drag_info.want_move_threshold = false; // don't copy again
2769 /* this is committed in the grab finished callback. */
2771 begin_reversible_command (_("Drag region copy"));
2773 /* duplicate the region(s) */
2775 vector<RegionView*> new_regionviews;
2777 set<Playlist*> affected_playlists;
2778 pair<set<Playlist*>::iterator,bool> insert_result;
2780 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2785 Playlist* to_playlist = rv->region().playlist();
2786 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2788 insert_result = affected_playlists.insert (to_playlist);
2789 if (insert_result.second) {
2790 session->add_undo (to_playlist->get_memento ());
2793 latest_regionview = 0;
2795 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2797 /* create a new region with the same name. */
2800 Region* newregion = NULL;
2801 if (dynamic_cast<AudioRegion*>(&rv->region()))
2802 newregion = new AudioRegion (dynamic_cast<AudioRegion&>(rv->region()));
2803 else if (dynamic_cast<MidiRegion*>(&rv->region()))
2804 newregion = new MidiRegion (dynamic_cast<MidiRegion&>(rv->region()));
2807 /* if the original region was locked, we don't care */
2809 newregion->set_locked (false);
2811 to_playlist->add_region (*newregion, (jack_nframes_t) (rv->region().position() * atv->get_diskstream()->speed()));
2815 if (latest_regionview) {
2816 new_regionviews.push_back (latest_regionview);
2820 if (new_regionviews.empty()) {
2824 /* reset selection to new regionviews */
2826 selection->set (new_regionviews);
2828 /* reset drag_info data to reflect the fact that we are dragging the copies */
2830 drag_info.data = new_regionviews.front();
2831 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2834 /* Which trackview is this ? */
2836 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2837 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2839 /* The region motion is only processed if the pointer is over
2843 if (!tv || !tv->is_audio_track()) {
2844 /* To make sure we hide the verbose canvas cursor when the mouse is
2845 not held over and audiotrack.
2847 hide_verbose_canvas_cursor ();
2851 original_pointer_order = drag_info.last_trackview->order;
2853 /************************************************************
2855 ************************************************************/
2857 if (drag_info.brushing) {
2858 clamp_y_axis = true;
2863 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2865 int32_t children = 0, numtracks = 0;
2866 // XXX hard coding track limit, oh my, so very very bad
2867 bitset <1024> tracks (0x00);
2868 /* get a bitmask representing the visible tracks */
2870 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2871 TimeAxisView *tracklist_timeview;
2872 tracklist_timeview = (*i);
2873 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2874 list<TimeAxisView*> children_list;
2876 /* zeroes are audio tracks. ones are other types. */
2878 if (!atv2->hidden()) {
2880 if (visible_y_high < atv2->order) {
2881 visible_y_high = atv2->order;
2883 if (visible_y_low > atv2->order) {
2884 visible_y_low = atv2->order;
2887 if (!atv2->is_audio_track()) {
2888 tracks = tracks |= (0x01 << atv2->order);
2891 height_list[atv2->order] = (*i)->height;
2893 if ((children_list = atv2->get_child_list()).size() > 0) {
2894 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2895 tracks = tracks |= (0x01 << (atv2->order + children));
2896 height_list[atv2->order + children] = (*j)->height;
2904 /* find the actual span according to the canvas */
2906 canvas_pointer_y_span = pointer_y_span;
2907 if (drag_info.last_trackview->order >= tv->order) {
2909 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2910 if (height_list[y] == 0 ) {
2911 canvas_pointer_y_span--;
2916 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2917 if ( height_list[y] == 0 ) {
2918 canvas_pointer_y_span++;
2923 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2924 RegionView* rv2 = (*i);
2925 double ix1, ix2, iy1, iy2;
2928 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2929 rv2->get_canvas_group()->i2w (ix1, iy1);
2930 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2931 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2933 if (atv2->order != original_pointer_order) {
2934 /* this isn't the pointer track */
2936 if (canvas_pointer_y_span > 0) {
2938 /* moving up the canvas */
2939 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2941 int32_t visible_tracks = 0;
2942 while (visible_tracks < canvas_pointer_y_span ) {
2945 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2946 /* we're passing through a hidden track */
2951 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2952 clamp_y_axis = true;
2956 clamp_y_axis = true;
2959 } else if (canvas_pointer_y_span < 0) {
2961 /*moving down the canvas*/
2963 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2966 int32_t visible_tracks = 0;
2968 while (visible_tracks > canvas_pointer_y_span ) {
2971 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2975 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2976 clamp_y_axis = true;
2981 clamp_y_axis = true;
2987 /* this is the pointer's track */
2988 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2989 clamp_y_axis = true;
2990 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2991 clamp_y_axis = true;
2999 } else if (drag_info.last_trackview == tv) {
3000 clamp_y_axis = true;
3004 if (!clamp_y_axis) {
3005 drag_info.last_trackview = tv;
3008 /************************************************************
3010 ************************************************************/
3012 /* compute the amount of pointer motion in frames, and where
3013 the region would be if we moved it by that much.
3016 if (drag_info.move_threshold_passed) {
3018 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3020 jack_nframes_t sync_frame;
3021 jack_nframes_t sync_offset;
3024 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3026 sync_offset = rv->region().sync_offset (sync_dir);
3027 sync_frame = rv->region().adjust_to_sync (pending_region_position);
3029 /* we snap if the snap modifier is not enabled.
3032 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3033 snap_to (sync_frame);
3036 if (sync_frame - sync_offset <= sync_frame) {
3037 pending_region_position = sync_frame - (sync_dir*sync_offset);
3039 pending_region_position = 0;
3043 pending_region_position = 0;
3046 if (pending_region_position > max_frames - rv->region().length()) {
3047 pending_region_position = drag_info.last_frame_position;
3050 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3052 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3054 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3055 to make it appear at the new location.
3058 if (pending_region_position > drag_info.last_frame_position) {
3059 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3061 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3064 drag_info.last_frame_position = pending_region_position;
3071 /* threshold not passed */
3076 /*************************************************************
3078 ************************************************************/
3080 if (x_delta == 0 && (pointer_y_span == 0)) {
3081 /* haven't reached next snap point, and we're not switching
3082 trackviews. nothing to do.
3088 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3090 RegionView* rv2 = (*i);
3092 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3094 double ix1, ix2, iy1, iy2;
3095 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3096 rv2->get_canvas_group()->i2w (ix1, iy1);
3105 /*************************************************************
3107 ************************************************************/
3109 pair<set<Playlist*>::iterator,bool> insert_result;
3110 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3112 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3114 RegionView* rv = (*i);
3115 double ix1, ix2, iy1, iy2;
3116 int32_t temp_pointer_y_span = pointer_y_span;
3118 /* get item BBox, which will be relative to parent. so we have
3119 to query on a child, then convert to world coordinates using
3123 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3124 rv->get_canvas_group()->i2w (ix1, iy1);
3125 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3126 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3127 AudioTimeAxisView* temp_atv;
3129 if ((pointer_y_span != 0) && !clamp_y_axis) {
3132 for (j = height_list.begin(); j!= height_list.end(); j++) {
3133 if (x == canvas_atv->order) {
3134 /* we found the track the region is on */
3135 if (x != original_pointer_order) {
3136 /*this isn't from the same track we're dragging from */
3137 temp_pointer_y_span = canvas_pointer_y_span;
3139 while (temp_pointer_y_span > 0) {
3140 /* we're moving up canvas-wise,
3141 so we need to find the next track height
3143 if (j != height_list.begin()) {
3146 if (x != original_pointer_order) {
3147 /* we're not from the dragged track, so ignore hidden tracks. */
3149 temp_pointer_y_span++;
3153 temp_pointer_y_span--;
3155 while (temp_pointer_y_span < 0) {
3157 if (x != original_pointer_order) {
3159 temp_pointer_y_span--;
3163 if (j != height_list.end()) {
3166 temp_pointer_y_span++;
3168 /* find out where we'll be when we move and set height accordingly */
3170 tvp2 = trackview_by_y_position (iy1 + y_delta);
3171 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3172 rv->set_height (temp_atv->height);
3174 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3175 personally, i think this can confuse things, but never mind.
3178 //const GdkColor& col (temp_atv->view->get_region_color());
3179 //rv->set_color (const_cast<GdkColor&>(col));
3186 /* prevent the regionview from being moved to before
3187 the zero position on the canvas.
3192 if (-x_delta > ix1) {
3195 } else if ((x_delta > 0) &&(rv->region().last_frame() > max_frames - x_delta)) {
3196 x_delta = max_frames - rv->region().last_frame();
3199 if (drag_info.first_move) {
3201 /* hide any dependent views */
3203 // rv->get_time_axis_view().hide_dependent_views (*rv);
3205 /* this is subtle. raising the regionview itself won't help,
3206 because raise_to_top() just puts the item on the top of
3207 its parent's stack. so, we need to put the trackview canvas_display group
3208 on the top, since its parent is the whole canvas.
3211 rv->get_canvas_group()->raise_to_top();
3212 rv->get_time_axis_view().canvas_display->raise_to_top();
3213 cursor_group->raise_to_top();
3215 /* freeze the playlists from notifying till
3219 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3220 if (atv && atv->is_audio_track()) {
3221 AudioPlaylist* pl = dynamic_cast<AudioPlaylist*>(atv->get_diskstream()->playlist());
3223 /* only freeze and capture state once */
3225 insert_result = motion_frozen_playlists.insert (pl);
3226 if (insert_result.second) {
3228 session->add_undo(pl->get_memento());
3234 if (drag_info.brushing) {
3235 mouse_brush_insert_region (rv, pending_region_position);
3237 rv->move (x_delta, y_delta);
3241 if (drag_info.first_move) {
3242 cursor_group->raise_to_top();
3245 drag_info.first_move = false;
3247 if (x_delta != 0 && !drag_info.brushing) {
3248 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3254 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3256 jack_nframes_t where;
3257 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3258 pair<set<Playlist*>::iterator,bool> insert_result;
3259 bool nocommit = true;
3261 RouteTimeAxisView* atv;
3262 bool regionview_y_movement;
3263 bool regionview_x_movement;
3265 /* first_move is set to false if the regionview has been moved in the
3269 if (drag_info.first_move) {
3276 /* The regionview has been moved at some stage during the grab so we need
3277 to account for any mouse movement between this event and the last one.
3280 region_drag_motion_callback (item, event);
3282 if (drag_info.brushing) {
3283 /* all changes were made during motion event handlers */
3287 /* adjust for track speed */
3290 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3291 if (atv && atv->get_diskstream()) {
3292 speed = atv->get_diskstream()->speed();
3295 regionview_x_movement = (drag_info.last_frame_position != (jack_nframes_t) (rv->region().position()/speed));
3296 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3298 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3299 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3301 if (regionview_y_movement) {
3303 /* motion between tracks */
3305 list<RegionView*> new_selection;
3307 /* moved to a different audio track. */
3309 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3311 RegionView* rv2 = (*i);
3313 /* the region that used to be in the old playlist is not
3314 moved to the new one - we make a copy of it. as a result,
3315 any existing editor for the region should no longer be
3319 if (!drag_info.copy) {
3320 rv2->hide_region_editor();
3322 new_selection.push_back (rv2);
3326 /* first, freeze the target tracks */
3328 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3330 Playlist* from_playlist;
3331 Playlist* to_playlist;
3333 double ix1, ix2, iy1, iy2;
3335 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3336 (*i)->get_canvas_group()->i2w (ix1, iy1);
3337 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3338 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3340 from_playlist = (*i)->region().playlist();
3341 to_playlist = atv2->playlist();
3343 /* the from_playlist was frozen in the "first_move" case
3344 of the motion handler. the insert can fail,
3345 but that doesn't matter. it just means
3346 we already have the playlist in the list.
3349 motion_frozen_playlists.insert (from_playlist);
3351 /* only freeze the to_playlist once */
3353 insert_result = motion_frozen_playlists.insert(to_playlist);
3354 if (insert_result.second) {
3355 to_playlist->freeze();
3356 session->add_undo(to_playlist->get_memento());
3361 /* now do it again with the actual operations */
3363 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3365 Playlist* from_playlist;
3366 Playlist* to_playlist;
3368 double ix1, ix2, iy1, iy2;
3370 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3371 (*i)->get_canvas_group()->i2w (ix1, iy1);
3372 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3373 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3375 from_playlist = (*i)->region().playlist();
3376 to_playlist = atv2->playlist();
3378 latest_regionview = 0;
3380 where = (jack_nframes_t) (unit_to_frame (ix1) * speed);
3381 Region* new_region = createRegion ((*i)->region());
3383 from_playlist->remove_region (&((*i)->region()));
3385 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3386 to_playlist->add_region (*new_region, where);
3389 if (latest_regionview) {
3390 selection->add (latest_regionview);
3396 /* motion within a single track */
3398 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3402 if (rv->region().locked()) {
3406 if (regionview_x_movement) {
3407 double ownspeed = 1.0;
3408 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3410 if (atv && atv->get_diskstream()) {
3411 ownspeed = atv->get_diskstream()->speed();
3414 /* base the new region position on the current position of the regionview.*/
3416 double ix1, ix2, iy1, iy2;
3418 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3419 rv->get_canvas_group()->i2w (ix1, iy1);
3420 where = (jack_nframes_t) (unit_to_frame (ix1) * ownspeed);
3424 where = rv->region().position();
3427 rv->get_time_axis_view().reveal_dependent_views (*rv);
3429 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3431 rv->region().set_position (where, (void *) this);
3436 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3438 session->add_redo_no_execute ((*p)->get_memento());
3441 motion_frozen_playlists.clear ();
3444 commit_reversible_command ();
3449 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3451 /* Either add to or set the set the region selection, unless
3452 this is an alignment click (control used)
3455 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3456 TimeAxisView* tv = &rv.get_time_axis_view();
3457 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3459 if (atv && atv->is_audio_track()) {
3460 speed = atv->get_diskstream()->speed();
3463 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3465 align_region (rv.region(), SyncPoint, (jack_nframes_t) (edit_cursor->current_frame * speed));
3467 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3469 align_region (rv.region(), End, (jack_nframes_t) (edit_cursor->current_frame * speed));
3473 align_region (rv.region(), Start, (jack_nframes_t) (edit_cursor->current_frame * speed));
3479 Editor::show_verbose_time_cursor (jack_nframes_t frame, double offset, double xpos, double ypos)
3490 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3491 case AudioClock::BBT:
3492 session->bbt_time (frame, bbt);
3493 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3496 case AudioClock::SMPTE:
3497 session->smpte_time (frame, smpte);
3498 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3501 case AudioClock::MinSec:
3502 /* XXX fix this to compute min/sec properly */
3503 session->smpte_time (frame, smpte);
3504 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3505 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3509 snprintf (buf, sizeof(buf), "%u", frame);
3513 if (xpos >= 0 && ypos >=0) {
3514 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3517 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3519 show_verbose_canvas_cursor ();
3523 Editor::show_verbose_duration_cursor (jack_nframes_t start, jack_nframes_t end, double offset, double xpos, double ypos)
3530 Meter meter_at_start(session->tempo_map().meter_at(start));
3536 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3537 case AudioClock::BBT:
3538 session->bbt_time (start, sbbt);
3539 session->bbt_time (end, ebbt);
3542 /* XXX this computation won't work well if the
3543 user makes a selection that spans any meter changes.
3546 ebbt.bars -= sbbt.bars;
3547 if (ebbt.beats >= sbbt.beats) {
3548 ebbt.beats -= sbbt.beats;
3551 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3553 if (ebbt.ticks >= sbbt.ticks) {
3554 ebbt.ticks -= sbbt.ticks;
3557 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3560 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3563 case AudioClock::SMPTE:
3564 session->smpte_duration (end - start, smpte);
3565 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3568 case AudioClock::MinSec:
3569 /* XXX fix this to compute min/sec properly */
3570 session->smpte_duration (end - start, smpte);
3571 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3572 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3576 snprintf (buf, sizeof(buf), "%u", end - start);
3580 if (xpos >= 0 && ypos >=0) {
3581 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3584 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3586 show_verbose_canvas_cursor ();
3590 Editor::collect_new_region_view (RegionView* rv)
3592 latest_regionview = rv;
3596 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3598 if (clicked_regionview == 0) {
3602 /* lets try to create new Region for the selection */
3604 vector<AudioRegion*> new_regions;
3605 create_region_from_selection (new_regions);
3607 if (new_regions.empty()) {
3611 /* XXX fix me one day to use all new regions */
3613 Region* region = new_regions.front();
3615 /* add it to the current stream/playlist.
3617 tricky: the streamview for the track will add a new regionview. we will
3618 catch the signal it sends when it creates the regionview to
3619 set the regionview we want to then drag.
3622 latest_regionview = 0;
3623 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3625 /* A selection grab currently creates two undo/redo operations, one for
3626 creating the new region and another for moving it.
3629 begin_reversible_command (_("selection grab"));
3631 Playlist* playlist = clicked_trackview->playlist();
3633 session->add_undo (playlist->get_memento ());
3634 clicked_trackview->playlist()->add_region (*region, selection->time[clicked_selection].start);
3635 session->add_redo_no_execute (playlist->get_memento ());
3637 commit_reversible_command ();
3641 if (latest_regionview == 0) {
3642 /* something went wrong */
3646 /* we need to deselect all other regionviews, and select this one
3647 i'm ignoring undo stuff, because the region creation will take care of it */
3648 selection->set (latest_regionview);
3650 drag_info.item = latest_regionview->get_canvas_group();
3651 drag_info.data = latest_regionview;
3652 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3653 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3657 drag_info.last_trackview = clicked_trackview;
3658 drag_info.last_frame_position = latest_regionview->region().position();
3659 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3661 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3665 Editor::cancel_selection ()
3667 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3668 (*i)->hide_selection ();
3670 begin_reversible_command (_("cancel selection"));
3671 selection->clear ();
3672 clicked_selection = 0;
3673 commit_reversible_command ();
3677 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3679 jack_nframes_t start = 0;
3680 jack_nframes_t end = 0;
3686 drag_info.item = item;
3687 drag_info.motion_callback = &Editor::drag_selection;
3688 drag_info.finished_callback = &Editor::end_selection_op;
3693 case CreateSelection:
3694 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3695 drag_info.copy = true;
3697 drag_info.copy = false;
3699 start_grab (event, selector_cursor);
3702 case SelectionStartTrim:
3703 if (clicked_trackview) {
3704 clicked_trackview->order_selection_trims (item, true);
3706 start_grab (event, trimmer_cursor);
3707 start = selection->time[clicked_selection].start;
3708 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3711 case SelectionEndTrim:
3712 if (clicked_trackview) {
3713 clicked_trackview->order_selection_trims (item, false);
3715 start_grab (event, trimmer_cursor);
3716 end = selection->time[clicked_selection].end;
3717 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3721 start = selection->time[clicked_selection].start;
3723 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3727 if (selection_op == SelectionMove) {
3728 show_verbose_time_cursor(start, 10);
3730 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3735 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3737 jack_nframes_t start = 0;
3738 jack_nframes_t end = 0;
3739 jack_nframes_t length;
3740 jack_nframes_t pending_position;
3742 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3743 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3746 pending_position = 0;
3749 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3750 snap_to (pending_position);
3753 /* only alter selection if the current frame is
3754 different from the last frame position (adjusted)
3757 if (pending_position == drag_info.last_pointer_frame) return;
3759 switch (selection_op) {
3760 case CreateSelection:
3762 if (drag_info.first_move) {
3763 snap_to (drag_info.grab_frame);
3766 if (pending_position < drag_info.grab_frame) {
3767 start = pending_position;
3768 end = drag_info.grab_frame;
3770 end = pending_position;
3771 start = drag_info.grab_frame;
3774 /* first drag: Either add to the selection
3775 or create a new selection->
3778 if (drag_info.first_move) {
3780 begin_reversible_command (_("range selection"));
3782 if (drag_info.copy) {
3783 /* adding to the selection */
3784 clicked_selection = selection->add (start, end);
3785 drag_info.copy = false;
3787 /* new selection-> */
3788 clicked_selection = selection->set (clicked_trackview, start, end);
3793 case SelectionStartTrim:
3795 if (drag_info.first_move) {
3796 begin_reversible_command (_("trim selection start"));
3799 start = selection->time[clicked_selection].start;
3800 end = selection->time[clicked_selection].end;
3802 if (pending_position > end) {
3805 start = pending_position;
3809 case SelectionEndTrim:
3811 if (drag_info.first_move) {
3812 begin_reversible_command (_("trim selection end"));
3815 start = selection->time[clicked_selection].start;
3816 end = selection->time[clicked_selection].end;
3818 if (pending_position < start) {
3821 end = pending_position;
3828 if (drag_info.first_move) {
3829 begin_reversible_command (_("move selection"));
3832 start = selection->time[clicked_selection].start;
3833 end = selection->time[clicked_selection].end;
3835 length = end - start;
3837 start = pending_position;
3840 end = start + length;
3845 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3846 start_canvas_autoscroll (1);
3850 selection->replace (clicked_selection, start, end);
3853 drag_info.last_pointer_frame = pending_position;
3854 drag_info.first_move = false;
3856 if (selection_op == SelectionMove) {
3857 show_verbose_time_cursor(start, 10);
3859 show_verbose_time_cursor(pending_position, 10);
3864 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3866 if (!drag_info.first_move) {
3867 drag_selection (item, event);
3868 /* XXX this is not object-oriented programming at all. ick */
3869 if (selection->time.consolidate()) {
3870 selection->TimeChanged ();
3872 commit_reversible_command ();
3874 /* just a click, no pointer movement.*/
3876 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3878 selection->clear_time();
3883 /* XXX what happens if its a music selection? */
3884 session->set_audio_range (selection->time);
3885 stop_canvas_autoscroll ();
3889 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3892 TimeAxisView* tvp = clicked_trackview;
3893 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3895 if (tv && tv->is_audio_track()) {
3896 speed = tv->get_diskstream()->speed();
3899 jack_nframes_t region_start = (jack_nframes_t) (clicked_regionview->region().position() / speed);
3900 jack_nframes_t region_end = (jack_nframes_t) (clicked_regionview->region().last_frame() / speed);
3901 jack_nframes_t region_length = (jack_nframes_t) (clicked_regionview->region().length() / speed);
3903 motion_frozen_playlists.clear();
3905 //drag_info.item = clicked_regionview->get_name_highlight();
3906 drag_info.item = item;
3907 drag_info.motion_callback = &Editor::trim_motion_callback;
3908 drag_info.finished_callback = &Editor::trim_finished_callback;
3910 start_grab (event, trimmer_cursor);
3912 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3913 trim_op = ContentsTrim;
3915 /* These will get overridden for a point trim.*/
3916 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3917 /* closer to start */
3918 trim_op = StartTrim;
3919 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3927 show_verbose_time_cursor(region_start, 10);
3930 show_verbose_time_cursor(region_end, 10);
3933 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3939 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3941 RegionView* rv = clicked_regionview;
3942 jack_nframes_t frame_delta = 0;
3943 bool left_direction;
3944 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3946 /* snap modifier works differently here..
3947 its' current state has to be passed to the
3948 various trim functions in order to work properly
3952 TimeAxisView* tvp = clicked_trackview;
3953 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3954 pair<set<Playlist*>::iterator,bool> insert_result;
3956 if (tv && tv->is_audio_track()) {
3957 speed = tv->get_diskstream()->speed();
3960 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3961 left_direction = true;
3963 left_direction = false;
3967 snap_to (drag_info.current_pointer_frame);
3970 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3974 if (drag_info.first_move) {
3980 trim_type = "Region start trim";
3983 trim_type = "Region end trim";
3986 trim_type = "Region content trim";
3990 begin_reversible_command (trim_type);
3992 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3993 (*i)->region().freeze ();
3995 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3997 arv->temporarily_hide_envelope ();
3999 Playlist * pl = (*i)->region().playlist();
4000 insert_result = motion_frozen_playlists.insert (pl);
4001 if (insert_result.second) {
4002 session->add_undo (pl->get_memento());
4007 if (left_direction) {
4008 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4010 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4015 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region().first_frame()/speed)) {
4018 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4019 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4025 if ((left_direction == true) && (drag_info.current_pointer_frame > (jack_nframes_t) (rv->region().last_frame()/speed))) {
4028 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4029 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4036 bool swap_direction = false;
4038 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4039 swap_direction = true;
4042 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4043 i != selection->regions.by_layer().end(); ++i)
4045 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4053 show_verbose_time_cursor((jack_nframes_t) (rv->region().position()/speed), 10);
4056 show_verbose_time_cursor((jack_nframes_t) (rv->region().last_frame()/speed), 10);
4059 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4063 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4064 drag_info.first_move = false;
4068 Editor::single_contents_trim (RegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4070 Region& region (rv.region());
4072 if (region.locked()) {
4076 jack_nframes_t new_bound;
4079 TimeAxisView* tvp = clicked_trackview;
4080 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4082 if (tv && tv->is_audio_track()) {
4083 speed = tv->get_diskstream()->speed();
4086 if (left_direction) {
4087 if (swap_direction) {
4088 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4090 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4093 if (swap_direction) {
4094 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4096 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4101 snap_to (new_bound);
4103 region.trim_start ((jack_nframes_t) (new_bound * speed), this);
4104 rv.region_changed (StartChanged);
4108 Editor::single_start_trim (RegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4110 Region& region (rv.region());
4112 if (region.locked()) {
4116 jack_nframes_t new_bound;
4119 TimeAxisView* tvp = clicked_trackview;
4120 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4122 if (tv && tv->is_audio_track()) {
4123 speed = tv->get_diskstream()->speed();
4126 if (left_direction) {
4127 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
4129 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
4133 snap_to (new_bound, (left_direction ? 0 : 1));
4136 region.trim_front ((jack_nframes_t) (new_bound * speed), this);
4138 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4142 Editor::single_end_trim (RegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
4144 Region& region (rv.region());
4146 if (region.locked()) {
4150 jack_nframes_t new_bound;
4153 TimeAxisView* tvp = clicked_trackview;
4154 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4156 if (tv && tv->is_audio_track()) {
4157 speed = tv->get_diskstream()->speed();
4160 if (left_direction) {
4161 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) - frame_delta;
4163 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) + frame_delta;
4167 snap_to (new_bound);
4169 region.trim_end ((jack_nframes_t) (new_bound * speed), this);
4170 rv.region_changed (LengthChanged);
4174 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4176 if (!drag_info.first_move) {
4177 trim_motion_callback (item, event);
4179 if (!clicked_regionview->get_selected()) {
4180 thaw_region_after_trim (*clicked_regionview);
4183 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4184 i != selection->regions.by_layer().end(); ++i)
4186 thaw_region_after_trim (**i);
4190 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4192 session->add_redo_no_execute ((*p)->get_memento());
4195 motion_frozen_playlists.clear ();
4197 commit_reversible_command();
4199 /* no mouse movement */
4205 Editor::point_trim (GdkEvent* event)
4207 RegionView* rv = clicked_regionview;
4208 jack_nframes_t new_bound = drag_info.current_pointer_frame;
4210 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4211 snap_to (new_bound);
4214 /* Choose action dependant on which button was pressed */
4215 switch (event->button.button) {
4217 trim_op = StartTrim;
4218 begin_reversible_command (_("Start point trim"));
4220 if (rv->get_selected()) {
4222 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4223 i != selection->regions.by_layer().end(); ++i)
4225 if (!(*i)->region().locked()) {
4226 session->add_undo ((*i)->region().playlist()->get_memento());
4227 (*i)->region().trim_front (new_bound, this);
4228 session->add_redo_no_execute ((*i)->region().playlist()->get_memento());
4234 if (!rv->region().locked()) {
4235 session->add_undo (rv->region().playlist()->get_memento());
4236 rv->region().trim_front (new_bound, this);
4237 session->add_redo_no_execute (rv->region().playlist()->get_memento());
4241 commit_reversible_command();
4246 begin_reversible_command (_("End point trim"));
4248 if (rv->get_selected()) {
4250 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4252 if (!(*i)->region().locked()) {
4253 session->add_undo ((*i)->region().playlist()->get_memento());
4254 (*i)->region().trim_end (new_bound, this);
4255 session->add_redo_no_execute ((*i)->region().playlist()->get_memento());
4261 if (!rv->region().locked()) {
4262 session->add_undo (rv->region().playlist()->get_memento());
4263 rv->region().trim_end (new_bound, this);
4264 session->add_redo_no_execute (rv->region().playlist()->get_memento());
4268 commit_reversible_command();
4277 Editor::thaw_region_after_trim (RegionView& rv)
4279 Region& region (rv.region());
4281 if (region.locked()) {
4285 region.thaw (_("trimmed region"));
4286 session->add_redo_no_execute (region.playlist()->get_memento());
4288 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4290 arv->unhide_envelope ();
4294 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4299 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4300 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4304 Location* location = find_location_from_marker (marker, is_start);
4305 location->set_hidden (true, this);
4310 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4316 drag_info.item = item;
4317 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4318 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4320 range_marker_op = op;
4322 if (!temp_location) {
4323 temp_location = new Location;
4327 case CreateRangeMarker:
4328 case CreateTransportMarker:
4330 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4331 drag_info.copy = true;
4333 drag_info.copy = false;
4335 start_grab (event, selector_cursor);
4339 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4344 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4346 jack_nframes_t start = 0;
4347 jack_nframes_t end = 0;
4348 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4350 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4351 snap_to (drag_info.current_pointer_frame);
4354 /* only alter selection if the current frame is
4355 different from the last frame position.
4358 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4360 switch (range_marker_op) {
4361 case CreateRangeMarker:
4362 case CreateTransportMarker:
4363 if (drag_info.first_move) {
4364 snap_to (drag_info.grab_frame);
4367 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4368 start = drag_info.current_pointer_frame;
4369 end = drag_info.grab_frame;
4371 end = drag_info.current_pointer_frame;
4372 start = drag_info.grab_frame;
4375 /* first drag: Either add to the selection
4376 or create a new selection.
4379 if (drag_info.first_move) {
4381 temp_location->set (start, end);
4385 update_marker_drag_item (temp_location);
4386 range_marker_drag_rect->show();
4387 range_marker_drag_rect->raise_to_top();
4393 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4394 start_canvas_autoscroll (1);
4398 temp_location->set (start, end);
4400 double x1 = frame_to_pixel (start);
4401 double x2 = frame_to_pixel (end);
4402 crect->property_x1() = x1;
4403 crect->property_x2() = x2;
4405 update_marker_drag_item (temp_location);
4408 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4409 drag_info.first_move = false;
4411 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4416 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4418 Location * newloc = 0;
4420 if (!drag_info.first_move) {
4421 drag_range_markerbar_op (item, event);
4423 switch (range_marker_op) {
4424 case CreateRangeMarker:
4425 begin_reversible_command (_("new range marker"));
4426 session->add_undo (session->locations()->get_memento());
4427 newloc = new Location(temp_location->start(), temp_location->end(), "unnamed", Location::IsRangeMarker);
4428 session->locations()->add (newloc, true);
4429 session->add_redo_no_execute (session->locations()->get_memento());
4430 commit_reversible_command ();
4432 range_bar_drag_rect->hide();
4433 range_marker_drag_rect->hide();
4436 case CreateTransportMarker:
4437 // popup menu to pick loop or punch
4438 new_transport_marker_context_menu (&event->button, item);
4443 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4445 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4447 jack_nframes_t start;
4450 start = session->locations()->first_mark_before (drag_info.grab_frame);
4451 end = session->locations()->first_mark_after (drag_info.grab_frame);
4453 if (end == max_frames) {
4454 end = session->current_end_frame ();
4458 start = session->current_start_frame ();
4461 switch (mouse_mode) {
4463 /* find the two markers on either side and then make the selection from it */
4464 cerr << "select between " << start << " .. " << end << endl;
4465 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4469 /* find the two markers on either side of the click and make the range out of it */
4470 selection->set (0, start, end);
4479 stop_canvas_autoscroll ();
4485 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4487 drag_info.item = item;
4488 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4489 drag_info.finished_callback = &Editor::end_mouse_zoom;
4491 start_grab (event, zoom_cursor);
4493 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4497 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4499 jack_nframes_t start;
4502 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4503 snap_to (drag_info.current_pointer_frame);
4505 if (drag_info.first_move) {
4506 snap_to (drag_info.grab_frame);
4510 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4512 /* base start and end on initial click position */
4513 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4514 start = drag_info.current_pointer_frame;
4515 end = drag_info.grab_frame;
4517 end = drag_info.current_pointer_frame;
4518 start = drag_info.grab_frame;
4523 if (drag_info.first_move) {
4525 zoom_rect->raise_to_top();
4528 reposition_zoom_rect(start, end);
4530 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4531 drag_info.first_move = false;
4533 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4538 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4540 if (!drag_info.first_move) {
4541 drag_mouse_zoom (item, event);
4543 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4544 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4546 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4549 temporal_zoom_to_frame (false, drag_info.grab_frame);
4551 temporal_zoom_step (false);
4552 center_screen (drag_info.grab_frame);
4560 Editor::reposition_zoom_rect (jack_nframes_t start, jack_nframes_t end)
4562 double x1 = frame_to_pixel (start);
4563 double x2 = frame_to_pixel (end);
4564 double y2 = canvas_height - 2;
4566 zoom_rect->property_x1() = x1;
4567 zoom_rect->property_y1() = 1.0;
4568 zoom_rect->property_x2() = x2;
4569 zoom_rect->property_y2() = y2;
4573 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4575 drag_info.item = item;
4576 drag_info.motion_callback = &Editor::drag_rubberband_select;
4577 drag_info.finished_callback = &Editor::end_rubberband_select;
4579 start_grab (event, cross_hair_cursor);
4581 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4585 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4587 jack_nframes_t start;
4592 /* use a bigger drag threshold than the default */
4594 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4598 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4599 // snap_to (drag_info.current_pointer_frame);
4601 // if (drag_info.first_move) {
4602 // snap_to (drag_info.grab_frame);
4607 /* base start and end on initial click position */
4608 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4609 start = drag_info.current_pointer_frame;
4610 end = drag_info.grab_frame;
4612 end = drag_info.current_pointer_frame;
4613 start = drag_info.grab_frame;
4616 if (drag_info.current_pointer_y < drag_info.grab_y) {
4617 y1 = drag_info.current_pointer_y;
4618 y2 = drag_info.grab_y;
4621 y2 = drag_info.current_pointer_y;
4622 y1 = drag_info.grab_y;
4626 if (start != end || y1 != y2) {
4628 double x1 = frame_to_pixel (start);
4629 double x2 = frame_to_pixel (end);
4631 rubberband_rect->property_x1() = x1;
4632 rubberband_rect->property_y1() = y1;
4633 rubberband_rect->property_x2() = x2;
4634 rubberband_rect->property_y2() = y2;
4636 rubberband_rect->show();
4637 rubberband_rect->raise_to_top();
4639 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4640 drag_info.first_move = false;
4642 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4647 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4649 if (!drag_info.first_move) {
4651 drag_rubberband_select (item, event);
4654 if (drag_info.current_pointer_y < drag_info.grab_y) {
4655 y1 = drag_info.current_pointer_y;
4656 y2 = drag_info.grab_y;
4659 y2 = drag_info.current_pointer_y;
4660 y1 = drag_info.grab_y;
4664 Selection::Operation op = Keyboard::selection_type (event->button.state);
4667 begin_reversible_command (_("select regions"));
4669 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4670 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4672 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4676 commit_reversible_command ();
4680 selection->clear_regions();
4681 selection->clear_points ();
4682 selection->clear_lines ();
4685 rubberband_rect->hide();
4690 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4692 using namespace Gtkmm2ext;
4694 ArdourPrompter prompter (false);
4696 prompter.set_prompt (_("Name for region:"));
4697 prompter.set_initial_text (clicked_regionview->region().name());
4698 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4699 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4700 prompter.show_all ();
4701 switch (prompter.run ()) {
4702 case Gtk::RESPONSE_ACCEPT:
4704 prompter.get_result(str);
4706 clicked_regionview->region().set_name (str);
4714 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4716 drag_info.item = item;
4717 drag_info.motion_callback = &Editor::time_fx_motion;
4718 drag_info.finished_callback = &Editor::end_time_fx;
4722 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4726 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4728 RegionView* rv = clicked_regionview;
4730 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4731 snap_to (drag_info.current_pointer_frame);
4734 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4738 if (drag_info.current_pointer_frame > rv->region().position()) {
4739 rv->get_time_axis_view().show_timestretch (rv->region().position(), drag_info.current_pointer_frame);
4742 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4743 drag_info.first_move = false;
4745 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4749 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4751 clicked_regionview->get_time_axis_view().hide_timestretch ();
4753 if (drag_info.first_move) {
4757 jack_nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region().position();
4758 float percentage = (float) ((double) newlen - (double) clicked_regionview->region().length()) / ((double) newlen) * 100.0f;
4760 begin_reversible_command (_("timestretch"));
4762 if (run_timestretch (selection->regions, percentage) == 0) {
4763 session->commit_reversible_command ();
4768 Editor::mouse_brush_insert_region (RegionView* rv, jack_nframes_t pos)
4770 /* no brushing without a useful snap setting */
4773 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4776 switch (snap_mode) {
4778 return; /* can't work because it allows region to be placed anywhere */
4783 switch (snap_type) {
4786 case SnapToEditCursor:
4793 /* don't brush a copy over the original */
4795 if (pos == rv->region().position()) {
4799 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4801 if (atv == 0 || !atv->is_audio_track()) {
4805 Playlist* playlist = atv->playlist();
4806 double speed = atv->get_diskstream()->speed();
4808 session->add_undo (playlist->get_memento());
4809 playlist->add_region (*(new AudioRegion (arv->audio_region())), (jack_nframes_t) (pos * speed));
4810 session->add_redo_no_execute (playlist->get_memento());
4812 // playlist is frozen, so we have to update manually
4814 playlist->StateChanged (Change (~0)); /* EMIT SIGNAL */
4818 Editor::track_height_step_timeout ()
4821 struct timeval delta;
4823 gettimeofday (&now, 0);
4824 timersub (&now, &last_track_height_step_timestamp, &delta);
4826 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4827 current_stepping_trackview = 0;