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 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2807 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
2808 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2810 insert_result = affected_playlists.insert (to_playlist);
2811 if (insert_result.second) {
2812 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
2815 latest_regionview = 0;
2817 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2819 /* create a new region with the same name. */
2821 // FIXME: ew. need a (virtual) Region::duplicate() or something?
2823 boost::shared_ptr<Region> newregion;
2824 boost::shared_ptr<Region> ar;
2826 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
2827 newregion = RegionFactory::create (ar);
2829 assert(newregion != 0);
2831 /* if the original region was locked, we don't care */
2833 newregion->set_locked (false);
2835 to_playlist->add_region (newregion, (nframes_t) (rv->region()->position() * atv->get_diskstream()->speed()));
2839 if (latest_regionview) {
2840 new_regionviews.push_back (latest_regionview);
2846 if (new_regionviews.empty()) {
2850 /* reset selection to new regionviews */
2852 selection->set (new_regionviews);
2854 /* reset drag_info data to reflect the fact that we are dragging the copies */
2856 drag_info.data = new_regionviews.front();
2857 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2860 /* Which trackview is this ? */
2862 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2863 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2865 /* The region motion is only processed if the pointer is over
2869 if (!tv || !tv->is_audio_track()) {
2870 /* To make sure we hide the verbose canvas cursor when the mouse is
2871 not held over and audiotrack.
2873 hide_verbose_canvas_cursor ();
2877 original_pointer_order = drag_info.last_trackview->order;
2879 /************************************************************
2881 ************************************************************/
2883 if (drag_info.brushing) {
2884 clamp_y_axis = true;
2889 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2891 int32_t children = 0, numtracks = 0;
2892 // XXX hard coding track limit, oh my, so very very bad
2893 bitset <1024> tracks (0x00);
2894 /* get a bitmask representing the visible tracks */
2896 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2897 TimeAxisView *tracklist_timeview;
2898 tracklist_timeview = (*i);
2899 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2900 list<TimeAxisView*> children_list;
2902 /* zeroes are audio tracks. ones are other types. */
2904 if (!atv2->hidden()) {
2906 if (visible_y_high < atv2->order) {
2907 visible_y_high = atv2->order;
2909 if (visible_y_low > atv2->order) {
2910 visible_y_low = atv2->order;
2913 if (!atv2->is_audio_track()) {
2914 tracks = tracks |= (0x01 << atv2->order);
2917 height_list[atv2->order] = (*i)->height;
2919 if ((children_list = atv2->get_child_list()).size() > 0) {
2920 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2921 tracks = tracks |= (0x01 << (atv2->order + children));
2922 height_list[atv2->order + children] = (*j)->height;
2930 /* find the actual span according to the canvas */
2932 canvas_pointer_y_span = pointer_y_span;
2933 if (drag_info.last_trackview->order >= tv->order) {
2935 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2936 if (height_list[y] == 0 ) {
2937 canvas_pointer_y_span--;
2942 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2943 if ( height_list[y] == 0 ) {
2944 canvas_pointer_y_span++;
2949 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2950 RegionView* rv2 = (*i);
2951 double ix1, ix2, iy1, iy2;
2954 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2955 rv2->get_canvas_group()->i2w (ix1, iy1);
2956 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2957 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2959 if (atv2->order != original_pointer_order) {
2960 /* this isn't the pointer track */
2962 if (canvas_pointer_y_span > 0) {
2964 /* moving up the canvas */
2965 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2967 int32_t visible_tracks = 0;
2968 while (visible_tracks < canvas_pointer_y_span ) {
2971 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2972 /* we're passing through a hidden track */
2977 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2978 clamp_y_axis = true;
2982 clamp_y_axis = true;
2985 } else if (canvas_pointer_y_span < 0) {
2987 /*moving down the canvas*/
2989 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2992 int32_t visible_tracks = 0;
2994 while (visible_tracks > canvas_pointer_y_span ) {
2997 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3001 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3002 clamp_y_axis = true;
3007 clamp_y_axis = true;
3013 /* this is the pointer's track */
3014 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3015 clamp_y_axis = true;
3016 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3017 clamp_y_axis = true;
3025 } else if (drag_info.last_trackview == tv) {
3026 clamp_y_axis = true;
3030 if (!clamp_y_axis) {
3031 drag_info.last_trackview = tv;
3034 /************************************************************
3036 ************************************************************/
3038 /* compute the amount of pointer motion in frames, and where
3039 the region would be if we moved it by that much.
3042 if (drag_info.move_threshold_passed) {
3044 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3046 nframes_t sync_frame;
3047 nframes_t sync_offset;
3050 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3052 sync_offset = rv->region()->sync_offset (sync_dir);
3053 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3055 /* we snap if the snap modifier is not enabled.
3058 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3059 snap_to (sync_frame);
3062 if (sync_frame - sync_offset <= sync_frame) {
3063 pending_region_position = sync_frame - (sync_dir*sync_offset);
3065 pending_region_position = 0;
3069 pending_region_position = 0;
3072 if (pending_region_position > max_frames - rv->region()->length()) {
3073 pending_region_position = drag_info.last_frame_position;
3076 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3078 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3080 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3081 to make it appear at the new location.
3084 if (pending_region_position > drag_info.last_frame_position) {
3085 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3087 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3090 drag_info.last_frame_position = pending_region_position;
3097 /* threshold not passed */
3102 /*************************************************************
3104 ************************************************************/
3106 if (x_delta == 0 && (pointer_y_span == 0)) {
3107 /* haven't reached next snap point, and we're not switching
3108 trackviews. nothing to do.
3114 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3116 RegionView* rv2 = (*i);
3118 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3120 double ix1, ix2, iy1, iy2;
3121 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3122 rv2->get_canvas_group()->i2w (ix1, iy1);
3131 /*************************************************************
3133 ************************************************************/
3135 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3136 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3138 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3140 RegionView* rv = (*i);
3141 double ix1, ix2, iy1, iy2;
3142 int32_t temp_pointer_y_span = pointer_y_span;
3144 /* get item BBox, which will be relative to parent. so we have
3145 to query on a child, then convert to world coordinates using
3149 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3150 rv->get_canvas_group()->i2w (ix1, iy1);
3151 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3152 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3153 AudioTimeAxisView* temp_atv;
3155 if ((pointer_y_span != 0) && !clamp_y_axis) {
3158 for (j = height_list.begin(); j!= height_list.end(); j++) {
3159 if (x == canvas_atv->order) {
3160 /* we found the track the region is on */
3161 if (x != original_pointer_order) {
3162 /*this isn't from the same track we're dragging from */
3163 temp_pointer_y_span = canvas_pointer_y_span;
3165 while (temp_pointer_y_span > 0) {
3166 /* we're moving up canvas-wise,
3167 so we need to find the next track height
3169 if (j != height_list.begin()) {
3172 if (x != original_pointer_order) {
3173 /* we're not from the dragged track, so ignore hidden tracks. */
3175 temp_pointer_y_span++;
3179 temp_pointer_y_span--;
3181 while (temp_pointer_y_span < 0) {
3183 if (x != original_pointer_order) {
3185 temp_pointer_y_span--;
3189 if (j != height_list.end()) {
3192 temp_pointer_y_span++;
3194 /* find out where we'll be when we move and set height accordingly */
3196 tvp2 = trackview_by_y_position (iy1 + y_delta);
3197 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3198 rv->set_height (temp_atv->height);
3200 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3201 personally, i think this can confuse things, but never mind.
3204 //const GdkColor& col (temp_atv->view->get_region_color());
3205 //rv->set_color (const_cast<GdkColor&>(col));
3212 /* prevent the regionview from being moved to before
3213 the zero position on the canvas.
3218 if (-x_delta > ix1) {
3221 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3222 x_delta = max_frames - rv->region()->last_frame();
3225 if (drag_info.first_move) {
3227 /* hide any dependent views */
3229 rv->get_time_axis_view().hide_dependent_views (*rv);
3231 /* this is subtle. raising the regionview itself won't help,
3232 because raise_to_top() just puts the item on the top of
3233 its parent's stack. so, we need to put the trackview canvas_display group
3234 on the top, since its parent is the whole canvas.
3237 rv->get_canvas_group()->raise_to_top();
3238 rv->get_time_axis_view().canvas_display->raise_to_top();
3239 cursor_group->raise_to_top();
3241 /* freeze the playlists from notifying till
3245 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3246 if (atv && atv->is_audio_track()) {
3247 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>(atv->get_diskstream()->playlist());
3249 /* only freeze and capture state once */
3251 insert_result = motion_frozen_playlists.insert (pl);
3252 if (insert_result.second) {
3254 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
3258 rv->region()->set_opaque(false);
3261 if (drag_info.brushing) {
3262 mouse_brush_insert_region (rv, pending_region_position);
3264 rv->move (x_delta, y_delta);
3268 if (drag_info.first_move) {
3269 cursor_group->raise_to_top();
3272 drag_info.first_move = false;
3274 if (x_delta != 0 && !drag_info.brushing) {
3275 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3281 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3284 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3285 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3286 bool nocommit = true;
3288 RouteTimeAxisView* atv;
3289 bool regionview_y_movement;
3290 bool regionview_x_movement;
3292 /* first_move is set to false if the regionview has been moved in the
3296 if (drag_info.first_move) {
3303 /* The regionview has been moved at some stage during the grab so we need
3304 to account for any mouse movement between this event and the last one.
3307 region_drag_motion_callback (item, event);
3309 if (drag_info.brushing) {
3310 /* all changes were made during motion event handlers */
3314 /* adjust for track speed */
3317 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3318 if (atv && atv->get_diskstream()) {
3319 speed = atv->get_diskstream()->speed();
3322 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3323 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3325 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3326 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3328 if (regionview_y_movement) {
3330 /* motion between tracks */
3332 list<RegionView*> new_selection;
3334 /* moved to a different audio track. */
3336 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3338 RegionView* rv2 = (*i);
3340 /* the region that used to be in the old playlist is not
3341 moved to the new one - we make a copy of it. as a result,
3342 any existing editor for the region should no longer be
3346 if (!drag_info.copy) {
3347 rv2->hide_region_editor();
3349 new_selection.push_back (rv2);
3353 /* first, freeze the target tracks */
3355 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3357 boost::shared_ptr<Playlist> from_playlist;
3358 boost::shared_ptr<Playlist> to_playlist;
3360 double ix1, ix2, iy1, iy2;
3362 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3363 (*i)->get_canvas_group()->i2w (ix1, iy1);
3364 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3365 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3367 (*i)->region()->set_opaque (true);
3369 from_playlist = (*i)->region()->playlist();
3370 to_playlist = atv2->playlist();
3372 /* the from_playlist was frozen in the "first_move" case
3373 of the motion handler. the insert can fail,
3374 but that doesn't matter. it just means
3375 we already have the playlist in the list.
3378 motion_frozen_playlists.insert (from_playlist);
3380 /* only freeze the to_playlist once */
3382 insert_result = motion_frozen_playlists.insert(to_playlist);
3383 if (insert_result.second) {
3384 to_playlist->freeze();
3385 session->add_command(new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3390 /* now do it again with the actual operations */
3392 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3394 boost::shared_ptr<Playlist> from_playlist;
3395 boost::shared_ptr<Playlist> to_playlist;
3397 double ix1, ix2, iy1, iy2;
3399 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3400 (*i)->get_canvas_group()->i2w (ix1, iy1);
3401 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3402 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3404 from_playlist = (*i)->region()->playlist();
3405 to_playlist = atv2->playlist();
3407 latest_regionview = 0;
3409 where = (nframes_t) (unit_to_frame (ix1) * speed);
3410 boost::shared_ptr<Region> new_region (RegionFactory::create ((*i)->region()));
3412 from_playlist->remove_region (((*i)->region()));
3414 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3415 to_playlist->add_region (new_region, where);
3418 if (latest_regionview) {
3419 selection->add (latest_regionview);
3425 /* motion within a single track */
3427 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3431 if (rv->region()->locked()) {
3435 if (regionview_x_movement) {
3436 double ownspeed = 1.0;
3437 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3439 if (atv && atv->get_diskstream()) {
3440 ownspeed = atv->get_diskstream()->speed();
3443 /* base the new region position on the current position of the regionview.*/
3445 double ix1, ix2, iy1, iy2;
3447 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3448 rv->get_canvas_group()->i2w (ix1, iy1);
3449 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3453 where = rv->region()->position();
3456 rv->get_time_axis_view().reveal_dependent_views (*rv);
3458 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3460 rv->region()->set_position (where, (void *) this);
3461 rv->region()->set_opaque (true);
3466 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3468 session->add_command (new MementoCommand<Playlist>(*((*p).get()), 0, & (*p)->get_state()));
3471 motion_frozen_playlists.clear ();
3474 commit_reversible_command ();
3479 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3481 /* Either add to or set the set the region selection, unless
3482 this is an alignment click (control used)
3485 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3486 TimeAxisView* tv = &rv.get_time_axis_view();
3487 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3489 if (atv && atv->is_audio_track()) {
3490 speed = atv->get_diskstream()->speed();
3493 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3495 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3497 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3499 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3503 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3509 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3515 nframes_t frame_rate;
3522 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3523 case AudioClock::BBT:
3524 session->bbt_time (frame, bbt);
3525 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3528 case AudioClock::SMPTE:
3529 session->smpte_time (frame, smpte);
3530 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3533 case AudioClock::MinSec:
3534 /* XXX this is copied from show_verbose_duration_cursor() */
3535 frame_rate = session->frame_rate();
3536 hours = frame / (frame_rate * 3600);
3537 frame = frame % (frame_rate * 3600);
3538 mins = frame / (frame_rate * 60);
3539 frame = frame % (frame_rate * 60);
3540 secs = (float) frame / (float) frame_rate;
3541 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3545 snprintf (buf, sizeof(buf), "%u", frame);
3549 if (xpos >= 0 && ypos >=0) {
3550 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3553 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3555 show_verbose_canvas_cursor ();
3559 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3566 nframes_t distance, frame_rate;
3568 Meter meter_at_start(session->tempo_map().meter_at(start));
3574 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3575 case AudioClock::BBT:
3576 session->bbt_time (start, sbbt);
3577 session->bbt_time (end, ebbt);
3580 /* XXX this computation won't work well if the
3581 user makes a selection that spans any meter changes.
3584 ebbt.bars -= sbbt.bars;
3585 if (ebbt.beats >= sbbt.beats) {
3586 ebbt.beats -= sbbt.beats;
3589 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3591 if (ebbt.ticks >= sbbt.ticks) {
3592 ebbt.ticks -= sbbt.ticks;
3595 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3598 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3601 case AudioClock::SMPTE:
3602 session->smpte_duration (end - start, smpte);
3603 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3606 case AudioClock::MinSec:
3607 /* XXX this stuff should be elsewhere.. */
3608 distance = end - start;
3609 frame_rate = session->frame_rate();
3610 hours = distance / (frame_rate * 3600);
3611 distance = distance % (frame_rate * 3600);
3612 mins = distance / (frame_rate * 60);
3613 distance = distance % (frame_rate * 60);
3614 secs = (float) distance / (float) frame_rate;
3615 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3619 snprintf (buf, sizeof(buf), "%u", end - start);
3623 if (xpos >= 0 && ypos >=0) {
3624 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3627 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3629 show_verbose_canvas_cursor ();
3633 Editor::collect_new_region_view (RegionView* rv)
3635 latest_regionview = rv;
3639 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3641 if (clicked_regionview == 0) {
3645 /* lets try to create new Region for the selection */
3647 vector<boost::shared_ptr<AudioRegion> > new_regions;
3648 create_region_from_selection (new_regions);
3650 if (new_regions.empty()) {
3654 /* XXX fix me one day to use all new regions */
3656 boost::shared_ptr<Region> region (new_regions.front());
3658 /* add it to the current stream/playlist.
3660 tricky: the streamview for the track will add a new regionview. we will
3661 catch the signal it sends when it creates the regionview to
3662 set the regionview we want to then drag.
3665 latest_regionview = 0;
3666 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3668 /* A selection grab currently creates two undo/redo operations, one for
3669 creating the new region and another for moving it.
3672 begin_reversible_command (_("selection grab"));
3674 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3676 XMLNode *before = &(playlist->get_state());
3677 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3678 XMLNode *after = &(playlist->get_state());
3679 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3681 commit_reversible_command ();
3685 if (latest_regionview == 0) {
3686 /* something went wrong */
3690 /* we need to deselect all other regionviews, and select this one
3691 i'm ignoring undo stuff, because the region creation will take care of it */
3692 selection->set (latest_regionview);
3694 drag_info.item = latest_regionview->get_canvas_group();
3695 drag_info.data = latest_regionview;
3696 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3697 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3701 drag_info.last_trackview = clicked_trackview;
3702 drag_info.last_frame_position = latest_regionview->region()->position();
3703 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3705 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3709 Editor::cancel_selection ()
3711 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3712 (*i)->hide_selection ();
3714 begin_reversible_command (_("cancel selection"));
3715 selection->clear ();
3716 clicked_selection = 0;
3717 commit_reversible_command ();
3721 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3723 nframes_t start = 0;
3730 drag_info.item = item;
3731 drag_info.motion_callback = &Editor::drag_selection;
3732 drag_info.finished_callback = &Editor::end_selection_op;
3737 case CreateSelection:
3738 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3739 drag_info.copy = true;
3741 drag_info.copy = false;
3743 start_grab (event, selector_cursor);
3746 case SelectionStartTrim:
3747 if (clicked_trackview) {
3748 clicked_trackview->order_selection_trims (item, true);
3750 start_grab (event, trimmer_cursor);
3751 start = selection->time[clicked_selection].start;
3752 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3755 case SelectionEndTrim:
3756 if (clicked_trackview) {
3757 clicked_trackview->order_selection_trims (item, false);
3759 start_grab (event, trimmer_cursor);
3760 end = selection->time[clicked_selection].end;
3761 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3765 start = selection->time[clicked_selection].start;
3767 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3771 if (selection_op == SelectionMove) {
3772 show_verbose_time_cursor(start, 10);
3774 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3779 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3781 nframes_t start = 0;
3784 nframes_t pending_position;
3786 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3787 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3790 pending_position = 0;
3793 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3794 snap_to (pending_position);
3797 /* only alter selection if the current frame is
3798 different from the last frame position (adjusted)
3801 if (pending_position == drag_info.last_pointer_frame) return;
3803 switch (selection_op) {
3804 case CreateSelection:
3806 if (drag_info.first_move) {
3807 snap_to (drag_info.grab_frame);
3810 if (pending_position < drag_info.grab_frame) {
3811 start = pending_position;
3812 end = drag_info.grab_frame;
3814 end = pending_position;
3815 start = drag_info.grab_frame;
3818 /* first drag: Either add to the selection
3819 or create a new selection->
3822 if (drag_info.first_move) {
3824 begin_reversible_command (_("range selection"));
3826 if (drag_info.copy) {
3827 /* adding to the selection */
3828 clicked_selection = selection->add (start, end);
3829 drag_info.copy = false;
3831 /* new selection-> */
3832 clicked_selection = selection->set (clicked_trackview, start, end);
3837 case SelectionStartTrim:
3839 if (drag_info.first_move) {
3840 begin_reversible_command (_("trim selection start"));
3843 start = selection->time[clicked_selection].start;
3844 end = selection->time[clicked_selection].end;
3846 if (pending_position > end) {
3849 start = pending_position;
3853 case SelectionEndTrim:
3855 if (drag_info.first_move) {
3856 begin_reversible_command (_("trim selection end"));
3859 start = selection->time[clicked_selection].start;
3860 end = selection->time[clicked_selection].end;
3862 if (pending_position < start) {
3865 end = pending_position;
3872 if (drag_info.first_move) {
3873 begin_reversible_command (_("move selection"));
3876 start = selection->time[clicked_selection].start;
3877 end = selection->time[clicked_selection].end;
3879 length = end - start;
3881 start = pending_position;
3884 end = start + length;
3889 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3890 start_canvas_autoscroll (1);
3894 selection->replace (clicked_selection, start, end);
3897 drag_info.last_pointer_frame = pending_position;
3898 drag_info.first_move = false;
3900 if (selection_op == SelectionMove) {
3901 show_verbose_time_cursor(start, 10);
3903 show_verbose_time_cursor(pending_position, 10);
3908 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3910 if (!drag_info.first_move) {
3911 drag_selection (item, event);
3912 /* XXX this is not object-oriented programming at all. ick */
3913 if (selection->time.consolidate()) {
3914 selection->TimeChanged ();
3916 commit_reversible_command ();
3918 /* just a click, no pointer movement.*/
3920 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3922 selection->clear_time();
3927 /* XXX what happens if its a music selection? */
3928 session->set_audio_range (selection->time);
3929 stop_canvas_autoscroll ();
3933 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3936 TimeAxisView* tvp = clicked_trackview;
3937 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3939 if (tv && tv->is_audio_track()) {
3940 speed = tv->get_diskstream()->speed();
3943 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3944 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3945 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3947 motion_frozen_playlists.clear();
3949 //drag_info.item = clicked_regionview->get_name_highlight();
3950 drag_info.item = item;
3951 drag_info.motion_callback = &Editor::trim_motion_callback;
3952 drag_info.finished_callback = &Editor::trim_finished_callback;
3954 start_grab (event, trimmer_cursor);
3956 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3957 trim_op = ContentsTrim;
3959 /* These will get overridden for a point trim.*/
3960 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3961 /* closer to start */
3962 trim_op = StartTrim;
3963 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3971 show_verbose_time_cursor(region_start, 10);
3974 show_verbose_time_cursor(region_end, 10);
3977 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3983 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3985 RegionView* rv = clicked_regionview;
3986 nframes_t frame_delta = 0;
3987 bool left_direction;
3988 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3990 /* snap modifier works differently here..
3991 its' current state has to be passed to the
3992 various trim functions in order to work properly
3996 TimeAxisView* tvp = clicked_trackview;
3997 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3998 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4000 if (tv && tv->is_audio_track()) {
4001 speed = tv->get_diskstream()->speed();
4004 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4005 left_direction = true;
4007 left_direction = false;
4011 snap_to (drag_info.current_pointer_frame);
4014 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4018 if (drag_info.first_move) {
4024 trim_type = "Region start trim";
4027 trim_type = "Region end trim";
4030 trim_type = "Region content trim";
4034 begin_reversible_command (trim_type);
4036 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4037 (*i)->region()->set_opaque(false);
4038 (*i)->region()->freeze ();
4040 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4042 arv->temporarily_hide_envelope ();
4044 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4045 insert_result = motion_frozen_playlists.insert (pl);
4046 if (insert_result.second) {
4047 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4052 if (left_direction) {
4053 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4055 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4060 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4063 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4064 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4070 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4073 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4074 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4081 bool swap_direction = false;
4083 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4084 swap_direction = true;
4087 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4088 i != selection->regions.by_layer().end(); ++i)
4090 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4098 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4101 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4104 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4108 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4109 drag_info.first_move = false;
4113 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4115 boost::shared_ptr<Region> region (rv.region());
4117 if (region->locked()) {
4121 nframes_t new_bound;
4124 TimeAxisView* tvp = clicked_trackview;
4125 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4127 if (tv && tv->is_audio_track()) {
4128 speed = tv->get_diskstream()->speed();
4131 if (left_direction) {
4132 if (swap_direction) {
4133 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4135 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4138 if (swap_direction) {
4139 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4141 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4146 snap_to (new_bound);
4148 region->trim_start ((nframes_t) (new_bound * speed), this);
4149 rv.region_changed (StartChanged);
4153 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4155 boost::shared_ptr<Region> region (rv.region());
4157 if (region->locked()) {
4161 nframes_t new_bound;
4164 TimeAxisView* tvp = clicked_trackview;
4165 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4167 if (tv && tv->is_audio_track()) {
4168 speed = tv->get_diskstream()->speed();
4171 if (left_direction) {
4172 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4174 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4178 snap_to (new_bound, (left_direction ? 0 : 1));
4181 region->trim_front ((nframes_t) (new_bound * speed), this);
4183 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4187 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4189 boost::shared_ptr<Region> region (rv.region());
4191 if (region->locked()) {
4195 nframes_t new_bound;
4198 TimeAxisView* tvp = clicked_trackview;
4199 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4201 if (tv && tv->is_audio_track()) {
4202 speed = tv->get_diskstream()->speed();
4205 if (left_direction) {
4206 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4208 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4212 snap_to (new_bound);
4214 region->trim_end ((nframes_t) (new_bound * speed), this);
4215 rv.region_changed (LengthChanged);
4219 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4221 if (!drag_info.first_move) {
4222 trim_motion_callback (item, event);
4224 if (!clicked_regionview->get_selected()) {
4225 thaw_region_after_trim (*clicked_regionview);
4228 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4229 i != selection->regions.by_layer().end(); ++i)
4231 thaw_region_after_trim (**i);
4232 (*i)->region()->set_opaque(true);
4236 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4238 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4241 motion_frozen_playlists.clear ();
4243 commit_reversible_command();
4245 /* no mouse movement */
4251 Editor::point_trim (GdkEvent* event)
4253 RegionView* rv = clicked_regionview;
4254 nframes_t new_bound = drag_info.current_pointer_frame;
4256 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4257 snap_to (new_bound);
4260 /* Choose action dependant on which button was pressed */
4261 switch (event->button.button) {
4263 trim_op = StartTrim;
4264 begin_reversible_command (_("Start point trim"));
4266 if (rv->get_selected()) {
4268 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4269 i != selection->regions.by_layer().end(); ++i)
4271 if (!(*i)->region()->locked()) {
4272 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4273 XMLNode &before = pl->get_state();
4274 (*i)->region()->trim_front (new_bound, this);
4275 XMLNode &after = pl->get_state();
4276 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4282 if (!rv->region()->locked()) {
4283 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4284 XMLNode &before = pl->get_state();
4285 rv->region()->trim_front (new_bound, this);
4286 XMLNode &after = pl->get_state();
4287 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4291 commit_reversible_command();
4296 begin_reversible_command (_("End point trim"));
4298 if (rv->get_selected()) {
4300 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4302 if (!(*i)->region()->locked()) {
4303 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4304 XMLNode &before = pl->get_state();
4305 (*i)->region()->trim_end (new_bound, this);
4306 XMLNode &after = pl->get_state();
4307 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4313 if (!rv->region()->locked()) {
4314 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4315 XMLNode &before = pl->get_state();
4316 rv->region()->trim_end (new_bound, this);
4317 XMLNode &after = pl->get_state();
4318 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4322 commit_reversible_command();
4331 Editor::thaw_region_after_trim (RegionView& rv)
4333 boost::shared_ptr<Region> region (rv.region());
4335 if (region->locked()) {
4339 region->thaw (_("trimmed region"));
4340 XMLNode &after = region->playlist()->get_state();
4341 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4343 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4345 arv->unhide_envelope ();
4349 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4354 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4355 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4359 Location* location = find_location_from_marker (marker, is_start);
4360 location->set_hidden (true, this);
4365 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4371 drag_info.item = item;
4372 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4373 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4375 range_marker_op = op;
4377 if (!temp_location) {
4378 temp_location = new Location;
4382 case CreateRangeMarker:
4383 case CreateTransportMarker:
4385 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4386 drag_info.copy = true;
4388 drag_info.copy = false;
4390 start_grab (event, selector_cursor);
4394 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4399 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4401 nframes_t start = 0;
4403 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4405 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4406 snap_to (drag_info.current_pointer_frame);
4409 /* only alter selection if the current frame is
4410 different from the last frame position.
4413 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4415 switch (range_marker_op) {
4416 case CreateRangeMarker:
4417 case CreateTransportMarker:
4418 if (drag_info.first_move) {
4419 snap_to (drag_info.grab_frame);
4422 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4423 start = drag_info.current_pointer_frame;
4424 end = drag_info.grab_frame;
4426 end = drag_info.current_pointer_frame;
4427 start = drag_info.grab_frame;
4430 /* first drag: Either add to the selection
4431 or create a new selection.
4434 if (drag_info.first_move) {
4436 temp_location->set (start, end);
4440 update_marker_drag_item (temp_location);
4441 range_marker_drag_rect->show();
4442 range_marker_drag_rect->raise_to_top();
4448 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4449 start_canvas_autoscroll (1);
4453 temp_location->set (start, end);
4455 double x1 = frame_to_pixel (start);
4456 double x2 = frame_to_pixel (end);
4457 crect->property_x1() = x1;
4458 crect->property_x2() = x2;
4460 update_marker_drag_item (temp_location);
4463 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4464 drag_info.first_move = false;
4466 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4471 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4473 Location * newloc = 0;
4476 if (!drag_info.first_move) {
4477 drag_range_markerbar_op (item, event);
4479 switch (range_marker_op) {
4480 case CreateRangeMarker:
4482 begin_reversible_command (_("new range marker"));
4483 XMLNode &before = session->locations()->get_state();
4484 session->locations()->next_available_name(rangename,"unnamed");
4485 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4486 session->locations()->add (newloc, true);
4487 XMLNode &after = session->locations()->get_state();
4488 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4489 commit_reversible_command ();
4491 range_bar_drag_rect->hide();
4492 range_marker_drag_rect->hide();
4496 case CreateTransportMarker:
4497 // popup menu to pick loop or punch
4498 new_transport_marker_context_menu (&event->button, item);
4503 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4505 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4510 start = session->locations()->first_mark_before (drag_info.grab_frame);
4511 end = session->locations()->first_mark_after (drag_info.grab_frame);
4513 if (end == max_frames) {
4514 end = session->current_end_frame ();
4518 start = session->current_start_frame ();
4521 switch (mouse_mode) {
4523 /* find the two markers on either side and then make the selection from it */
4524 cerr << "select between " << start << " .. " << end << endl;
4525 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4529 /* find the two markers on either side of the click and make the range out of it */
4530 selection->set (0, start, end);
4539 stop_canvas_autoscroll ();
4545 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4547 drag_info.item = item;
4548 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4549 drag_info.finished_callback = &Editor::end_mouse_zoom;
4551 start_grab (event, zoom_cursor);
4553 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4557 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4562 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4563 snap_to (drag_info.current_pointer_frame);
4565 if (drag_info.first_move) {
4566 snap_to (drag_info.grab_frame);
4570 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4572 /* base start and end on initial click position */
4573 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4574 start = drag_info.current_pointer_frame;
4575 end = drag_info.grab_frame;
4577 end = drag_info.current_pointer_frame;
4578 start = drag_info.grab_frame;
4583 if (drag_info.first_move) {
4585 zoom_rect->raise_to_top();
4588 reposition_zoom_rect(start, end);
4590 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4591 drag_info.first_move = false;
4593 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4598 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4600 if (!drag_info.first_move) {
4601 drag_mouse_zoom (item, event);
4603 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4604 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4606 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4609 temporal_zoom_to_frame (false, drag_info.grab_frame);
4611 temporal_zoom_step (false);
4612 center_screen (drag_info.grab_frame);
4620 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4622 double x1 = frame_to_pixel (start);
4623 double x2 = frame_to_pixel (end);
4624 double y2 = full_canvas_height - 1.0;
4626 zoom_rect->property_x1() = x1;
4627 zoom_rect->property_y1() = 1.0;
4628 zoom_rect->property_x2() = x2;
4629 zoom_rect->property_y2() = y2;
4633 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4635 drag_info.item = item;
4636 drag_info.motion_callback = &Editor::drag_rubberband_select;
4637 drag_info.finished_callback = &Editor::end_rubberband_select;
4639 start_grab (event, cross_hair_cursor);
4641 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4645 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4652 /* use a bigger drag threshold than the default */
4654 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4658 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4659 // snap_to (drag_info.current_pointer_frame);
4661 // if (drag_info.first_move) {
4662 // snap_to (drag_info.grab_frame);
4667 /* base start and end on initial click position */
4668 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4669 start = drag_info.current_pointer_frame;
4670 end = drag_info.grab_frame;
4672 end = drag_info.current_pointer_frame;
4673 start = drag_info.grab_frame;
4676 if (drag_info.current_pointer_y < drag_info.grab_y) {
4677 y1 = drag_info.current_pointer_y;
4678 y2 = drag_info.grab_y;
4681 y2 = drag_info.current_pointer_y;
4682 y1 = drag_info.grab_y;
4686 if (start != end || y1 != y2) {
4688 double x1 = frame_to_pixel (start);
4689 double x2 = frame_to_pixel (end);
4691 rubberband_rect->property_x1() = x1;
4692 rubberband_rect->property_y1() = y1;
4693 rubberband_rect->property_x2() = x2;
4694 rubberband_rect->property_y2() = y2;
4696 rubberband_rect->show();
4697 rubberband_rect->raise_to_top();
4699 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4700 drag_info.first_move = false;
4702 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4707 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4709 if (!drag_info.first_move) {
4711 drag_rubberband_select (item, event);
4714 if (drag_info.current_pointer_y < drag_info.grab_y) {
4715 y1 = drag_info.current_pointer_y;
4716 y2 = drag_info.grab_y;
4719 y2 = drag_info.current_pointer_y;
4720 y1 = drag_info.grab_y;
4724 Selection::Operation op = Keyboard::selection_type (event->button.state);
4727 begin_reversible_command (_("select regions"));
4729 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4730 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4732 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4736 commit_reversible_command ();
4740 selection->clear_regions();
4741 selection->clear_points ();
4742 selection->clear_lines ();
4745 rubberband_rect->hide();
4750 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4752 using namespace Gtkmm2ext;
4754 ArdourPrompter prompter (false);
4756 prompter.set_prompt (_("Name for region:"));
4757 prompter.set_initial_text (clicked_regionview->region()->name());
4758 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4759 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4760 prompter.show_all ();
4761 switch (prompter.run ()) {
4762 case Gtk::RESPONSE_ACCEPT:
4764 prompter.get_result(str);
4766 clicked_regionview->region()->set_name (str);
4774 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4776 drag_info.item = item;
4777 drag_info.motion_callback = &Editor::time_fx_motion;
4778 drag_info.finished_callback = &Editor::end_time_fx;
4782 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4786 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4788 RegionView* rv = clicked_regionview;
4790 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4791 snap_to (drag_info.current_pointer_frame);
4794 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4798 if (drag_info.current_pointer_frame > rv->region()->position()) {
4799 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4802 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4803 drag_info.first_move = false;
4805 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4809 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4811 clicked_regionview->get_time_axis_view().hide_timestretch ();
4813 if (drag_info.first_move) {
4817 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4818 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4820 begin_reversible_command (_("timestretch"));
4822 if (run_timestretch (selection->regions, percentage) == 0) {
4823 session->commit_reversible_command ();
4828 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4830 /* no brushing without a useful snap setting */
4833 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4836 switch (snap_mode) {
4838 return; /* can't work because it allows region to be placed anywhere */
4843 switch (snap_type) {
4846 case SnapToEditCursor:
4853 /* don't brush a copy over the original */
4855 if (pos == rv->region()->position()) {
4859 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4861 if (atv == 0 || !atv->is_audio_track()) {
4865 boost::shared_ptr<Playlist> playlist = atv->playlist();
4866 double speed = atv->get_diskstream()->speed();
4868 XMLNode &before = playlist->get_state();
4869 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4870 XMLNode &after = playlist->get_state();
4871 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4873 // playlist is frozen, so we have to update manually
4875 playlist->Modified(); /* EMIT SIGNAL */
4879 Editor::track_height_step_timeout ()
4882 struct timeval delta;
4884 gettimeofday (&now, 0);
4885 timersub (&now, &last_track_height_step_timestamp, &delta);
4887 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4888 current_stepping_trackview = 0;