2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <pbd/error.h>
30 #include <gtkmm2ext/utils.h>
31 #include <pbd/memento_command.h>
33 #include "ardour_ui.h"
35 #include "time_axis_view.h"
36 #include "audio_time_axis.h"
37 #include "audio_region_view.h"
39 #include "streamview.h"
40 #include "region_gain_line.h"
41 #include "automation_time_axis.h"
44 #include "selection.h"
47 #include "rgb_macros.h"
49 #include <ardour/types.h>
50 #include <ardour/route.h>
51 #include <ardour/audio_track.h>
52 #include <ardour/audio_diskstream.h>
53 #include <ardour/playlist.h>
54 #include <ardour/audioplaylist.h>
55 #include <ardour/audioregion.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 /* XXX make tying track/region selection optional */
320 c1 = set_selected_track_from_click (op, true);
321 c2 = set_selected_regionview_from_click (press, op, true);
325 case RegionViewNameHighlight:
327 /* XXX make tying track/region selection optional */
328 c1 = set_selected_track_from_click (op, true);
329 c2 = set_selected_regionview_from_click (press, op, true);
333 case GainAutomationControlPointItem:
334 case PanAutomationControlPointItem:
335 case RedirectAutomationControlPointItem:
336 /* XXX make tying track/region selection optional */
337 c1 = set_selected_track_from_click (op, true);
338 c2 = set_selected_control_point_from_click (op, false);
343 commit = set_selected_track_from_click (op, true);
346 case AutomationTrackItem:
347 commit = set_selected_track_from_click (op, true);
354 #define SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
355 #ifdef SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
356 /* in range mode, button 1/2/3 press potentially selects a track */
358 if (mouse_mode == MouseRange &&
359 event->type == GDK_BUTTON_PRESS &&
360 event->button.button <= 3) {
365 case AutomationTrackItem:
366 commit = set_selected_track_from_click (op, true);
375 commit_reversible_command ();
380 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
382 nframes_t where = event_frame (event, 0, 0);
384 track_canvas.grab_focus();
386 if (session && session->actively_recording()) {
390 button_selection (item, event, item_type);
392 if (drag_info.item == 0 &&
393 (Keyboard::is_delete_event (&event->button) ||
394 Keyboard::is_context_menu_event (&event->button) ||
395 Keyboard::is_edit_event (&event->button))) {
397 /* handled by button release */
401 switch (event->button.button) {
404 if (event->type == GDK_BUTTON_PRESS) {
406 if (drag_info.item) {
407 drag_info.item->ungrab (event->button.time);
410 /* single mouse clicks on any of these item types operate
411 independent of mouse mode, mostly because they are
412 not on the main track canvas or because we want
418 case PlayheadCursorItem:
419 start_cursor_grab (item, event);
423 if (Keyboard::modifier_state_equals (event->button.state,
424 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
425 hide_marker (item, event);
427 start_marker_grab (item, event);
431 case TempoMarkerItem:
432 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
433 start_tempo_marker_copy_grab (item, event);
435 start_tempo_marker_grab (item, event);
439 case MeterMarkerItem:
440 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
441 start_meter_marker_copy_grab (item, event);
443 start_meter_marker_grab (item, event);
453 case RangeMarkerBarItem:
454 start_range_markerbar_op (item, event, CreateRangeMarker);
458 case TransportMarkerBarItem:
459 start_range_markerbar_op (item, event, CreateTransportMarker);
468 switch (mouse_mode) {
471 case StartSelectionTrimItem:
472 start_selection_op (item, event, SelectionStartTrim);
475 case EndSelectionTrimItem:
476 start_selection_op (item, event, SelectionEndTrim);
480 if (Keyboard::modifier_state_contains
481 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
482 // contains and not equals because I can't use alt as a modifier alone.
483 start_selection_grab (item, event);
484 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
485 /* grab selection for moving */
486 start_selection_op (item, event, SelectionMove);
489 /* this was debated, but decided the more common action was to
490 make a new selection */
491 start_selection_op (item, event, CreateSelection);
496 start_selection_op (item, event, CreateSelection);
502 if (Keyboard::modifier_state_contains (event->button.state,
503 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt))
504 && event->type == GDK_BUTTON_PRESS) {
506 start_rubberband_select (item, event);
508 } else if (event->type == GDK_BUTTON_PRESS) {
511 case FadeInHandleItem:
512 start_fade_in_grab (item, event);
515 case FadeOutHandleItem:
516 start_fade_out_grab (item, event);
520 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
521 start_region_copy_grab (item, event);
522 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
523 start_region_brush_grab (item, event);
525 start_region_grab (item, event);
529 case RegionViewNameHighlight:
530 start_trim (item, event);
535 /* rename happens on edit clicks */
536 start_trim (clicked_regionview->get_name_highlight(), event);
540 case GainAutomationControlPointItem:
541 case PanAutomationControlPointItem:
542 case RedirectAutomationControlPointItem:
543 start_control_point_grab (item, event);
547 case GainAutomationLineItem:
548 case PanAutomationLineItem:
549 case RedirectAutomationLineItem:
550 start_line_grab_from_line (item, event);
555 case AutomationTrackItem:
556 start_rubberband_select (item, event);
559 /* <CMT Additions> */
560 case ImageFrameHandleStartItem:
561 imageframe_start_handle_op(item, event) ;
564 case ImageFrameHandleEndItem:
565 imageframe_end_handle_op(item, event) ;
568 case MarkerViewHandleStartItem:
569 markerview_item_start_handle_op(item, event) ;
572 case MarkerViewHandleEndItem:
573 markerview_item_end_handle_op(item, event) ;
576 /* </CMT Additions> */
578 /* <CMT Additions> */
580 start_markerview_grab(item, event) ;
583 start_imageframe_grab(item, event) ;
585 /* </CMT Additions> */
601 // start_line_grab_from_regionview (item, event);
604 case GainControlPointItem:
605 start_control_point_grab (item, event);
609 start_line_grab_from_line (item, event);
612 case GainAutomationControlPointItem:
613 case PanAutomationControlPointItem:
614 case RedirectAutomationControlPointItem:
615 start_control_point_grab (item, event);
626 case GainAutomationControlPointItem:
627 case PanAutomationControlPointItem:
628 case RedirectAutomationControlPointItem:
629 start_control_point_grab (item, event);
632 case GainAutomationLineItem:
633 case PanAutomationLineItem:
634 case RedirectAutomationLineItem:
635 start_line_grab_from_line (item, event);
639 // XXX need automation mode to identify which
641 // start_line_grab_from_regionview (item, event);
651 if (event->type == GDK_BUTTON_PRESS) {
652 start_mouse_zoom (item, event);
659 if (item_type == RegionItem) {
660 start_time_fx (item, event);
665 /* handled in release */
674 switch (mouse_mode) {
676 if (event->type == GDK_BUTTON_PRESS) {
679 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
680 start_region_copy_grab (item, event);
682 start_region_grab (item, event);
686 case GainAutomationControlPointItem:
687 case PanAutomationControlPointItem:
688 case RedirectAutomationControlPointItem:
689 start_control_point_grab (item, event);
700 case RegionViewNameHighlight:
701 start_trim (item, event);
706 start_trim (clicked_regionview->get_name_highlight(), event);
717 if (event->type == GDK_BUTTON_PRESS) {
718 /* relax till release */
725 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
726 temporal_zoom_session();
728 temporal_zoom_to_frame (true, event_frame(event));
743 switch (mouse_mode) {
745 //temporal_zoom_to_frame (true, where);
746 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
747 temporal_zoom_to_frame (true, where);
750 temporal_zoom_step (true);
755 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
756 scroll_backward (0.6f);
759 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
760 scroll_tracks_up_line ();
762 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
763 if (clicked_trackview) {
764 if (!current_stepping_trackview) {
765 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
766 current_stepping_trackview = clicked_trackview;
768 gettimeofday (&last_track_height_step_timestamp, 0);
769 current_stepping_trackview->step_height (true);
772 else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
773 temporal_zoom_to_frame (true, where);
780 switch (mouse_mode) {
782 // temporal_zoom_to_frame (false, where);
783 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
784 temporal_zoom_to_frame (false, where);
787 temporal_zoom_step (false);
792 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
793 scroll_forward (0.6f);
796 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
797 scroll_tracks_down_line ();
799 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
800 if (clicked_trackview) {
801 if (!current_stepping_trackview) {
802 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
803 current_stepping_trackview = clicked_trackview;
805 gettimeofday (&last_track_height_step_timestamp, 0);
806 current_stepping_trackview->step_height (false);
808 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
809 temporal_zoom_to_frame (false, where);
824 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
826 nframes_t where = event_frame (event, 0, 0);
828 /* no action if we're recording */
830 if (session && session->actively_recording()) {
834 /* first, see if we're finishing a drag ... */
836 if (drag_info.item) {
837 if (end_grab (item, event)) {
838 /* grab dragged, so do nothing else */
843 button_selection (item, event, item_type);
845 /* edit events get handled here */
847 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
853 case TempoMarkerItem:
854 edit_tempo_marker (item);
857 case MeterMarkerItem:
858 edit_meter_marker (item);
862 if (clicked_regionview->name_active()) {
863 return mouse_rename_region (item, event);
873 /* context menu events get handled here */
875 if (Keyboard::is_context_menu_event (&event->button)) {
877 if (drag_info.item == 0) {
879 /* no matter which button pops up the context menu, tell the menu
880 widget to use button 1 to drive menu selection.
885 case FadeInHandleItem:
887 case FadeOutHandleItem:
888 popup_fade_context_menu (1, event->button.time, item, item_type);
892 popup_track_context_menu (1, event->button.time, item_type, false, where);
896 case RegionViewNameHighlight:
898 popup_track_context_menu (1, event->button.time, item_type, false, where);
902 popup_track_context_menu (1, event->button.time, item_type, true, where);
905 case AutomationTrackItem:
906 popup_track_context_menu (1, event->button.time, item_type, false, where);
910 case RangeMarkerBarItem:
911 case TransportMarkerBarItem:
914 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
918 marker_context_menu (&event->button, item);
921 case TempoMarkerItem:
922 tm_marker_context_menu (&event->button, item);
925 case MeterMarkerItem:
926 tm_marker_context_menu (&event->button, item);
929 case CrossfadeViewItem:
930 popup_track_context_menu (1, event->button.time, item_type, false, where);
933 /* <CMT Additions> */
935 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
937 case ImageFrameTimeAxisItem:
938 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
941 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
943 case MarkerTimeAxisItem:
944 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
946 /* <CMT Additions> */
957 /* delete events get handled here */
959 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
962 case TempoMarkerItem:
963 remove_tempo_marker (item);
966 case MeterMarkerItem:
967 remove_meter_marker (item);
971 remove_marker (*item, event);
975 if (mouse_mode == MouseObject) {
976 remove_clicked_region ();
980 case GainControlPointItem:
981 if (mouse_mode == MouseGain) {
982 remove_gain_control_point (item, event);
986 case GainAutomationControlPointItem:
987 case PanAutomationControlPointItem:
988 case RedirectAutomationControlPointItem:
989 remove_control_point (item, event);
998 switch (event->button.button) {
1001 switch (item_type) {
1002 /* see comments in button_press_handler */
1003 case EditCursorItem:
1004 case PlayheadCursorItem:
1007 case GainAutomationLineItem:
1008 case PanAutomationLineItem:
1009 case RedirectAutomationLineItem:
1010 case StartSelectionTrimItem:
1011 case EndSelectionTrimItem:
1015 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1016 snap_to (where, 0, true);
1018 mouse_add_new_marker (where);
1022 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1025 mouse_add_new_tempo_event (where);
1029 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1037 switch (mouse_mode) {
1039 switch (item_type) {
1040 case AutomationTrackItem:
1041 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1055 // Gain only makes sense for audio regions
1056 if ( ! dynamic_cast<AudioRegionView*>(clicked_regionview))
1059 switch (item_type) {
1061 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1065 case AutomationTrackItem:
1066 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1067 add_automation_event (item, event, where, event->button.y);
1076 switch (item_type) {
1078 audition_selected_region ();
1095 switch (mouse_mode) {
1098 switch (item_type) {
1100 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1102 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1105 // Button2 click is unused
1118 // x_style_paste (where, 1.0);
1138 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1144 switch (item_type) {
1145 case GainControlPointItem:
1146 if (mouse_mode == MouseGain) {
1147 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1148 cp->set_visible (true);
1152 at_y = cp->get_y ();
1153 cp->item->i2w (at_x, at_y);
1157 fraction = 1.0 - (cp->get_y() / cp->line.height());
1159 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1160 show_verbose_canvas_cursor ();
1162 if (is_drawable()) {
1163 track_canvas.get_window()->set_cursor (*fader_cursor);
1168 case GainAutomationControlPointItem:
1169 case PanAutomationControlPointItem:
1170 case RedirectAutomationControlPointItem:
1171 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1172 cp->set_visible (true);
1176 at_y = cp->get_y ();
1177 cp->item->i2w (at_x, at_y);
1181 fraction = 1.0 - (cp->get_y() / cp->line.height());
1183 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1184 show_verbose_canvas_cursor ();
1186 if (is_drawable()) {
1187 track_canvas.get_window()->set_cursor (*fader_cursor);
1192 if (mouse_mode == MouseGain) {
1193 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1195 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1196 if (is_drawable()) {
1197 track_canvas.get_window()->set_cursor (*fader_cursor);
1202 case GainAutomationLineItem:
1203 case RedirectAutomationLineItem:
1204 case PanAutomationLineItem:
1206 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1208 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1210 if (is_drawable()) {
1211 track_canvas.get_window()->set_cursor (*fader_cursor);
1215 case RegionViewNameHighlight:
1216 if (is_drawable() && mouse_mode == MouseObject) {
1217 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1221 case StartSelectionTrimItem:
1222 case EndSelectionTrimItem:
1223 /* <CMT Additions> */
1224 case ImageFrameHandleStartItem:
1225 case ImageFrameHandleEndItem:
1226 case MarkerViewHandleStartItem:
1227 case MarkerViewHandleEndItem:
1228 /* </CMT Additions> */
1230 if (is_drawable()) {
1231 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1235 case EditCursorItem:
1236 case PlayheadCursorItem:
1237 if (is_drawable()) {
1238 track_canvas.get_window()->set_cursor (*grabber_cursor);
1242 case RegionViewName:
1244 /* when the name is not an active item, the entire name highlight is for trimming */
1246 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1247 if (mouse_mode == MouseObject && is_drawable()) {
1248 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1254 case AutomationTrackItem:
1255 if (is_drawable()) {
1256 Gdk::Cursor *cursor;
1257 switch (mouse_mode) {
1259 cursor = selector_cursor;
1262 cursor = zoom_cursor;
1265 cursor = cross_hair_cursor;
1269 track_canvas.get_window()->set_cursor (*cursor);
1271 AutomationTimeAxisView* atv;
1272 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1273 clear_entered_track = false;
1274 set_entered_track (atv);
1280 case RangeMarkerBarItem:
1281 case TransportMarkerBarItem:
1284 if (is_drawable()) {
1285 time_canvas.get_window()->set_cursor (*timebar_cursor);
1290 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1293 marker->set_color_rgba (color_map[cEnteredMarker]);
1295 case MeterMarkerItem:
1296 case TempoMarkerItem:
1297 if (is_drawable()) {
1298 time_canvas.get_window()->set_cursor (*timebar_cursor);
1301 case FadeInHandleItem:
1302 case FadeOutHandleItem:
1303 if (mouse_mode == MouseObject) {
1304 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1306 rect->property_fill_color_rgba() = 0;
1307 rect->property_outline_pixels() = 1;
1316 /* second pass to handle entered track status in a comprehensible way.
1319 switch (item_type) {
1321 case GainAutomationLineItem:
1322 case RedirectAutomationLineItem:
1323 case PanAutomationLineItem:
1324 case GainControlPointItem:
1325 case GainAutomationControlPointItem:
1326 case PanAutomationControlPointItem:
1327 case RedirectAutomationControlPointItem:
1328 /* these do not affect the current entered track state */
1329 clear_entered_track = false;
1332 case AutomationTrackItem:
1333 /* handled above already */
1337 set_entered_track (0);
1345 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1354 switch (item_type) {
1355 case GainControlPointItem:
1356 case GainAutomationControlPointItem:
1357 case PanAutomationControlPointItem:
1358 case RedirectAutomationControlPointItem:
1359 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1360 if (cp->line.npoints() > 1) {
1361 if (!cp->selected) {
1362 cp->set_visible (false);
1366 if (is_drawable()) {
1367 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1370 hide_verbose_canvas_cursor ();
1373 case RegionViewNameHighlight:
1374 case StartSelectionTrimItem:
1375 case EndSelectionTrimItem:
1376 case EditCursorItem:
1377 case PlayheadCursorItem:
1378 /* <CMT Additions> */
1379 case ImageFrameHandleStartItem:
1380 case ImageFrameHandleEndItem:
1381 case MarkerViewHandleStartItem:
1382 case MarkerViewHandleEndItem:
1383 /* </CMT Additions> */
1384 if (is_drawable()) {
1385 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1390 case GainAutomationLineItem:
1391 case RedirectAutomationLineItem:
1392 case PanAutomationLineItem:
1393 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1395 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1397 line->property_fill_color_rgba() = al->get_line_color();
1399 if (is_drawable()) {
1400 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1404 case RegionViewName:
1405 /* see enter_handler() for notes */
1406 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1407 if (is_drawable() && mouse_mode == MouseObject) {
1408 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1413 case RangeMarkerBarItem:
1414 case TransportMarkerBarItem:
1418 if (is_drawable()) {
1419 time_canvas.get_window()->set_cursor (*timebar_cursor);
1424 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1427 loc = find_location_from_marker (marker, is_start);
1428 if (loc) location_flags_changed (loc, this);
1430 case MeterMarkerItem:
1431 case TempoMarkerItem:
1433 if (is_drawable()) {
1434 time_canvas.get_window()->set_cursor (*timebar_cursor);
1439 case FadeInHandleItem:
1440 case FadeOutHandleItem:
1441 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1443 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1445 rect->property_fill_color_rgba() = rv->get_fill_color();
1446 rect->property_outline_pixels() = 0;
1451 case AutomationTrackItem:
1452 if (is_drawable()) {
1453 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1454 clear_entered_track = true;
1455 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1467 Editor::left_automation_track ()
1469 if (clear_entered_track) {
1470 set_entered_track (0);
1471 clear_entered_track = false;
1477 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1481 /* We call this so that MOTION_NOTIFY events continue to be
1482 delivered to the canvas. We need to do this because we set
1483 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1484 the density of the events, at the expense of a round-trip
1485 to the server. Given that this will mostly occur on cases
1486 where DISPLAY = :0.0, and given the cost of what the motion
1487 event might do, its a good tradeoff.
1490 track_canvas.get_pointer (x, y);
1492 if (current_stepping_trackview) {
1493 /* don't keep the persistent stepped trackview if the mouse moves */
1494 current_stepping_trackview = 0;
1495 step_timeout.disconnect ();
1498 if (session && session->actively_recording()) {
1499 /* Sorry. no dragging stuff around while we record */
1503 drag_info.item_type = item_type;
1504 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1505 &drag_info.current_pointer_y);
1507 if (!from_autoscroll && drag_info.item) {
1508 /* item != 0 is the best test i can think of for dragging.
1510 if (!drag_info.move_threshold_passed) {
1512 drag_info.move_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1514 // and change the initial grab loc/frame if this drag info wants us to
1516 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1517 drag_info.grab_frame = drag_info.current_pointer_frame;
1518 drag_info.grab_x = drag_info.current_pointer_x;
1519 drag_info.grab_y = drag_info.current_pointer_y;
1520 drag_info.last_pointer_frame = drag_info.grab_frame;
1521 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1526 switch (item_type) {
1527 case PlayheadCursorItem:
1528 case EditCursorItem:
1530 case GainControlPointItem:
1531 case RedirectAutomationControlPointItem:
1532 case GainAutomationControlPointItem:
1533 case PanAutomationControlPointItem:
1534 case TempoMarkerItem:
1535 case MeterMarkerItem:
1536 case RegionViewNameHighlight:
1537 case StartSelectionTrimItem:
1538 case EndSelectionTrimItem:
1541 case RedirectAutomationLineItem:
1542 case GainAutomationLineItem:
1543 case PanAutomationLineItem:
1544 case FadeInHandleItem:
1545 case FadeOutHandleItem:
1546 /* <CMT Additions> */
1547 case ImageFrameHandleStartItem:
1548 case ImageFrameHandleEndItem:
1549 case MarkerViewHandleStartItem:
1550 case MarkerViewHandleEndItem:
1551 /* </CMT Additions> */
1552 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1553 (event->motion.state & Gdk::BUTTON2_MASK))) {
1554 if (!from_autoscroll) {
1555 maybe_autoscroll (event);
1557 (this->*(drag_info.motion_callback)) (item, event);
1566 switch (mouse_mode) {
1571 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1572 (event->motion.state & GDK_BUTTON2_MASK))) {
1573 if (!from_autoscroll) {
1574 maybe_autoscroll (event);
1576 (this->*(drag_info.motion_callback)) (item, event);
1587 track_canvas_motion (event);
1588 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1596 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1598 if (drag_info.item == 0) {
1599 fatal << _("programming error: start_grab called without drag item") << endmsg;
1605 cursor = grabber_cursor;
1608 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1610 if (event->button.button == 2) {
1611 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1612 drag_info.y_constrained = true;
1613 drag_info.x_constrained = false;
1615 drag_info.y_constrained = false;
1616 drag_info.x_constrained = true;
1619 drag_info.x_constrained = false;
1620 drag_info.y_constrained = false;
1623 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1624 drag_info.last_pointer_frame = drag_info.grab_frame;
1625 drag_info.current_pointer_frame = drag_info.grab_frame;
1626 drag_info.current_pointer_x = drag_info.grab_x;
1627 drag_info.current_pointer_y = drag_info.grab_y;
1628 drag_info.cumulative_x_drag = 0;
1629 drag_info.cumulative_y_drag = 0;
1630 drag_info.first_move = true;
1631 drag_info.move_threshold_passed = false;
1632 drag_info.want_move_threshold = false;
1633 drag_info.pointer_frame_offset = 0;
1634 drag_info.brushing = false;
1635 drag_info.copied_location = 0;
1637 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1639 event->button.time);
1641 if (session && session->transport_rolling()) {
1642 drag_info.was_rolling = true;
1644 drag_info.was_rolling = false;
1647 switch (snap_type) {
1648 case SnapToRegionStart:
1649 case SnapToRegionEnd:
1650 case SnapToRegionSync:
1651 case SnapToRegionBoundary:
1652 build_region_boundary_cache ();
1660 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1662 drag_info.item->ungrab (0);
1663 drag_info.item = new_item;
1666 cursor = grabber_cursor;
1669 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1673 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1675 bool did_drag = false;
1677 stop_canvas_autoscroll ();
1679 if (drag_info.item == 0) {
1683 drag_info.item->ungrab (event->button.time);
1685 if (drag_info.finished_callback) {
1686 (this->*(drag_info.finished_callback)) (item, event);
1689 did_drag = !drag_info.first_move;
1691 hide_verbose_canvas_cursor();
1694 drag_info.copy = false;
1695 drag_info.motion_callback = 0;
1696 drag_info.finished_callback = 0;
1697 drag_info.last_trackview = 0;
1698 drag_info.last_frame_position = 0;
1699 drag_info.grab_frame = 0;
1700 drag_info.last_pointer_frame = 0;
1701 drag_info.current_pointer_frame = 0;
1702 drag_info.brushing = false;
1704 if (drag_info.copied_location) {
1705 delete drag_info.copied_location;
1706 drag_info.copied_location = 0;
1713 Editor::set_edit_cursor (GdkEvent* event)
1715 nframes_t pointer_frame = event_frame (event);
1717 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1718 if (snap_type != SnapToEditCursor) {
1719 snap_to (pointer_frame);
1723 edit_cursor->set_position (pointer_frame);
1724 edit_cursor_clock.set (pointer_frame);
1728 Editor::set_playhead_cursor (GdkEvent* event)
1730 nframes_t pointer_frame = event_frame (event);
1732 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1733 snap_to (pointer_frame);
1737 session->request_locate (pointer_frame, session->transport_rolling());
1742 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1744 drag_info.item = item;
1745 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1746 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1750 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1751 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1755 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1757 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1761 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1763 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1765 nframes_t fade_length;
1767 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1768 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1774 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1778 if (pos < (arv->region()->position() + 64)) {
1779 fade_length = 64; // this should be a minimum defined somewhere
1780 } else if (pos > arv->region()->last_frame()) {
1781 fade_length = arv->region()->length();
1783 fade_length = pos - arv->region()->position();
1786 arv->reset_fade_in_shape_width (fade_length);
1788 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1790 drag_info.first_move = false;
1794 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1796 if (drag_info.first_move) return;
1798 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1800 nframes_t fade_length;
1802 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1803 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1809 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1813 if (pos < (arv->region()->position() + 64)) {
1814 fade_length = 64; // this should be a minimum defined somewhere
1816 else if (pos > arv->region()->last_frame()) {
1817 fade_length = arv->region()->length();
1820 fade_length = pos - arv->region()->position();
1823 begin_reversible_command (_("change fade in length"));
1824 AutomationList& alist = arv->audio_region()->fade_in();
1825 XMLNode &before = alist.get_state();
1827 arv->audio_region()->set_fade_in_length (fade_length);
1829 XMLNode &after = alist.get_state();
1830 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1831 commit_reversible_command ();
1832 fade_in_drag_motion_callback (item, event);
1836 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1838 drag_info.item = item;
1839 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1840 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1844 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1845 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1849 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1851 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1855 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1857 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1859 nframes_t fade_length;
1861 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1862 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1868 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1872 if (pos > (arv->region()->last_frame() - 64)) {
1873 fade_length = 64; // this should really be a minimum fade defined somewhere
1875 else if (pos < arv->region()->position()) {
1876 fade_length = arv->region()->length();
1879 fade_length = arv->region()->last_frame() - pos;
1882 arv->reset_fade_out_shape_width (fade_length);
1884 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1886 drag_info.first_move = false;
1890 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1892 if (drag_info.first_move) return;
1894 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1896 nframes_t fade_length;
1898 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1899 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1905 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1909 if (pos > (arv->region()->last_frame() - 64)) {
1910 fade_length = 64; // this should really be a minimum fade defined somewhere
1912 else if (pos < arv->region()->position()) {
1913 fade_length = arv->region()->length();
1916 fade_length = arv->region()->last_frame() - pos;
1919 begin_reversible_command (_("change fade out length"));
1920 AutomationList& alist = arv->audio_region()->fade_out();
1921 XMLNode &before = alist.get_state();
1923 arv->audio_region()->set_fade_out_length (fade_length);
1925 XMLNode &after = alist.get_state();
1926 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1927 commit_reversible_command ();
1929 fade_out_drag_motion_callback (item, event);
1933 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1935 drag_info.item = item;
1936 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1937 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1941 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1942 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1946 Cursor* cursor = (Cursor *) drag_info.data;
1948 if (cursor == playhead_cursor) {
1949 _dragging_playhead = true;
1951 if (session && drag_info.was_rolling) {
1952 session->request_stop ();
1956 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1958 show_verbose_time_cursor (cursor->current_frame, 10);
1962 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1964 Cursor* cursor = (Cursor *) drag_info.data;
1965 nframes_t adjusted_frame;
1967 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1968 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1974 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1975 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1976 snap_to (adjusted_frame);
1980 if (adjusted_frame == drag_info.last_pointer_frame) return;
1982 cursor->set_position (adjusted_frame);
1984 if (cursor == edit_cursor) {
1985 edit_cursor_clock.set (cursor->current_frame);
1987 UpdateAllTransportClocks (cursor->current_frame);
1990 show_verbose_time_cursor (cursor->current_frame, 10);
1992 drag_info.last_pointer_frame = adjusted_frame;
1993 drag_info.first_move = false;
1997 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1999 if (drag_info.first_move) return;
2001 cursor_drag_motion_callback (item, event);
2003 _dragging_playhead = false;
2005 if (item == &playhead_cursor->canvas_item) {
2007 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2009 } else if (item == &edit_cursor->canvas_item) {
2010 edit_cursor->set_position (edit_cursor->current_frame);
2011 edit_cursor_clock.set (edit_cursor->current_frame);
2016 Editor::update_marker_drag_item (Location *location)
2018 double x1 = frame_to_pixel (location->start());
2019 double x2 = frame_to_pixel (location->end());
2021 if (location->is_mark()) {
2022 marker_drag_line_points.front().set_x(x1);
2023 marker_drag_line_points.back().set_x(x1);
2024 marker_drag_line->property_points() = marker_drag_line_points;
2027 range_marker_drag_rect->property_x1() = x1;
2028 range_marker_drag_rect->property_x2() = x2;
2033 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2037 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2038 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2044 Location *location = find_location_from_marker (marker, is_start);
2046 drag_info.item = item;
2047 drag_info.data = marker;
2048 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2049 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2053 drag_info.copied_location = new Location (*location);
2054 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2056 update_marker_drag_item (location);
2058 if (location->is_mark()) {
2059 marker_drag_line->show();
2060 marker_drag_line->raise_to_top();
2063 range_marker_drag_rect->show();
2064 range_marker_drag_rect->raise_to_top();
2067 if (is_start) show_verbose_time_cursor (location->start(), 10);
2068 else show_verbose_time_cursor (location->end(), 10);
2072 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2075 Marker* marker = (Marker *) drag_info.data;
2076 Location *real_location;
2077 Location *copy_location;
2079 bool move_both = false;
2083 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2084 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2090 nframes_t next = newframe;
2092 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2093 snap_to (newframe, 0, true);
2096 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2100 /* call this to find out if its the start or end */
2102 real_location = find_location_from_marker (marker, is_start);
2104 /* use the copy that we're "dragging" around */
2106 copy_location = drag_info.copied_location;
2108 f_delta = copy_location->end() - copy_location->start();
2110 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2114 if (copy_location->is_mark()) {
2117 copy_location->set_start (newframe);
2121 if (is_start) { // start-of-range marker
2124 copy_location->set_start (newframe);
2125 copy_location->set_end (newframe + f_delta);
2126 } else if (newframe < copy_location->end()) {
2127 copy_location->set_start (newframe);
2129 snap_to (next, 1, true);
2130 copy_location->set_end (next);
2131 copy_location->set_start (newframe);
2134 } else { // end marker
2137 copy_location->set_end (newframe);
2138 copy_location->set_start (newframe - f_delta);
2139 } else if (newframe > copy_location->start()) {
2140 copy_location->set_end (newframe);
2142 } else if (newframe > 0) {
2143 snap_to (next, -1, true);
2144 copy_location->set_start (next);
2145 copy_location->set_end (newframe);
2150 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2151 drag_info.first_move = false;
2153 update_marker_drag_item (copy_location);
2155 LocationMarkers* lm = find_location_markers (real_location);
2156 lm->set_position (copy_location->start(), copy_location->end());
2158 show_verbose_time_cursor (newframe, 10);
2162 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2164 if (drag_info.first_move) {
2165 marker_drag_motion_callback (item, event);
2169 Marker* marker = (Marker *) drag_info.data;
2173 begin_reversible_command ( _("move marker") );
2174 XMLNode &before = session->locations()->get_state();
2176 Location * location = find_location_from_marker (marker, is_start);
2179 if (location->is_mark()) {
2180 location->set_start (drag_info.copied_location->start());
2182 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2186 XMLNode &after = session->locations()->get_state();
2187 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2188 commit_reversible_command ();
2190 marker_drag_line->hide();
2191 range_marker_drag_rect->hide();
2195 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2198 MeterMarker* meter_marker;
2200 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2201 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2205 meter_marker = dynamic_cast<MeterMarker*> (marker);
2207 MetricSection& section (meter_marker->meter());
2209 if (!section.movable()) {
2213 drag_info.item = item;
2214 drag_info.data = marker;
2215 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2216 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2220 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2222 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2226 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2229 MeterMarker* meter_marker;
2231 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2232 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2236 meter_marker = dynamic_cast<MeterMarker*> (marker);
2238 // create a dummy marker for visual representation of moving the copy.
2239 // The actual copying is not done before we reach the finish callback.
2241 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2242 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2243 *new MeterSection(meter_marker->meter()));
2245 drag_info.item = &new_marker->the_item();
2246 drag_info.copy = true;
2247 drag_info.data = new_marker;
2248 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2249 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2253 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2255 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2259 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2261 MeterMarker* marker = (MeterMarker *) drag_info.data;
2262 nframes_t adjusted_frame;
2264 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2265 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2271 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2272 snap_to (adjusted_frame);
2275 if (adjusted_frame == drag_info.last_pointer_frame) return;
2277 marker->set_position (adjusted_frame);
2280 drag_info.last_pointer_frame = adjusted_frame;
2281 drag_info.first_move = false;
2283 show_verbose_time_cursor (adjusted_frame, 10);
2287 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2289 if (drag_info.first_move) return;
2291 meter_marker_drag_motion_callback (drag_info.item, event);
2293 MeterMarker* marker = (MeterMarker *) drag_info.data;
2296 TempoMap& map (session->tempo_map());
2297 map.bbt_time (drag_info.last_pointer_frame, when);
2299 if (drag_info.copy == true) {
2300 begin_reversible_command (_("copy meter mark"));
2301 XMLNode &before = map.get_state();
2302 map.add_meter (marker->meter(), when);
2303 XMLNode &after = map.get_state();
2304 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2305 commit_reversible_command ();
2307 // delete the dummy marker we used for visual representation of copying.
2308 // a new visual marker will show up automatically.
2311 begin_reversible_command (_("move meter mark"));
2312 XMLNode &before = map.get_state();
2313 map.move_meter (marker->meter(), when);
2314 XMLNode &after = map.get_state();
2315 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2316 commit_reversible_command ();
2321 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2324 TempoMarker* tempo_marker;
2326 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2327 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2331 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2332 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2336 MetricSection& section (tempo_marker->tempo());
2338 if (!section.movable()) {
2342 drag_info.item = item;
2343 drag_info.data = marker;
2344 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2345 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2349 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2350 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2354 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2357 TempoMarker* tempo_marker;
2359 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2360 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2364 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2365 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2369 // create a dummy marker for visual representation of moving the copy.
2370 // The actual copying is not done before we reach the finish callback.
2372 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2373 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2374 *new TempoSection(tempo_marker->tempo()));
2376 drag_info.item = &new_marker->the_item();
2377 drag_info.copy = true;
2378 drag_info.data = new_marker;
2379 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2380 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2384 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2386 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2390 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2392 TempoMarker* marker = (TempoMarker *) drag_info.data;
2393 nframes_t adjusted_frame;
2395 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2396 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2402 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2403 snap_to (adjusted_frame);
2406 if (adjusted_frame == drag_info.last_pointer_frame) return;
2408 /* OK, we've moved far enough to make it worth actually move the thing. */
2410 marker->set_position (adjusted_frame);
2412 show_verbose_time_cursor (adjusted_frame, 10);
2414 drag_info.last_pointer_frame = adjusted_frame;
2415 drag_info.first_move = false;
2419 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2421 if (drag_info.first_move) return;
2423 tempo_marker_drag_motion_callback (drag_info.item, event);
2425 TempoMarker* marker = (TempoMarker *) drag_info.data;
2428 TempoMap& map (session->tempo_map());
2429 map.bbt_time (drag_info.last_pointer_frame, when);
2431 if (drag_info.copy == true) {
2432 begin_reversible_command (_("copy tempo mark"));
2433 XMLNode &before = map.get_state();
2434 map.add_tempo (marker->tempo(), when);
2435 XMLNode &after = map.get_state();
2436 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2437 commit_reversible_command ();
2439 // delete the dummy marker we used for visual representation of copying.
2440 // a new visual marker will show up automatically.
2443 begin_reversible_command (_("move tempo mark"));
2444 XMLNode &before = map.get_state();
2445 map.move_tempo (marker->tempo(), when);
2446 XMLNode &after = map.get_state();
2447 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2448 commit_reversible_command ();
2453 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2455 ControlPoint* control_point;
2457 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2458 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2462 // We shouldn't remove the first or last gain point
2463 if (control_point->line.is_last_point(*control_point) ||
2464 control_point->line.is_first_point(*control_point)) {
2468 control_point->line.remove_point (*control_point);
2472 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2474 ControlPoint* control_point;
2476 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2477 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2481 control_point->line.remove_point (*control_point);
2485 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2487 ControlPoint* control_point;
2489 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2490 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2494 drag_info.item = item;
2495 drag_info.data = control_point;
2496 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2497 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2499 start_grab (event, fader_cursor);
2501 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2503 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2504 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2505 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2507 show_verbose_canvas_cursor ();
2511 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2513 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2515 double cx = drag_info.current_pointer_x;
2516 double cy = drag_info.current_pointer_y;
2518 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2519 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2521 if (drag_info.x_constrained) {
2522 cx = drag_info.grab_x;
2524 if (drag_info.y_constrained) {
2525 cy = drag_info.grab_y;
2528 cp->line.parent_group().w2i (cx, cy);
2532 cy = min ((double) cp->line.height(), cy);
2534 //translate cx to frames
2535 nframes_t cx_frames = unit_to_frame (cx);
2537 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2538 snap_to (cx_frames);
2541 float fraction = 1.0 - (cy / cp->line.height());
2545 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2551 cp->line.point_drag (*cp, cx_frames , fraction, push);
2553 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2555 drag_info.first_move = false;
2559 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2561 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2563 if (drag_info.first_move) {
2567 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2568 reset_point_selection ();
2572 control_point_drag_motion_callback (item, event);
2574 cp->line.end_drag (cp);
2578 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2580 switch (mouse_mode) {
2582 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2583 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2591 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2595 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2596 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2600 start_line_grab (al, event);
2604 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2608 nframes_t frame_within_region;
2610 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2614 cx = event->button.x;
2615 cy = event->button.y;
2616 line->parent_group().w2i (cx, cy);
2617 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2619 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2620 current_line_drag_info.after)) {
2621 /* no adjacent points */
2625 drag_info.item = &line->grab_item();
2626 drag_info.data = line;
2627 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2628 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2630 start_grab (event, fader_cursor);
2632 double fraction = 1.0 - (cy / line->height());
2634 line->start_drag (0, drag_info.grab_frame, fraction);
2636 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2637 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2638 show_verbose_canvas_cursor ();
2642 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2644 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2645 double cx = drag_info.current_pointer_x;
2646 double cy = drag_info.current_pointer_y;
2648 line->parent_group().w2i (cx, cy);
2651 fraction = 1.0 - (cy / line->height());
2655 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2661 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2663 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2667 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2669 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2670 line_drag_motion_callback (item, event);
2675 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2677 if (selection->regions.empty() || clicked_regionview == 0) {
2681 drag_info.copy = false;
2682 drag_info.item = item;
2683 drag_info.data = clicked_regionview;
2684 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2685 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2690 TimeAxisView* tvp = clicked_trackview;
2691 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2693 if (tv && tv->is_audio_track()) {
2694 speed = tv->get_diskstream()->speed();
2697 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2698 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2699 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2700 // we want a move threshold
2701 drag_info.want_move_threshold = true;
2703 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2705 begin_reversible_command (_("move region(s)"));
2709 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2711 cerr << "start region copy grab, selected regions = " << selection->regions.size() << endl;
2713 if (selection->regions.empty() || clicked_regionview == 0) {
2717 drag_info.copy = true;
2718 drag_info.item = item;
2719 drag_info.data = clicked_regionview;
2723 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2724 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2727 if (atv && atv->is_audio_track()) {
2728 speed = atv->get_diskstream()->speed();
2731 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2732 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2733 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2734 // we want a move threshold
2735 drag_info.want_move_threshold = true;
2736 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2737 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2738 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2742 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2744 if (selection->regions.empty() || clicked_regionview == 0) {
2748 drag_info.copy = false;
2749 drag_info.item = item;
2750 drag_info.data = clicked_regionview;
2751 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2752 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2757 TimeAxisView* tvp = clicked_trackview;
2758 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2760 if (tv && tv->is_audio_track()) {
2761 speed = tv->get_diskstream()->speed();
2764 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2765 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2766 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2767 // we want a move threshold
2768 drag_info.want_move_threshold = true;
2769 drag_info.brushing = true;
2771 begin_reversible_command (_("Drag region brush"));
2775 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2779 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2780 nframes_t pending_region_position = 0;
2781 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2782 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2783 bool clamp_y_axis = false;
2784 vector<int32_t> height_list(512) ;
2785 vector<int32_t>::iterator j;
2787 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2789 drag_info.want_move_threshold = false; // don't copy again
2791 /* this is committed in the grab finished callback. */
2793 begin_reversible_command (_("Drag region copy"));
2795 /* duplicate the region(s) */
2797 vector<RegionView*> new_regionviews;
2799 set<boost::shared_ptr<Playlist> > affected_playlists;
2800 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2802 // TODO: Crossfades need to be copied!
2803 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2808 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
2809 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2811 insert_result = affected_playlists.insert (to_playlist);
2812 if (insert_result.second) {
2813 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
2816 latest_regionview = 0;
2818 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2820 /* create a new region with the same name. */
2822 // FIXME: ew. need a (virtual) Region::duplicate() or something?
2824 boost::shared_ptr<Region> newregion;
2825 boost::shared_ptr<Region> ar;
2827 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
2828 newregion = RegionFactory::create (ar);
2830 assert(newregion != 0);
2832 /* if the original region was locked, we don't care */
2834 newregion->set_locked (false);
2836 to_playlist->add_region (newregion, (nframes_t) (rv->region()->position() * atv->get_diskstream()->speed()));
2840 if (latest_regionview) {
2841 new_regionviews.push_back (latest_regionview);
2847 if (new_regionviews.empty()) {
2851 /* reset selection to new regionviews */
2853 selection->set (new_regionviews);
2855 /* reset drag_info data to reflect the fact that we are dragging the copies */
2857 drag_info.data = new_regionviews.front();
2858 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2861 /* Which trackview is this ? */
2863 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2864 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2866 /* The region motion is only processed if the pointer is over
2870 if (!tv || !tv->is_audio_track()) {
2871 /* To make sure we hide the verbose canvas cursor when the mouse is
2872 not held over and audiotrack.
2874 hide_verbose_canvas_cursor ();
2878 original_pointer_order = drag_info.last_trackview->order;
2880 /************************************************************
2882 ************************************************************/
2884 if (drag_info.brushing) {
2885 clamp_y_axis = true;
2890 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2892 int32_t children = 0, numtracks = 0;
2893 // XXX hard coding track limit, oh my, so very very bad
2894 bitset <1024> tracks (0x00);
2895 /* get a bitmask representing the visible tracks */
2897 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2898 TimeAxisView *tracklist_timeview;
2899 tracklist_timeview = (*i);
2900 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2901 list<TimeAxisView*> children_list;
2903 /* zeroes are audio tracks. ones are other types. */
2905 if (!atv2->hidden()) {
2907 if (visible_y_high < atv2->order) {
2908 visible_y_high = atv2->order;
2910 if (visible_y_low > atv2->order) {
2911 visible_y_low = atv2->order;
2914 if (!atv2->is_audio_track()) {
2915 tracks = tracks |= (0x01 << atv2->order);
2918 height_list[atv2->order] = (*i)->height;
2920 if ((children_list = atv2->get_child_list()).size() > 0) {
2921 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2922 tracks = tracks |= (0x01 << (atv2->order + children));
2923 height_list[atv2->order + children] = (*j)->height;
2931 /* find the actual span according to the canvas */
2933 canvas_pointer_y_span = pointer_y_span;
2934 if (drag_info.last_trackview->order >= tv->order) {
2936 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2937 if (height_list[y] == 0 ) {
2938 canvas_pointer_y_span--;
2943 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2944 if ( height_list[y] == 0 ) {
2945 canvas_pointer_y_span++;
2950 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2951 RegionView* rv2 = (*i);
2952 double ix1, ix2, iy1, iy2;
2955 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2956 rv2->get_canvas_group()->i2w (ix1, iy1);
2957 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2958 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2960 if (atv2->order != original_pointer_order) {
2961 /* this isn't the pointer track */
2963 if (canvas_pointer_y_span > 0) {
2965 /* moving up the canvas */
2966 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2968 int32_t visible_tracks = 0;
2969 while (visible_tracks < canvas_pointer_y_span ) {
2972 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2973 /* we're passing through a hidden track */
2978 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2979 clamp_y_axis = true;
2983 clamp_y_axis = true;
2986 } else if (canvas_pointer_y_span < 0) {
2988 /*moving down the canvas*/
2990 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2993 int32_t visible_tracks = 0;
2995 while (visible_tracks > canvas_pointer_y_span ) {
2998 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3002 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3003 clamp_y_axis = true;
3008 clamp_y_axis = true;
3014 /* this is the pointer's track */
3015 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3016 clamp_y_axis = true;
3017 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3018 clamp_y_axis = true;
3026 } else if (drag_info.last_trackview == tv) {
3027 clamp_y_axis = true;
3031 if (!clamp_y_axis) {
3032 drag_info.last_trackview = tv;
3035 /************************************************************
3037 ************************************************************/
3039 /* compute the amount of pointer motion in frames, and where
3040 the region would be if we moved it by that much.
3043 if (drag_info.move_threshold_passed) {
3045 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3047 nframes_t sync_frame;
3048 nframes_t sync_offset;
3051 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3053 sync_offset = rv->region()->sync_offset (sync_dir);
3054 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3056 /* we snap if the snap modifier is not enabled.
3059 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3060 snap_to (sync_frame);
3063 if (sync_frame - sync_offset <= sync_frame) {
3064 pending_region_position = sync_frame - (sync_dir*sync_offset);
3066 pending_region_position = 0;
3070 pending_region_position = 0;
3073 if (pending_region_position > max_frames - rv->region()->length()) {
3074 pending_region_position = drag_info.last_frame_position;
3077 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3079 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3081 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3082 to make it appear at the new location.
3085 if (pending_region_position > drag_info.last_frame_position) {
3086 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3088 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3091 drag_info.last_frame_position = pending_region_position;
3098 /* threshold not passed */
3103 /*************************************************************
3105 ************************************************************/
3107 if (x_delta == 0 && (pointer_y_span == 0)) {
3108 /* haven't reached next snap point, and we're not switching
3109 trackviews. nothing to do.
3115 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3117 RegionView* rv2 = (*i);
3119 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3121 double ix1, ix2, iy1, iy2;
3122 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3123 rv2->get_canvas_group()->i2w (ix1, iy1);
3132 /*************************************************************
3134 ************************************************************/
3136 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3137 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3139 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3141 RegionView* rv = (*i);
3142 double ix1, ix2, iy1, iy2;
3143 int32_t temp_pointer_y_span = pointer_y_span;
3145 /* get item BBox, which will be relative to parent. so we have
3146 to query on a child, then convert to world coordinates using
3150 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3151 rv->get_canvas_group()->i2w (ix1, iy1);
3152 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3153 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3154 AudioTimeAxisView* temp_atv;
3156 if ((pointer_y_span != 0) && !clamp_y_axis) {
3159 for (j = height_list.begin(); j!= height_list.end(); j++) {
3160 if (x == canvas_atv->order) {
3161 /* we found the track the region is on */
3162 if (x != original_pointer_order) {
3163 /*this isn't from the same track we're dragging from */
3164 temp_pointer_y_span = canvas_pointer_y_span;
3166 while (temp_pointer_y_span > 0) {
3167 /* we're moving up canvas-wise,
3168 so we need to find the next track height
3170 if (j != height_list.begin()) {
3173 if (x != original_pointer_order) {
3174 /* we're not from the dragged track, so ignore hidden tracks. */
3176 temp_pointer_y_span++;
3180 temp_pointer_y_span--;
3182 while (temp_pointer_y_span < 0) {
3184 if (x != original_pointer_order) {
3186 temp_pointer_y_span--;
3190 if (j != height_list.end()) {
3193 temp_pointer_y_span++;
3195 /* find out where we'll be when we move and set height accordingly */
3197 tvp2 = trackview_by_y_position (iy1 + y_delta);
3198 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3199 rv->set_height (temp_atv->height);
3201 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3202 personally, i think this can confuse things, but never mind.
3205 //const GdkColor& col (temp_atv->view->get_region_color());
3206 //rv->set_color (const_cast<GdkColor&>(col));
3213 /* prevent the regionview from being moved to before
3214 the zero position on the canvas.
3219 if (-x_delta > ix1) {
3222 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3223 x_delta = max_frames - rv->region()->last_frame();
3226 if (drag_info.first_move) {
3228 /* hide any dependent views */
3230 // rv->get_time_axis_view().hide_dependent_views (*rv);
3232 /* this is subtle. raising the regionview itself won't help,
3233 because raise_to_top() just puts the item on the top of
3234 its parent's stack. so, we need to put the trackview canvas_display group
3235 on the top, since its parent is the whole canvas.
3238 rv->get_canvas_group()->raise_to_top();
3239 rv->get_time_axis_view().canvas_display->raise_to_top();
3240 cursor_group->raise_to_top();
3242 /* freeze the playlists from notifying till
3246 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3247 if (atv && atv->is_audio_track()) {
3248 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>(atv->get_diskstream()->playlist());
3250 /* only freeze and capture state once */
3252 insert_result = motion_frozen_playlists.insert (pl);
3253 if (insert_result.second) {
3255 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
3259 rv->region()->set_opaque(false);
3262 if (drag_info.brushing) {
3263 mouse_brush_insert_region (rv, pending_region_position);
3265 rv->move (x_delta, y_delta);
3269 if (drag_info.first_move) {
3270 cursor_group->raise_to_top();
3273 drag_info.first_move = false;
3275 if (x_delta != 0 && !drag_info.brushing) {
3276 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3282 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3285 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3286 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3287 bool nocommit = true;
3289 RouteTimeAxisView* atv;
3290 bool regionview_y_movement;
3291 bool regionview_x_movement;
3293 /* first_move is set to false if the regionview has been moved in the
3297 if (drag_info.first_move) {
3304 /* The regionview has been moved at some stage during the grab so we need
3305 to account for any mouse movement between this event and the last one.
3308 region_drag_motion_callback (item, event);
3310 if (drag_info.brushing) {
3311 /* all changes were made during motion event handlers */
3315 /* adjust for track speed */
3318 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3319 if (atv && atv->get_diskstream()) {
3320 speed = atv->get_diskstream()->speed();
3323 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3324 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3326 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3327 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3329 if (regionview_y_movement) {
3331 /* motion between tracks */
3333 list<RegionView*> new_selection;
3335 /* moved to a different audio track. */
3337 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3339 RegionView* rv2 = (*i);
3341 /* the region that used to be in the old playlist is not
3342 moved to the new one - we make a copy of it. as a result,
3343 any existing editor for the region should no longer be
3347 if (!drag_info.copy) {
3348 rv2->hide_region_editor();
3350 new_selection.push_back (rv2);
3354 /* first, freeze the target tracks */
3356 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3358 boost::shared_ptr<Playlist> from_playlist;
3359 boost::shared_ptr<Playlist> to_playlist;
3361 double ix1, ix2, iy1, iy2;
3363 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3364 (*i)->get_canvas_group()->i2w (ix1, iy1);
3365 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3366 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3368 (*i)->region()->set_opaque (true);
3370 from_playlist = (*i)->region()->playlist();
3371 to_playlist = atv2->playlist();
3373 /* the from_playlist was frozen in the "first_move" case
3374 of the motion handler. the insert can fail,
3375 but that doesn't matter. it just means
3376 we already have the playlist in the list.
3379 motion_frozen_playlists.insert (from_playlist);
3381 /* only freeze the to_playlist once */
3383 insert_result = motion_frozen_playlists.insert(to_playlist);
3384 if (insert_result.second) {
3385 to_playlist->freeze();
3386 session->add_command(new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3391 /* now do it again with the actual operations */
3393 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3395 boost::shared_ptr<Playlist> from_playlist;
3396 boost::shared_ptr<Playlist> to_playlist;
3398 double ix1, ix2, iy1, iy2;
3400 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3401 (*i)->get_canvas_group()->i2w (ix1, iy1);
3402 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3403 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3405 from_playlist = (*i)->region()->playlist();
3406 to_playlist = atv2->playlist();
3408 latest_regionview = 0;
3410 where = (nframes_t) (unit_to_frame (ix1) * speed);
3411 boost::shared_ptr<Region> new_region (RegionFactory::create ((*i)->region()));
3413 from_playlist->remove_region (((*i)->region()));
3415 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3416 to_playlist->add_region (new_region, where);
3419 if (latest_regionview) {
3420 selection->add (latest_regionview);
3426 /* motion within a single track */
3428 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3432 if (rv->region()->locked()) {
3436 if (regionview_x_movement) {
3437 double ownspeed = 1.0;
3438 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3440 if (atv && atv->get_diskstream()) {
3441 ownspeed = atv->get_diskstream()->speed();
3444 /* base the new region position on the current position of the regionview.*/
3446 double ix1, ix2, iy1, iy2;
3448 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3449 rv->get_canvas_group()->i2w (ix1, iy1);
3450 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3454 where = rv->region()->position();
3457 rv->get_time_axis_view().reveal_dependent_views (*rv);
3459 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3461 rv->region()->set_position (where, (void *) this);
3462 rv->region()->set_opaque (true);
3467 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3469 session->add_command (new MementoCommand<Playlist>(*((*p).get()), 0, & (*p)->get_state()));
3472 motion_frozen_playlists.clear ();
3475 commit_reversible_command ();
3480 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3482 /* Either add to or set the set the region selection, unless
3483 this is an alignment click (control used)
3486 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3487 TimeAxisView* tv = &rv.get_time_axis_view();
3488 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3490 if (atv && atv->is_audio_track()) {
3491 speed = atv->get_diskstream()->speed();
3494 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3496 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3498 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3500 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3504 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3510 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3521 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3522 case AudioClock::BBT:
3523 session->bbt_time (frame, bbt);
3524 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3527 case AudioClock::SMPTE:
3528 session->smpte_time (frame, smpte);
3529 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3532 case AudioClock::MinSec:
3533 /* XXX fix this to compute min/sec properly */
3534 session->smpte_time (frame, smpte);
3535 secs = smpte.seconds + ((float) smpte.frames / Config->get_smpte_frames_per_second());
3536 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3540 snprintf (buf, sizeof(buf), "%u", frame);
3544 if (xpos >= 0 && ypos >=0) {
3545 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3548 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3550 show_verbose_canvas_cursor ();
3554 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3561 Meter meter_at_start(session->tempo_map().meter_at(start));
3567 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3568 case AudioClock::BBT:
3569 session->bbt_time (start, sbbt);
3570 session->bbt_time (end, ebbt);
3573 /* XXX this computation won't work well if the
3574 user makes a selection that spans any meter changes.
3577 ebbt.bars -= sbbt.bars;
3578 if (ebbt.beats >= sbbt.beats) {
3579 ebbt.beats -= sbbt.beats;
3582 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3584 if (ebbt.ticks >= sbbt.ticks) {
3585 ebbt.ticks -= sbbt.ticks;
3588 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3591 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3594 case AudioClock::SMPTE:
3595 session->smpte_duration (end - start, smpte);
3596 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3599 case AudioClock::MinSec:
3600 /* XXX fix this to compute min/sec properly */
3601 session->smpte_duration (end - start, smpte);
3602 secs = smpte.seconds + ((float) smpte.frames / Config->get_smpte_frames_per_second());
3603 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3607 snprintf (buf, sizeof(buf), "%u", end - start);
3611 if (xpos >= 0 && ypos >=0) {
3612 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3615 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3617 show_verbose_canvas_cursor ();
3621 Editor::collect_new_region_view (RegionView* rv)
3623 latest_regionview = rv;
3627 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3629 if (clicked_regionview == 0) {
3633 /* lets try to create new Region for the selection */
3635 vector<boost::shared_ptr<AudioRegion> > new_regions;
3636 create_region_from_selection (new_regions);
3638 if (new_regions.empty()) {
3642 /* XXX fix me one day to use all new regions */
3644 boost::shared_ptr<Region> region (new_regions.front());
3646 /* add it to the current stream/playlist.
3648 tricky: the streamview for the track will add a new regionview. we will
3649 catch the signal it sends when it creates the regionview to
3650 set the regionview we want to then drag.
3653 latest_regionview = 0;
3654 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3656 /* A selection grab currently creates two undo/redo operations, one for
3657 creating the new region and another for moving it.
3660 begin_reversible_command (_("selection grab"));
3662 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3664 XMLNode *before = &(playlist->get_state());
3665 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3666 XMLNode *after = &(playlist->get_state());
3667 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3669 commit_reversible_command ();
3673 if (latest_regionview == 0) {
3674 /* something went wrong */
3678 /* we need to deselect all other regionviews, and select this one
3679 i'm ignoring undo stuff, because the region creation will take care of it */
3680 selection->set (latest_regionview);
3682 drag_info.item = latest_regionview->get_canvas_group();
3683 drag_info.data = latest_regionview;
3684 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3685 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3689 drag_info.last_trackview = clicked_trackview;
3690 drag_info.last_frame_position = latest_regionview->region()->position();
3691 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3693 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3697 Editor::cancel_selection ()
3699 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3700 (*i)->hide_selection ();
3702 begin_reversible_command (_("cancel selection"));
3703 selection->clear ();
3704 clicked_selection = 0;
3705 commit_reversible_command ();
3709 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3711 nframes_t start = 0;
3718 drag_info.item = item;
3719 drag_info.motion_callback = &Editor::drag_selection;
3720 drag_info.finished_callback = &Editor::end_selection_op;
3725 case CreateSelection:
3726 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3727 drag_info.copy = true;
3729 drag_info.copy = false;
3731 start_grab (event, selector_cursor);
3734 case SelectionStartTrim:
3735 if (clicked_trackview) {
3736 clicked_trackview->order_selection_trims (item, true);
3738 start_grab (event, trimmer_cursor);
3739 start = selection->time[clicked_selection].start;
3740 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3743 case SelectionEndTrim:
3744 if (clicked_trackview) {
3745 clicked_trackview->order_selection_trims (item, false);
3747 start_grab (event, trimmer_cursor);
3748 end = selection->time[clicked_selection].end;
3749 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3753 start = selection->time[clicked_selection].start;
3755 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3759 if (selection_op == SelectionMove) {
3760 show_verbose_time_cursor(start, 10);
3762 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3767 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3769 nframes_t start = 0;
3772 nframes_t pending_position;
3774 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3775 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3778 pending_position = 0;
3781 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3782 snap_to (pending_position);
3785 /* only alter selection if the current frame is
3786 different from the last frame position (adjusted)
3789 if (pending_position == drag_info.last_pointer_frame) return;
3791 switch (selection_op) {
3792 case CreateSelection:
3794 if (drag_info.first_move) {
3795 snap_to (drag_info.grab_frame);
3798 if (pending_position < drag_info.grab_frame) {
3799 start = pending_position;
3800 end = drag_info.grab_frame;
3802 end = pending_position;
3803 start = drag_info.grab_frame;
3806 /* first drag: Either add to the selection
3807 or create a new selection->
3810 if (drag_info.first_move) {
3812 begin_reversible_command (_("range selection"));
3814 if (drag_info.copy) {
3815 /* adding to the selection */
3816 clicked_selection = selection->add (start, end);
3817 drag_info.copy = false;
3819 /* new selection-> */
3820 clicked_selection = selection->set (clicked_trackview, start, end);
3825 case SelectionStartTrim:
3827 if (drag_info.first_move) {
3828 begin_reversible_command (_("trim selection start"));
3831 start = selection->time[clicked_selection].start;
3832 end = selection->time[clicked_selection].end;
3834 if (pending_position > end) {
3837 start = pending_position;
3841 case SelectionEndTrim:
3843 if (drag_info.first_move) {
3844 begin_reversible_command (_("trim selection end"));
3847 start = selection->time[clicked_selection].start;
3848 end = selection->time[clicked_selection].end;
3850 if (pending_position < start) {
3853 end = pending_position;
3860 if (drag_info.first_move) {
3861 begin_reversible_command (_("move selection"));
3864 start = selection->time[clicked_selection].start;
3865 end = selection->time[clicked_selection].end;
3867 length = end - start;
3869 start = pending_position;
3872 end = start + length;
3877 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3878 start_canvas_autoscroll (1);
3882 selection->replace (clicked_selection, start, end);
3885 drag_info.last_pointer_frame = pending_position;
3886 drag_info.first_move = false;
3888 if (selection_op == SelectionMove) {
3889 show_verbose_time_cursor(start, 10);
3891 show_verbose_time_cursor(pending_position, 10);
3896 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3898 if (!drag_info.first_move) {
3899 drag_selection (item, event);
3900 /* XXX this is not object-oriented programming at all. ick */
3901 if (selection->time.consolidate()) {
3902 selection->TimeChanged ();
3904 commit_reversible_command ();
3906 /* just a click, no pointer movement.*/
3908 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3910 selection->clear_time();
3915 /* XXX what happens if its a music selection? */
3916 session->set_audio_range (selection->time);
3917 stop_canvas_autoscroll ();
3921 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3924 TimeAxisView* tvp = clicked_trackview;
3925 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3927 if (tv && tv->is_audio_track()) {
3928 speed = tv->get_diskstream()->speed();
3931 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3932 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3933 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3935 motion_frozen_playlists.clear();
3937 //drag_info.item = clicked_regionview->get_name_highlight();
3938 drag_info.item = item;
3939 drag_info.motion_callback = &Editor::trim_motion_callback;
3940 drag_info.finished_callback = &Editor::trim_finished_callback;
3942 start_grab (event, trimmer_cursor);
3944 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3945 trim_op = ContentsTrim;
3947 /* These will get overridden for a point trim.*/
3948 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3949 /* closer to start */
3950 trim_op = StartTrim;
3951 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3959 show_verbose_time_cursor(region_start, 10);
3962 show_verbose_time_cursor(region_end, 10);
3965 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3971 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3973 RegionView* rv = clicked_regionview;
3974 nframes_t frame_delta = 0;
3975 bool left_direction;
3976 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3978 /* snap modifier works differently here..
3979 its' current state has to be passed to the
3980 various trim functions in order to work properly
3984 TimeAxisView* tvp = clicked_trackview;
3985 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3986 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3988 if (tv && tv->is_audio_track()) {
3989 speed = tv->get_diskstream()->speed();
3992 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3993 left_direction = true;
3995 left_direction = false;
3999 snap_to (drag_info.current_pointer_frame);
4002 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4006 if (drag_info.first_move) {
4012 trim_type = "Region start trim";
4015 trim_type = "Region end trim";
4018 trim_type = "Region content trim";
4022 begin_reversible_command (trim_type);
4024 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4025 (*i)->region()->set_opaque(false);
4026 (*i)->region()->freeze ();
4028 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4030 arv->temporarily_hide_envelope ();
4032 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4033 insert_result = motion_frozen_playlists.insert (pl);
4034 if (insert_result.second) {
4035 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4040 if (left_direction) {
4041 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4043 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4048 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4051 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4052 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4058 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4061 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4062 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4069 bool swap_direction = false;
4071 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4072 swap_direction = true;
4075 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4076 i != selection->regions.by_layer().end(); ++i)
4078 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4086 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4089 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4092 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4096 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4097 drag_info.first_move = false;
4101 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4103 boost::shared_ptr<Region> region (rv.region());
4105 if (region->locked()) {
4109 nframes_t new_bound;
4112 TimeAxisView* tvp = clicked_trackview;
4113 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4115 if (tv && tv->is_audio_track()) {
4116 speed = tv->get_diskstream()->speed();
4119 if (left_direction) {
4120 if (swap_direction) {
4121 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4123 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4126 if (swap_direction) {
4127 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4129 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4134 snap_to (new_bound);
4136 region->trim_start ((nframes_t) (new_bound * speed), this);
4137 rv.region_changed (StartChanged);
4141 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4143 boost::shared_ptr<Region> region (rv.region());
4145 if (region->locked()) {
4149 nframes_t new_bound;
4152 TimeAxisView* tvp = clicked_trackview;
4153 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4155 if (tv && tv->is_audio_track()) {
4156 speed = tv->get_diskstream()->speed();
4159 if (left_direction) {
4160 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4162 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4166 snap_to (new_bound, (left_direction ? 0 : 1));
4169 region->trim_front ((nframes_t) (new_bound * speed), this);
4171 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4175 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4177 boost::shared_ptr<Region> region (rv.region());
4179 if (region->locked()) {
4183 nframes_t new_bound;
4186 TimeAxisView* tvp = clicked_trackview;
4187 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4189 if (tv && tv->is_audio_track()) {
4190 speed = tv->get_diskstream()->speed();
4193 if (left_direction) {
4194 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4196 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4200 snap_to (new_bound);
4202 region->trim_end ((nframes_t) (new_bound * speed), this);
4203 rv.region_changed (LengthChanged);
4207 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4209 if (!drag_info.first_move) {
4210 trim_motion_callback (item, event);
4212 if (!clicked_regionview->get_selected()) {
4213 thaw_region_after_trim (*clicked_regionview);
4216 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4217 i != selection->regions.by_layer().end(); ++i)
4219 thaw_region_after_trim (**i);
4220 (*i)->region()->set_opaque(true);
4224 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4226 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4229 motion_frozen_playlists.clear ();
4231 commit_reversible_command();
4233 /* no mouse movement */
4239 Editor::point_trim (GdkEvent* event)
4241 RegionView* rv = clicked_regionview;
4242 nframes_t new_bound = drag_info.current_pointer_frame;
4244 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4245 snap_to (new_bound);
4248 /* Choose action dependant on which button was pressed */
4249 switch (event->button.button) {
4251 trim_op = StartTrim;
4252 begin_reversible_command (_("Start point trim"));
4254 if (rv->get_selected()) {
4256 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4257 i != selection->regions.by_layer().end(); ++i)
4259 if (!(*i)->region()->locked()) {
4260 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4261 XMLNode &before = pl->get_state();
4262 (*i)->region()->trim_front (new_bound, this);
4263 XMLNode &after = pl->get_state();
4264 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4270 if (!rv->region()->locked()) {
4271 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4272 XMLNode &before = pl->get_state();
4273 rv->region()->trim_front (new_bound, this);
4274 XMLNode &after = pl->get_state();
4275 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4279 commit_reversible_command();
4284 begin_reversible_command (_("End point trim"));
4286 if (rv->get_selected()) {
4288 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4290 if (!(*i)->region()->locked()) {
4291 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4292 XMLNode &before = pl->get_state();
4293 (*i)->region()->trim_end (new_bound, this);
4294 XMLNode &after = pl->get_state();
4295 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4301 if (!rv->region()->locked()) {
4302 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4303 XMLNode &before = pl->get_state();
4304 rv->region()->trim_end (new_bound, this);
4305 XMLNode &after = pl->get_state();
4306 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4310 commit_reversible_command();
4319 Editor::thaw_region_after_trim (RegionView& rv)
4321 boost::shared_ptr<Region> region (rv.region());
4323 if (region->locked()) {
4327 region->thaw (_("trimmed region"));
4328 XMLNode &after = region->playlist()->get_state();
4329 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4331 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4333 arv->unhide_envelope ();
4337 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4342 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4343 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4347 Location* location = find_location_from_marker (marker, is_start);
4348 location->set_hidden (true, this);
4353 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4359 drag_info.item = item;
4360 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4361 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4363 range_marker_op = op;
4365 if (!temp_location) {
4366 temp_location = new Location;
4370 case CreateRangeMarker:
4371 case CreateTransportMarker:
4373 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4374 drag_info.copy = true;
4376 drag_info.copy = false;
4378 start_grab (event, selector_cursor);
4382 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4387 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4389 nframes_t start = 0;
4391 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4393 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4394 snap_to (drag_info.current_pointer_frame);
4397 /* only alter selection if the current frame is
4398 different from the last frame position.
4401 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4403 switch (range_marker_op) {
4404 case CreateRangeMarker:
4405 case CreateTransportMarker:
4406 if (drag_info.first_move) {
4407 snap_to (drag_info.grab_frame);
4410 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4411 start = drag_info.current_pointer_frame;
4412 end = drag_info.grab_frame;
4414 end = drag_info.current_pointer_frame;
4415 start = drag_info.grab_frame;
4418 /* first drag: Either add to the selection
4419 or create a new selection.
4422 if (drag_info.first_move) {
4424 temp_location->set (start, end);
4428 update_marker_drag_item (temp_location);
4429 range_marker_drag_rect->show();
4430 range_marker_drag_rect->raise_to_top();
4436 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4437 start_canvas_autoscroll (1);
4441 temp_location->set (start, end);
4443 double x1 = frame_to_pixel (start);
4444 double x2 = frame_to_pixel (end);
4445 crect->property_x1() = x1;
4446 crect->property_x2() = x2;
4448 update_marker_drag_item (temp_location);
4451 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4452 drag_info.first_move = false;
4454 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4459 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4461 Location * newloc = 0;
4463 if (!drag_info.first_move) {
4464 drag_range_markerbar_op (item, event);
4466 switch (range_marker_op) {
4467 case CreateRangeMarker:
4469 begin_reversible_command (_("new range marker"));
4470 XMLNode &before = session->locations()->get_state();
4471 newloc = new Location(temp_location->start(), temp_location->end(), "unnamed", Location::IsRangeMarker);
4472 session->locations()->add (newloc, true);
4473 XMLNode &after = session->locations()->get_state();
4474 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4475 commit_reversible_command ();
4477 range_bar_drag_rect->hide();
4478 range_marker_drag_rect->hide();
4482 case CreateTransportMarker:
4483 // popup menu to pick loop or punch
4484 new_transport_marker_context_menu (&event->button, item);
4489 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4491 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4496 start = session->locations()->first_mark_before (drag_info.grab_frame);
4497 end = session->locations()->first_mark_after (drag_info.grab_frame);
4499 if (end == max_frames) {
4500 end = session->current_end_frame ();
4504 start = session->current_start_frame ();
4507 switch (mouse_mode) {
4509 /* find the two markers on either side and then make the selection from it */
4510 cerr << "select between " << start << " .. " << end << endl;
4511 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4515 /* find the two markers on either side of the click and make the range out of it */
4516 selection->set (0, start, end);
4525 stop_canvas_autoscroll ();
4531 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4533 drag_info.item = item;
4534 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4535 drag_info.finished_callback = &Editor::end_mouse_zoom;
4537 start_grab (event, zoom_cursor);
4539 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4543 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4548 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4549 snap_to (drag_info.current_pointer_frame);
4551 if (drag_info.first_move) {
4552 snap_to (drag_info.grab_frame);
4556 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4558 /* base start and end on initial click position */
4559 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4560 start = drag_info.current_pointer_frame;
4561 end = drag_info.grab_frame;
4563 end = drag_info.current_pointer_frame;
4564 start = drag_info.grab_frame;
4569 if (drag_info.first_move) {
4571 zoom_rect->raise_to_top();
4574 reposition_zoom_rect(start, end);
4576 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4577 drag_info.first_move = false;
4579 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4584 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4586 if (!drag_info.first_move) {
4587 drag_mouse_zoom (item, event);
4589 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4590 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4592 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4595 temporal_zoom_to_frame (false, drag_info.grab_frame);
4597 temporal_zoom_step (false);
4598 center_screen (drag_info.grab_frame);
4606 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4608 double x1 = frame_to_pixel (start);
4609 double x2 = frame_to_pixel (end);
4610 double y2 = full_canvas_height - 1.0;
4612 zoom_rect->property_x1() = x1;
4613 zoom_rect->property_y1() = 1.0;
4614 zoom_rect->property_x2() = x2;
4615 zoom_rect->property_y2() = y2;
4619 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4621 drag_info.item = item;
4622 drag_info.motion_callback = &Editor::drag_rubberband_select;
4623 drag_info.finished_callback = &Editor::end_rubberband_select;
4625 start_grab (event, cross_hair_cursor);
4627 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4631 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4638 /* use a bigger drag threshold than the default */
4640 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4644 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4645 // snap_to (drag_info.current_pointer_frame);
4647 // if (drag_info.first_move) {
4648 // snap_to (drag_info.grab_frame);
4653 /* base start and end on initial click position */
4654 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4655 start = drag_info.current_pointer_frame;
4656 end = drag_info.grab_frame;
4658 end = drag_info.current_pointer_frame;
4659 start = drag_info.grab_frame;
4662 if (drag_info.current_pointer_y < drag_info.grab_y) {
4663 y1 = drag_info.current_pointer_y;
4664 y2 = drag_info.grab_y;
4667 y2 = drag_info.current_pointer_y;
4668 y1 = drag_info.grab_y;
4672 if (start != end || y1 != y2) {
4674 double x1 = frame_to_pixel (start);
4675 double x2 = frame_to_pixel (end);
4677 rubberband_rect->property_x1() = x1;
4678 rubberband_rect->property_y1() = y1;
4679 rubberband_rect->property_x2() = x2;
4680 rubberband_rect->property_y2() = y2;
4682 rubberband_rect->show();
4683 rubberband_rect->raise_to_top();
4685 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4686 drag_info.first_move = false;
4688 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4693 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4695 if (!drag_info.first_move) {
4697 drag_rubberband_select (item, event);
4700 if (drag_info.current_pointer_y < drag_info.grab_y) {
4701 y1 = drag_info.current_pointer_y;
4702 y2 = drag_info.grab_y;
4705 y2 = drag_info.current_pointer_y;
4706 y1 = drag_info.grab_y;
4710 Selection::Operation op = Keyboard::selection_type (event->button.state);
4713 begin_reversible_command (_("select regions"));
4715 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4716 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4718 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4722 commit_reversible_command ();
4726 selection->clear_regions();
4727 selection->clear_points ();
4728 selection->clear_lines ();
4731 rubberband_rect->hide();
4736 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4738 using namespace Gtkmm2ext;
4740 ArdourPrompter prompter (false);
4742 prompter.set_prompt (_("Name for region:"));
4743 prompter.set_initial_text (clicked_regionview->region()->name());
4744 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4745 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4746 prompter.show_all ();
4747 switch (prompter.run ()) {
4748 case Gtk::RESPONSE_ACCEPT:
4750 prompter.get_result(str);
4752 clicked_regionview->region()->set_name (str);
4760 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4762 drag_info.item = item;
4763 drag_info.motion_callback = &Editor::time_fx_motion;
4764 drag_info.finished_callback = &Editor::end_time_fx;
4768 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4772 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4774 RegionView* rv = clicked_regionview;
4776 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4777 snap_to (drag_info.current_pointer_frame);
4780 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4784 if (drag_info.current_pointer_frame > rv->region()->position()) {
4785 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4788 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4789 drag_info.first_move = false;
4791 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4795 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4797 clicked_regionview->get_time_axis_view().hide_timestretch ();
4799 if (drag_info.first_move) {
4803 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4804 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4806 begin_reversible_command (_("timestretch"));
4808 if (run_timestretch (selection->regions, percentage) == 0) {
4809 session->commit_reversible_command ();
4814 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4816 /* no brushing without a useful snap setting */
4819 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4822 switch (snap_mode) {
4824 return; /* can't work because it allows region to be placed anywhere */
4829 switch (snap_type) {
4832 case SnapToEditCursor:
4839 /* don't brush a copy over the original */
4841 if (pos == rv->region()->position()) {
4845 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4847 if (atv == 0 || !atv->is_audio_track()) {
4851 boost::shared_ptr<Playlist> playlist = atv->playlist();
4852 double speed = atv->get_diskstream()->speed();
4854 XMLNode &before = playlist->get_state();
4855 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4856 XMLNode &after = playlist->get_state();
4857 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4859 // playlist is frozen, so we have to update manually
4861 playlist->Modified(); /* EMIT SIGNAL */
4865 Editor::track_height_step_timeout ()
4868 struct timeval delta;
4870 gettimeofday (&now, 0);
4871 timersub (&now, &last_track_height_step_timestamp, &delta);
4873 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4874 current_stepping_trackview = 0;