2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <pbd/error.h>
30 #include <gtkmm2ext/utils.h>
31 #include <pbd/memento_command.h>
33 #include "ardour_ui.h"
35 #include "time_axis_view.h"
36 #include "audio_time_axis.h"
37 #include "audio_region_view.h"
39 #include "streamview.h"
40 #include "region_gain_line.h"
41 #include "automation_time_axis.h"
44 #include "selection.h"
47 #include "rgb_macros.h"
49 #include <ardour/types.h>
50 #include <ardour/route.h>
51 #include <ardour/audio_track.h>
52 #include <ardour/audio_diskstream.h>
53 #include <ardour/playlist.h>
54 #include <ardour/audioplaylist.h>
55 #include <ardour/audioregion.h>
56 #include <ardour/dB.h>
57 #include <ardour/utils.h>
58 #include <ardour/region_factory.h>
65 using namespace ARDOUR;
69 using namespace Editing;
72 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
86 switch (event->type) {
87 case GDK_BUTTON_RELEASE:
88 case GDK_BUTTON_PRESS:
89 case GDK_2BUTTON_PRESS:
90 case GDK_3BUTTON_PRESS:
91 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
93 case GDK_MOTION_NOTIFY:
94 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
96 case GDK_ENTER_NOTIFY:
97 case GDK_LEAVE_NOTIFY:
98 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
101 case GDK_KEY_RELEASE:
102 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
105 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
109 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
110 position is negative (as can be the case with motion events in particular),
111 the frame location is always positive.
114 return pixel_to_frame (*pcx);
118 Editor::mouse_mode_toggled (MouseMode m)
120 if (ignore_mouse_mode_toggle) {
126 if (mouse_select_button.get_active()) {
132 if (mouse_move_button.get_active()) {
138 if (mouse_gain_button.get_active()) {
144 if (mouse_zoom_button.get_active()) {
150 if (mouse_timefx_button.get_active()) {
156 if (mouse_audition_button.get_active()) {
167 Editor::set_mouse_mode (MouseMode m, bool force)
169 if (drag_info.item) {
173 if (!force && m == mouse_mode) {
181 if (mouse_mode != MouseRange) {
183 /* in all modes except range, hide the range selection,
184 show the object (region) selection.
187 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
188 (*i)->set_should_show_selection (true);
190 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
191 (*i)->hide_selection ();
197 in range mode,show the range selection.
200 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
201 if ((*i)->get_selected()) {
202 (*i)->show_selection (selection->time);
207 /* XXX the hack of unsetting all other buttongs should go
208 away once GTK2 allows us to use regular radio buttons drawn like
209 normal buttons, rather than my silly GroupedButton hack.
212 ignore_mouse_mode_toggle = true;
214 switch (mouse_mode) {
216 mouse_select_button.set_active (true);
217 current_canvas_cursor = selector_cursor;
221 mouse_move_button.set_active (true);
222 current_canvas_cursor = grabber_cursor;
226 mouse_gain_button.set_active (true);
227 current_canvas_cursor = cross_hair_cursor;
231 mouse_zoom_button.set_active (true);
232 current_canvas_cursor = zoom_cursor;
236 mouse_timefx_button.set_active (true);
237 current_canvas_cursor = time_fx_cursor; // just use playhead
241 mouse_audition_button.set_active (true);
242 current_canvas_cursor = speaker_cursor;
246 ignore_mouse_mode_toggle = false;
249 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
254 Editor::step_mouse_mode (bool next)
256 switch (current_mouse_mode()) {
258 if (next) set_mouse_mode (MouseRange);
259 else set_mouse_mode (MouseTimeFX);
263 if (next) set_mouse_mode (MouseZoom);
264 else set_mouse_mode (MouseObject);
268 if (next) set_mouse_mode (MouseGain);
269 else set_mouse_mode (MouseRange);
273 if (next) set_mouse_mode (MouseTimeFX);
274 else set_mouse_mode (MouseZoom);
278 if (next) set_mouse_mode (MouseAudition);
279 else set_mouse_mode (MouseGain);
283 if (next) set_mouse_mode (MouseObject);
284 else set_mouse_mode (MouseTimeFX);
290 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
296 /* in object/audition/timefx mode, any button press sets
297 the selection if the object can be selected. this is a
298 bit of hack, because we want to avoid this if the
299 mouse operation is a region alignment.
301 note: not dbl-click or triple-click
304 if (((mouse_mode != MouseObject) &&
305 (mouse_mode != MouseAudition || item_type != RegionItem) &&
306 (mouse_mode != MouseTimeFX || item_type != RegionItem)) ||
307 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
312 Selection::Operation op = Keyboard::selection_type (event->button.state);
313 bool press = (event->type == GDK_BUTTON_PRESS);
315 begin_reversible_command (_("select on click"));
319 /* XXX make tying track/region selection optional */
320 c1 = set_selected_track_from_click (op, true);
321 c2 = set_selected_regionview_from_click (press, op, true);
325 case RegionViewNameHighlight:
327 /* XXX make tying track/region selection optional */
328 c1 = set_selected_track_from_click (op, true);
329 c2 = set_selected_regionview_from_click (press, op, true);
333 case GainAutomationControlPointItem:
334 case PanAutomationControlPointItem:
335 case RedirectAutomationControlPointItem:
336 /* XXX make tying track/region selection optional */
337 c1 = set_selected_track_from_click (op, true);
338 c2 = set_selected_control_point_from_click (op, false);
343 commit = set_selected_track_from_click (op, true);
346 case AutomationTrackItem:
347 commit = set_selected_track_from_click (op, true);
354 #define SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
355 #ifdef SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
356 /* in range mode, button 1/2/3 press potentially selects a track */
358 if (mouse_mode == MouseRange &&
359 event->type == GDK_BUTTON_PRESS &&
360 event->button.button <= 3) {
365 case AutomationTrackItem:
366 commit = set_selected_track_from_click (op, true);
375 commit_reversible_command ();
380 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
382 nframes_t where = event_frame (event, 0, 0);
384 track_canvas.grab_focus();
386 if (session && session->actively_recording()) {
390 button_selection (item, event, item_type);
392 if (drag_info.item == 0 &&
393 (Keyboard::is_delete_event (&event->button) ||
394 Keyboard::is_context_menu_event (&event->button) ||
395 Keyboard::is_edit_event (&event->button))) {
397 /* handled by button release */
401 switch (event->button.button) {
404 if (event->type == GDK_BUTTON_PRESS) {
406 if (drag_info.item) {
407 drag_info.item->ungrab (event->button.time);
410 /* single mouse clicks on any of these item types operate
411 independent of mouse mode, mostly because they are
412 not on the main track canvas or because we want
418 case PlayheadCursorItem:
419 start_cursor_grab (item, event);
423 if (Keyboard::modifier_state_equals (event->button.state,
424 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
425 hide_marker (item, event);
427 start_marker_grab (item, event);
431 case TempoMarkerItem:
432 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
433 start_tempo_marker_copy_grab (item, event);
435 start_tempo_marker_grab (item, event);
439 case MeterMarkerItem:
440 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
441 start_meter_marker_copy_grab (item, event);
443 start_meter_marker_grab (item, event);
453 case RangeMarkerBarItem:
454 start_range_markerbar_op (item, event, CreateRangeMarker);
458 case TransportMarkerBarItem:
459 start_range_markerbar_op (item, event, CreateTransportMarker);
468 switch (mouse_mode) {
471 case StartSelectionTrimItem:
472 start_selection_op (item, event, SelectionStartTrim);
475 case EndSelectionTrimItem:
476 start_selection_op (item, event, SelectionEndTrim);
480 if (Keyboard::modifier_state_contains
481 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
482 // contains and not equals because I can't use alt as a modifier alone.
483 start_selection_grab (item, event);
484 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
485 /* grab selection for moving */
486 start_selection_op (item, event, SelectionMove);
489 /* this was debated, but decided the more common action was to
490 make a new selection */
491 start_selection_op (item, event, CreateSelection);
496 start_selection_op (item, event, CreateSelection);
502 if (Keyboard::modifier_state_contains (event->button.state,
503 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt))
504 && event->type == GDK_BUTTON_PRESS) {
506 start_rubberband_select (item, event);
508 } else if (event->type == GDK_BUTTON_PRESS) {
511 case FadeInHandleItem:
512 start_fade_in_grab (item, event);
515 case FadeOutHandleItem:
516 start_fade_out_grab (item, event);
520 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
521 start_region_copy_grab (item, event);
522 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
523 start_region_brush_grab (item, event);
525 start_region_grab (item, event);
529 case RegionViewNameHighlight:
530 start_trim (item, event);
535 /* rename happens on edit clicks */
536 start_trim (clicked_regionview->get_name_highlight(), event);
540 case GainAutomationControlPointItem:
541 case PanAutomationControlPointItem:
542 case RedirectAutomationControlPointItem:
543 start_control_point_grab (item, event);
547 case GainAutomationLineItem:
548 case PanAutomationLineItem:
549 case RedirectAutomationLineItem:
550 start_line_grab_from_line (item, event);
555 case AutomationTrackItem:
556 start_rubberband_select (item, event);
559 /* <CMT Additions> */
560 case ImageFrameHandleStartItem:
561 imageframe_start_handle_op(item, event) ;
564 case ImageFrameHandleEndItem:
565 imageframe_end_handle_op(item, event) ;
568 case MarkerViewHandleStartItem:
569 markerview_item_start_handle_op(item, event) ;
572 case MarkerViewHandleEndItem:
573 markerview_item_end_handle_op(item, event) ;
576 /* </CMT Additions> */
578 /* <CMT Additions> */
580 start_markerview_grab(item, event) ;
583 start_imageframe_grab(item, event) ;
585 /* </CMT Additions> */
601 // start_line_grab_from_regionview (item, event);
604 case GainControlPointItem:
605 start_control_point_grab (item, event);
609 start_line_grab_from_line (item, event);
612 case GainAutomationControlPointItem:
613 case PanAutomationControlPointItem:
614 case RedirectAutomationControlPointItem:
615 start_control_point_grab (item, event);
626 case GainAutomationControlPointItem:
627 case PanAutomationControlPointItem:
628 case RedirectAutomationControlPointItem:
629 start_control_point_grab (item, event);
632 case GainAutomationLineItem:
633 case PanAutomationLineItem:
634 case RedirectAutomationLineItem:
635 start_line_grab_from_line (item, event);
639 // XXX need automation mode to identify which
641 // start_line_grab_from_regionview (item, event);
651 if (event->type == GDK_BUTTON_PRESS) {
652 start_mouse_zoom (item, event);
659 if (item_type == RegionItem) {
660 start_time_fx (item, event);
665 /* handled in release */
674 switch (mouse_mode) {
676 if (event->type == GDK_BUTTON_PRESS) {
679 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
680 start_region_copy_grab (item, event);
682 start_region_grab (item, event);
686 case GainAutomationControlPointItem:
687 case PanAutomationControlPointItem:
688 case RedirectAutomationControlPointItem:
689 start_control_point_grab (item, event);
700 case RegionViewNameHighlight:
701 start_trim (item, event);
706 start_trim (clicked_regionview->get_name_highlight(), event);
717 if (event->type == GDK_BUTTON_PRESS) {
718 /* relax till release */
725 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
726 temporal_zoom_session();
728 temporal_zoom_to_frame (true, event_frame(event));
743 switch (mouse_mode) {
745 //temporal_zoom_to_frame (true, where);
746 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
747 temporal_zoom_to_frame (true, where);
750 temporal_zoom_step (true);
755 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
756 scroll_backward (0.6f);
759 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
760 scroll_tracks_up_line ();
762 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
763 if (clicked_trackview) {
764 if (!current_stepping_trackview) {
765 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
766 current_stepping_trackview = clicked_trackview;
768 gettimeofday (&last_track_height_step_timestamp, 0);
769 current_stepping_trackview->step_height (true);
772 else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
773 temporal_zoom_to_frame (true, where);
780 switch (mouse_mode) {
782 // temporal_zoom_to_frame (false, where);
783 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
784 temporal_zoom_to_frame (false, where);
787 temporal_zoom_step (false);
792 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
793 scroll_forward (0.6f);
796 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
797 scroll_tracks_down_line ();
799 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
800 if (clicked_trackview) {
801 if (!current_stepping_trackview) {
802 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
803 current_stepping_trackview = clicked_trackview;
805 gettimeofday (&last_track_height_step_timestamp, 0);
806 current_stepping_trackview->step_height (false);
808 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
809 temporal_zoom_to_frame (false, where);
824 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
826 nframes_t where = event_frame (event, 0, 0);
828 /* no action if we're recording */
830 if (session && session->actively_recording()) {
834 /* first, see if we're finishing a drag ... */
836 if (drag_info.item) {
837 if (end_grab (item, event)) {
838 /* grab dragged, so do nothing else */
843 button_selection (item, event, item_type);
845 /* edit events get handled here */
847 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
853 case TempoMarkerItem:
854 edit_tempo_marker (item);
857 case MeterMarkerItem:
858 edit_meter_marker (item);
862 if (clicked_regionview->name_active()) {
863 return mouse_rename_region (item, event);
873 /* context menu events get handled here */
875 if (Keyboard::is_context_menu_event (&event->button)) {
877 if (drag_info.item == 0) {
879 /* no matter which button pops up the context menu, tell the menu
880 widget to use button 1 to drive menu selection.
885 case FadeInHandleItem:
887 case FadeOutHandleItem:
888 popup_fade_context_menu (1, event->button.time, item, item_type);
892 popup_track_context_menu (1, event->button.time, item_type, false, where);
896 case RegionViewNameHighlight:
898 popup_track_context_menu (1, event->button.time, item_type, false, where);
902 popup_track_context_menu (1, event->button.time, item_type, true, where);
905 case AutomationTrackItem:
906 popup_track_context_menu (1, event->button.time, item_type, false, where);
910 case RangeMarkerBarItem:
911 case TransportMarkerBarItem:
914 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
918 marker_context_menu (&event->button, item);
921 case TempoMarkerItem:
922 tm_marker_context_menu (&event->button, item);
925 case MeterMarkerItem:
926 tm_marker_context_menu (&event->button, item);
929 case CrossfadeViewItem:
930 popup_track_context_menu (1, event->button.time, item_type, false, where);
933 /* <CMT Additions> */
935 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
937 case ImageFrameTimeAxisItem:
938 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
941 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
943 case MarkerTimeAxisItem:
944 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
946 /* <CMT Additions> */
957 /* delete events get handled here */
959 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
962 case TempoMarkerItem:
963 remove_tempo_marker (item);
966 case MeterMarkerItem:
967 remove_meter_marker (item);
971 remove_marker (*item, event);
975 if (mouse_mode == MouseObject) {
976 remove_clicked_region ();
980 case GainControlPointItem:
981 if (mouse_mode == MouseGain) {
982 remove_gain_control_point (item, event);
986 case GainAutomationControlPointItem:
987 case PanAutomationControlPointItem:
988 case RedirectAutomationControlPointItem:
989 remove_control_point (item, event);
998 switch (event->button.button) {
1001 switch (item_type) {
1002 /* see comments in button_press_handler */
1003 case EditCursorItem:
1004 case PlayheadCursorItem:
1007 case GainAutomationLineItem:
1008 case PanAutomationLineItem:
1009 case RedirectAutomationLineItem:
1010 case StartSelectionTrimItem:
1011 case EndSelectionTrimItem:
1015 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1016 snap_to (where, 0, true);
1018 mouse_add_new_marker (where);
1022 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1025 mouse_add_new_tempo_event (where);
1029 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1037 switch (mouse_mode) {
1039 switch (item_type) {
1040 case AutomationTrackItem:
1041 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1055 // Gain only makes sense for audio regions
1056 if ( ! dynamic_cast<AudioRegionView*>(clicked_regionview))
1059 switch (item_type) {
1061 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1065 case AutomationTrackItem:
1066 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1067 add_automation_event (item, event, where, event->button.y);
1076 switch (item_type) {
1078 audition_selected_region ();
1095 switch (mouse_mode) {
1098 switch (item_type) {
1100 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1102 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1105 // Button2 click is unused
1118 // x_style_paste (where, 1.0);
1138 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1144 switch (item_type) {
1145 case GainControlPointItem:
1146 if (mouse_mode == MouseGain) {
1147 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1148 cp->set_visible (true);
1152 at_y = cp->get_y ();
1153 cp->item->i2w (at_x, at_y);
1157 fraction = 1.0 - (cp->get_y() / cp->line.height());
1159 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1160 show_verbose_canvas_cursor ();
1162 if (is_drawable()) {
1163 track_canvas.get_window()->set_cursor (*fader_cursor);
1168 case GainAutomationControlPointItem:
1169 case PanAutomationControlPointItem:
1170 case RedirectAutomationControlPointItem:
1171 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1172 cp->set_visible (true);
1176 at_y = cp->get_y ();
1177 cp->item->i2w (at_x, at_y);
1181 fraction = 1.0 - (cp->get_y() / cp->line.height());
1183 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1184 show_verbose_canvas_cursor ();
1186 if (is_drawable()) {
1187 track_canvas.get_window()->set_cursor (*fader_cursor);
1192 if (mouse_mode == MouseGain) {
1193 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1195 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1196 if (is_drawable()) {
1197 track_canvas.get_window()->set_cursor (*fader_cursor);
1202 case GainAutomationLineItem:
1203 case RedirectAutomationLineItem:
1204 case PanAutomationLineItem:
1206 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1208 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1210 if (is_drawable()) {
1211 track_canvas.get_window()->set_cursor (*fader_cursor);
1215 case RegionViewNameHighlight:
1216 if (is_drawable() && mouse_mode == MouseObject) {
1217 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1221 case StartSelectionTrimItem:
1222 case EndSelectionTrimItem:
1223 /* <CMT Additions> */
1224 case ImageFrameHandleStartItem:
1225 case ImageFrameHandleEndItem:
1226 case MarkerViewHandleStartItem:
1227 case MarkerViewHandleEndItem:
1228 /* </CMT Additions> */
1230 if (is_drawable()) {
1231 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1235 case EditCursorItem:
1236 case PlayheadCursorItem:
1237 if (is_drawable()) {
1238 track_canvas.get_window()->set_cursor (*grabber_cursor);
1242 case RegionViewName:
1244 /* when the name is not an active item, the entire name highlight is for trimming */
1246 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1247 if (mouse_mode == MouseObject && is_drawable()) {
1248 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1254 case AutomationTrackItem:
1255 if (is_drawable()) {
1256 Gdk::Cursor *cursor;
1257 switch (mouse_mode) {
1259 cursor = selector_cursor;
1262 cursor = zoom_cursor;
1265 cursor = cross_hair_cursor;
1269 track_canvas.get_window()->set_cursor (*cursor);
1271 AutomationTimeAxisView* atv;
1272 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1273 clear_entered_track = false;
1274 set_entered_track (atv);
1280 case RangeMarkerBarItem:
1281 case TransportMarkerBarItem:
1284 if (is_drawable()) {
1285 time_canvas.get_window()->set_cursor (*timebar_cursor);
1290 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1293 marker->set_color_rgba (color_map[cEnteredMarker]);
1295 case MeterMarkerItem:
1296 case TempoMarkerItem:
1297 if (is_drawable()) {
1298 time_canvas.get_window()->set_cursor (*timebar_cursor);
1301 case FadeInHandleItem:
1302 case FadeOutHandleItem:
1303 if (mouse_mode == MouseObject) {
1304 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1306 rect->property_fill_color_rgba() = 0;
1307 rect->property_outline_pixels() = 1;
1316 /* second pass to handle entered track status in a comprehensible way.
1319 switch (item_type) {
1321 case GainAutomationLineItem:
1322 case RedirectAutomationLineItem:
1323 case PanAutomationLineItem:
1324 case GainControlPointItem:
1325 case GainAutomationControlPointItem:
1326 case PanAutomationControlPointItem:
1327 case RedirectAutomationControlPointItem:
1328 /* these do not affect the current entered track state */
1329 clear_entered_track = false;
1332 case AutomationTrackItem:
1333 /* handled above already */
1337 set_entered_track (0);
1345 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1354 switch (item_type) {
1355 case GainControlPointItem:
1356 case GainAutomationControlPointItem:
1357 case PanAutomationControlPointItem:
1358 case RedirectAutomationControlPointItem:
1359 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1360 if (cp->line.npoints() > 1) {
1361 if (!cp->selected) {
1362 cp->set_visible (false);
1366 if (is_drawable()) {
1367 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1370 hide_verbose_canvas_cursor ();
1373 case RegionViewNameHighlight:
1374 case StartSelectionTrimItem:
1375 case EndSelectionTrimItem:
1376 case EditCursorItem:
1377 case PlayheadCursorItem:
1378 /* <CMT Additions> */
1379 case ImageFrameHandleStartItem:
1380 case ImageFrameHandleEndItem:
1381 case MarkerViewHandleStartItem:
1382 case MarkerViewHandleEndItem:
1383 /* </CMT Additions> */
1384 if (is_drawable()) {
1385 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1390 case GainAutomationLineItem:
1391 case RedirectAutomationLineItem:
1392 case PanAutomationLineItem:
1393 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1395 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1397 line->property_fill_color_rgba() = al->get_line_color();
1399 if (is_drawable()) {
1400 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1404 case RegionViewName:
1405 /* see enter_handler() for notes */
1406 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1407 if (is_drawable() && mouse_mode == MouseObject) {
1408 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1413 case RangeMarkerBarItem:
1414 case TransportMarkerBarItem:
1418 if (is_drawable()) {
1419 time_canvas.get_window()->set_cursor (*timebar_cursor);
1424 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1427 loc = find_location_from_marker (marker, is_start);
1428 if (loc) location_flags_changed (loc, this);
1430 case MeterMarkerItem:
1431 case TempoMarkerItem:
1433 if (is_drawable()) {
1434 time_canvas.get_window()->set_cursor (*timebar_cursor);
1439 case FadeInHandleItem:
1440 case FadeOutHandleItem:
1441 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1443 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1445 rect->property_fill_color_rgba() = rv->get_fill_color();
1446 rect->property_outline_pixels() = 0;
1451 case AutomationTrackItem:
1452 if (is_drawable()) {
1453 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1454 clear_entered_track = true;
1455 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1467 Editor::left_automation_track ()
1469 if (clear_entered_track) {
1470 set_entered_track (0);
1471 clear_entered_track = false;
1477 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1481 /* We call this so that MOTION_NOTIFY events continue to be
1482 delivered to the canvas. We need to do this because we set
1483 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1484 the density of the events, at the expense of a round-trip
1485 to the server. Given that this will mostly occur on cases
1486 where DISPLAY = :0.0, and given the cost of what the motion
1487 event might do, its a good tradeoff.
1490 track_canvas.get_pointer (x, y);
1492 if (current_stepping_trackview) {
1493 /* don't keep the persistent stepped trackview if the mouse moves */
1494 current_stepping_trackview = 0;
1495 step_timeout.disconnect ();
1498 if (session && session->actively_recording()) {
1499 /* Sorry. no dragging stuff around while we record */
1503 drag_info.item_type = item_type;
1504 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1505 &drag_info.current_pointer_y);
1507 if (!from_autoscroll && drag_info.item) {
1508 /* item != 0 is the best test i can think of for dragging.
1510 if (!drag_info.move_threshold_passed) {
1512 drag_info.move_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1514 // and change the initial grab loc/frame if this drag info wants us to
1516 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1517 drag_info.grab_frame = drag_info.current_pointer_frame;
1518 drag_info.grab_x = drag_info.current_pointer_x;
1519 drag_info.grab_y = drag_info.current_pointer_y;
1520 drag_info.last_pointer_frame = drag_info.grab_frame;
1521 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1526 switch (item_type) {
1527 case PlayheadCursorItem:
1528 case EditCursorItem:
1530 case GainControlPointItem:
1531 case RedirectAutomationControlPointItem:
1532 case GainAutomationControlPointItem:
1533 case PanAutomationControlPointItem:
1534 case TempoMarkerItem:
1535 case MeterMarkerItem:
1536 case RegionViewNameHighlight:
1537 case StartSelectionTrimItem:
1538 case EndSelectionTrimItem:
1541 case RedirectAutomationLineItem:
1542 case GainAutomationLineItem:
1543 case PanAutomationLineItem:
1544 case FadeInHandleItem:
1545 case FadeOutHandleItem:
1546 /* <CMT Additions> */
1547 case ImageFrameHandleStartItem:
1548 case ImageFrameHandleEndItem:
1549 case MarkerViewHandleStartItem:
1550 case MarkerViewHandleEndItem:
1551 /* </CMT Additions> */
1552 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1553 (event->motion.state & Gdk::BUTTON2_MASK))) {
1554 if (!from_autoscroll) {
1555 maybe_autoscroll (event);
1557 (this->*(drag_info.motion_callback)) (item, event);
1566 switch (mouse_mode) {
1571 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1572 (event->motion.state & GDK_BUTTON2_MASK))) {
1573 if (!from_autoscroll) {
1574 maybe_autoscroll (event);
1576 (this->*(drag_info.motion_callback)) (item, event);
1587 track_canvas_motion (event);
1588 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1596 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1598 if (drag_info.item == 0) {
1599 fatal << _("programming error: start_grab called without drag item") << endmsg;
1605 cursor = grabber_cursor;
1608 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1610 if (event->button.button == 2) {
1611 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1612 drag_info.y_constrained = true;
1613 drag_info.x_constrained = false;
1615 drag_info.y_constrained = false;
1616 drag_info.x_constrained = true;
1619 drag_info.x_constrained = false;
1620 drag_info.y_constrained = false;
1623 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1624 drag_info.last_pointer_frame = drag_info.grab_frame;
1625 drag_info.current_pointer_frame = drag_info.grab_frame;
1626 drag_info.current_pointer_x = drag_info.grab_x;
1627 drag_info.current_pointer_y = drag_info.grab_y;
1628 drag_info.cumulative_x_drag = 0;
1629 drag_info.cumulative_y_drag = 0;
1630 drag_info.first_move = true;
1631 drag_info.move_threshold_passed = false;
1632 drag_info.want_move_threshold = false;
1633 drag_info.pointer_frame_offset = 0;
1634 drag_info.brushing = false;
1635 drag_info.copied_location = 0;
1637 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1639 event->button.time);
1641 if (session && session->transport_rolling()) {
1642 drag_info.was_rolling = true;
1644 drag_info.was_rolling = false;
1647 switch (snap_type) {
1648 case SnapToRegionStart:
1649 case SnapToRegionEnd:
1650 case SnapToRegionSync:
1651 case SnapToRegionBoundary:
1652 build_region_boundary_cache ();
1660 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1662 drag_info.item->ungrab (0);
1663 drag_info.item = new_item;
1666 cursor = grabber_cursor;
1669 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1673 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1675 bool did_drag = false;
1677 stop_canvas_autoscroll ();
1679 if (drag_info.item == 0) {
1683 drag_info.item->ungrab (event->button.time);
1685 if (drag_info.finished_callback) {
1686 (this->*(drag_info.finished_callback)) (item, event);
1689 did_drag = !drag_info.first_move;
1691 hide_verbose_canvas_cursor();
1694 drag_info.copy = false;
1695 drag_info.motion_callback = 0;
1696 drag_info.finished_callback = 0;
1697 drag_info.last_trackview = 0;
1698 drag_info.last_frame_position = 0;
1699 drag_info.grab_frame = 0;
1700 drag_info.last_pointer_frame = 0;
1701 drag_info.current_pointer_frame = 0;
1702 drag_info.brushing = false;
1704 if (drag_info.copied_location) {
1705 delete drag_info.copied_location;
1706 drag_info.copied_location = 0;
1713 Editor::set_edit_cursor (GdkEvent* event)
1715 nframes_t pointer_frame = event_frame (event);
1717 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1718 if (snap_type != SnapToEditCursor) {
1719 snap_to (pointer_frame);
1723 edit_cursor->set_position (pointer_frame);
1724 edit_cursor_clock.set (pointer_frame);
1728 Editor::set_playhead_cursor (GdkEvent* event)
1730 nframes_t pointer_frame = event_frame (event);
1732 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1733 snap_to (pointer_frame);
1737 session->request_locate (pointer_frame, session->transport_rolling());
1742 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1744 drag_info.item = item;
1745 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1746 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1750 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1751 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1755 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1757 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1761 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1763 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1765 nframes_t fade_length;
1767 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1768 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1774 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1778 if (pos < (arv->region()->position() + 64)) {
1779 fade_length = 64; // this should be a minimum defined somewhere
1780 } else if (pos > arv->region()->last_frame()) {
1781 fade_length = arv->region()->length();
1783 fade_length = pos - arv->region()->position();
1786 arv->reset_fade_in_shape_width (fade_length);
1788 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1790 drag_info.first_move = false;
1794 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1796 if (drag_info.first_move) return;
1798 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1800 nframes_t fade_length;
1802 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1803 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1809 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1813 if (pos < (arv->region()->position() + 64)) {
1814 fade_length = 64; // this should be a minimum defined somewhere
1816 else if (pos > arv->region()->last_frame()) {
1817 fade_length = arv->region()->length();
1820 fade_length = pos - arv->region()->position();
1823 begin_reversible_command (_("change fade in length"));
1824 AutomationList& alist = arv->audio_region()->fade_in();
1825 XMLNode &before = alist.get_state();
1827 arv->audio_region()->set_fade_in_length (fade_length);
1829 XMLNode &after = alist.get_state();
1830 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1831 commit_reversible_command ();
1832 fade_in_drag_motion_callback (item, event);
1836 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1838 drag_info.item = item;
1839 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1840 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1844 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1845 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1849 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1851 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1855 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1857 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1859 nframes_t fade_length;
1861 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1862 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1868 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1872 if (pos > (arv->region()->last_frame() - 64)) {
1873 fade_length = 64; // this should really be a minimum fade defined somewhere
1875 else if (pos < arv->region()->position()) {
1876 fade_length = arv->region()->length();
1879 fade_length = arv->region()->last_frame() - pos;
1882 arv->reset_fade_out_shape_width (fade_length);
1884 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1886 drag_info.first_move = false;
1890 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1892 if (drag_info.first_move) return;
1894 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1896 nframes_t fade_length;
1898 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1899 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1905 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1909 if (pos > (arv->region()->last_frame() - 64)) {
1910 fade_length = 64; // this should really be a minimum fade defined somewhere
1912 else if (pos < arv->region()->position()) {
1913 fade_length = arv->region()->length();
1916 fade_length = arv->region()->last_frame() - pos;
1919 begin_reversible_command (_("change fade out length"));
1920 AutomationList& alist = arv->audio_region()->fade_out();
1921 XMLNode &before = alist.get_state();
1923 arv->audio_region()->set_fade_out_length (fade_length);
1925 XMLNode &after = alist.get_state();
1926 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1927 commit_reversible_command ();
1929 fade_out_drag_motion_callback (item, event);
1933 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1935 drag_info.item = item;
1936 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1937 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1941 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1942 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1946 Cursor* cursor = (Cursor *) drag_info.data;
1948 if (cursor == playhead_cursor) {
1949 _dragging_playhead = true;
1951 if (session && drag_info.was_rolling) {
1952 session->request_stop ();
1956 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1958 show_verbose_time_cursor (cursor->current_frame, 10);
1962 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1964 Cursor* cursor = (Cursor *) drag_info.data;
1965 nframes_t adjusted_frame;
1967 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1968 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1974 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1975 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1976 snap_to (adjusted_frame);
1980 if (adjusted_frame == drag_info.last_pointer_frame) return;
1982 cursor->set_position (adjusted_frame);
1984 if (cursor == edit_cursor) {
1985 edit_cursor_clock.set (cursor->current_frame);
1987 UpdateAllTransportClocks (cursor->current_frame);
1990 show_verbose_time_cursor (cursor->current_frame, 10);
1992 drag_info.last_pointer_frame = adjusted_frame;
1993 drag_info.first_move = false;
1997 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1999 if (drag_info.first_move) return;
2001 cursor_drag_motion_callback (item, event);
2003 _dragging_playhead = false;
2005 if (item == &playhead_cursor->canvas_item) {
2007 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2009 } else if (item == &edit_cursor->canvas_item) {
2010 edit_cursor->set_position (edit_cursor->current_frame);
2011 edit_cursor_clock.set (edit_cursor->current_frame);
2016 Editor::update_marker_drag_item (Location *location)
2018 double x1 = frame_to_pixel (location->start());
2019 double x2 = frame_to_pixel (location->end());
2021 if (location->is_mark()) {
2022 marker_drag_line_points.front().set_x(x1);
2023 marker_drag_line_points.back().set_x(x1);
2024 marker_drag_line->property_points() = marker_drag_line_points;
2027 range_marker_drag_rect->property_x1() = x1;
2028 range_marker_drag_rect->property_x2() = x2;
2033 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2037 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2038 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2044 Location *location = find_location_from_marker (marker, is_start);
2046 drag_info.item = item;
2047 drag_info.data = marker;
2048 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2049 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2053 drag_info.copied_location = new Location (*location);
2054 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2056 update_marker_drag_item (location);
2058 if (location->is_mark()) {
2059 marker_drag_line->show();
2060 marker_drag_line->raise_to_top();
2063 range_marker_drag_rect->show();
2064 range_marker_drag_rect->raise_to_top();
2067 if (is_start) show_verbose_time_cursor (location->start(), 10);
2068 else show_verbose_time_cursor (location->end(), 10);
2072 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2075 Marker* marker = (Marker *) drag_info.data;
2076 Location *real_location;
2077 Location *copy_location;
2079 bool move_both = false;
2083 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2084 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2090 nframes_t next = newframe;
2092 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2093 snap_to (newframe, 0, true);
2096 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2100 /* call this to find out if its the start or end */
2102 real_location = find_location_from_marker (marker, is_start);
2104 /* use the copy that we're "dragging" around */
2106 copy_location = drag_info.copied_location;
2108 f_delta = copy_location->end() - copy_location->start();
2110 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2114 if (copy_location->is_mark()) {
2117 copy_location->set_start (newframe);
2121 if (is_start) { // start-of-range marker
2124 copy_location->set_start (newframe);
2125 copy_location->set_end (newframe + f_delta);
2126 } else if (newframe < copy_location->end()) {
2127 copy_location->set_start (newframe);
2129 snap_to (next, 1, true);
2130 copy_location->set_end (next);
2131 copy_location->set_start (newframe);
2134 } else { // end marker
2137 copy_location->set_end (newframe);
2138 copy_location->set_start (newframe - f_delta);
2139 } else if (newframe > copy_location->start()) {
2140 copy_location->set_end (newframe);
2142 } else if (newframe > 0) {
2143 snap_to (next, -1, true);
2144 copy_location->set_start (next);
2145 copy_location->set_end (newframe);
2150 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2151 drag_info.first_move = false;
2153 update_marker_drag_item (copy_location);
2155 LocationMarkers* lm = find_location_markers (real_location);
2156 lm->set_position (copy_location->start(), copy_location->end());
2158 show_verbose_time_cursor (newframe, 10);
2162 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2164 if (drag_info.first_move) {
2165 marker_drag_motion_callback (item, event);
2169 Marker* marker = (Marker *) drag_info.data;
2173 begin_reversible_command ( _("move marker") );
2174 XMLNode &before = session->locations()->get_state();
2176 Location * location = find_location_from_marker (marker, is_start);
2179 if (location->is_mark()) {
2180 location->set_start (drag_info.copied_location->start());
2182 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2186 XMLNode &after = session->locations()->get_state();
2187 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2188 commit_reversible_command ();
2190 marker_drag_line->hide();
2191 range_marker_drag_rect->hide();
2195 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2198 MeterMarker* meter_marker;
2200 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2201 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2205 meter_marker = dynamic_cast<MeterMarker*> (marker);
2207 MetricSection& section (meter_marker->meter());
2209 if (!section.movable()) {
2213 drag_info.item = item;
2214 drag_info.data = marker;
2215 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2216 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2220 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2222 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2226 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2229 MeterMarker* meter_marker;
2231 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2232 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2236 meter_marker = dynamic_cast<MeterMarker*> (marker);
2238 // create a dummy marker for visual representation of moving the copy.
2239 // The actual copying is not done before we reach the finish callback.
2241 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2242 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2243 *new MeterSection(meter_marker->meter()));
2245 drag_info.item = &new_marker->the_item();
2246 drag_info.copy = true;
2247 drag_info.data = new_marker;
2248 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2249 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2253 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2255 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2259 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2261 MeterMarker* marker = (MeterMarker *) drag_info.data;
2262 nframes_t adjusted_frame;
2264 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2265 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2271 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2272 snap_to (adjusted_frame);
2275 if (adjusted_frame == drag_info.last_pointer_frame) return;
2277 marker->set_position (adjusted_frame);
2280 drag_info.last_pointer_frame = adjusted_frame;
2281 drag_info.first_move = false;
2283 show_verbose_time_cursor (adjusted_frame, 10);
2287 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2289 if (drag_info.first_move) return;
2291 meter_marker_drag_motion_callback (drag_info.item, event);
2293 MeterMarker* marker = (MeterMarker *) drag_info.data;
2296 TempoMap& map (session->tempo_map());
2297 map.bbt_time (drag_info.last_pointer_frame, when);
2299 if (drag_info.copy == true) {
2300 begin_reversible_command (_("copy meter mark"));
2301 XMLNode &before = map.get_state();
2302 map.add_meter (marker->meter(), when);
2303 XMLNode &after = map.get_state();
2304 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2305 commit_reversible_command ();
2307 // delete the dummy marker we used for visual representation of copying.
2308 // a new visual marker will show up automatically.
2311 begin_reversible_command (_("move meter mark"));
2312 XMLNode &before = map.get_state();
2313 map.move_meter (marker->meter(), when);
2314 XMLNode &after = map.get_state();
2315 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2316 commit_reversible_command ();
2321 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2324 TempoMarker* tempo_marker;
2326 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2327 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2331 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2332 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2336 MetricSection& section (tempo_marker->tempo());
2338 if (!section.movable()) {
2342 drag_info.item = item;
2343 drag_info.data = marker;
2344 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2345 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2349 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2350 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2354 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2357 TempoMarker* tempo_marker;
2359 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2360 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2364 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2365 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2369 // create a dummy marker for visual representation of moving the copy.
2370 // The actual copying is not done before we reach the finish callback.
2372 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2373 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2374 *new TempoSection(tempo_marker->tempo()));
2376 drag_info.item = &new_marker->the_item();
2377 drag_info.copy = true;
2378 drag_info.data = new_marker;
2379 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2380 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2384 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2386 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2390 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2392 TempoMarker* marker = (TempoMarker *) drag_info.data;
2393 nframes_t adjusted_frame;
2395 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2396 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2402 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2403 snap_to (adjusted_frame);
2406 if (adjusted_frame == drag_info.last_pointer_frame) return;
2408 /* OK, we've moved far enough to make it worth actually move the thing. */
2410 marker->set_position (adjusted_frame);
2412 show_verbose_time_cursor (adjusted_frame, 10);
2414 drag_info.last_pointer_frame = adjusted_frame;
2415 drag_info.first_move = false;
2419 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2421 if (drag_info.first_move) return;
2423 tempo_marker_drag_motion_callback (drag_info.item, event);
2425 TempoMarker* marker = (TempoMarker *) drag_info.data;
2428 TempoMap& map (session->tempo_map());
2429 map.bbt_time (drag_info.last_pointer_frame, when);
2431 if (drag_info.copy == true) {
2432 begin_reversible_command (_("copy tempo mark"));
2433 XMLNode &before = map.get_state();
2434 map.add_tempo (marker->tempo(), when);
2435 XMLNode &after = map.get_state();
2436 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2437 commit_reversible_command ();
2439 // delete the dummy marker we used for visual representation of copying.
2440 // a new visual marker will show up automatically.
2443 begin_reversible_command (_("move tempo mark"));
2444 XMLNode &before = map.get_state();
2445 map.move_tempo (marker->tempo(), when);
2446 XMLNode &after = map.get_state();
2447 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2448 commit_reversible_command ();
2453 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2455 ControlPoint* control_point;
2457 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2458 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2462 // We shouldn't remove the first or last gain point
2463 if (control_point->line.is_last_point(*control_point) ||
2464 control_point->line.is_first_point(*control_point)) {
2468 control_point->line.remove_point (*control_point);
2472 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2474 ControlPoint* control_point;
2476 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2477 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2481 control_point->line.remove_point (*control_point);
2485 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2487 ControlPoint* control_point;
2489 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2490 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2494 drag_info.item = item;
2495 drag_info.data = control_point;
2496 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2497 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2499 start_grab (event, fader_cursor);
2501 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2503 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2504 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2505 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2507 show_verbose_canvas_cursor ();
2511 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2513 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2515 double cx = drag_info.current_pointer_x;
2516 double cy = drag_info.current_pointer_y;
2518 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2519 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2521 if (drag_info.x_constrained) {
2522 cx = drag_info.grab_x;
2524 if (drag_info.y_constrained) {
2525 cy = drag_info.grab_y;
2528 cp->line.parent_group().w2i (cx, cy);
2532 cy = min ((double) cp->line.height(), cy);
2534 //translate cx to frames
2535 nframes_t cx_frames = unit_to_frame (cx);
2537 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2538 snap_to (cx_frames);
2541 float fraction = 1.0 - (cy / cp->line.height());
2545 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2551 cp->line.point_drag (*cp, cx_frames , fraction, push);
2553 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2555 drag_info.first_move = false;
2559 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2561 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2563 if (drag_info.first_move) {
2567 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2568 reset_point_selection ();
2572 control_point_drag_motion_callback (item, event);
2574 cp->line.end_drag (cp);
2578 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2580 switch (mouse_mode) {
2582 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2583 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2591 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2595 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2596 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2600 start_line_grab (al, event);
2604 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2608 nframes_t frame_within_region;
2610 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2614 cx = event->button.x;
2615 cy = event->button.y;
2616 line->parent_group().w2i (cx, cy);
2617 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2619 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2620 current_line_drag_info.after)) {
2621 /* no adjacent points */
2625 drag_info.item = &line->grab_item();
2626 drag_info.data = line;
2627 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2628 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2630 start_grab (event, fader_cursor);
2632 double fraction = 1.0 - (cy / line->height());
2634 line->start_drag (0, drag_info.grab_frame, fraction);
2636 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2637 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2638 show_verbose_canvas_cursor ();
2642 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2644 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2645 double cx = drag_info.current_pointer_x;
2646 double cy = drag_info.current_pointer_y;
2648 line->parent_group().w2i (cx, cy);
2651 fraction = 1.0 - (cy / line->height());
2655 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2661 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2663 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2667 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2669 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2670 line_drag_motion_callback (item, event);
2675 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2677 if (selection->regions.empty() || clicked_regionview == 0) {
2681 drag_info.copy = false;
2682 drag_info.item = item;
2683 drag_info.data = clicked_regionview;
2684 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2685 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2690 TimeAxisView* tvp = clicked_trackview;
2691 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2693 if (tv && tv->is_audio_track()) {
2694 speed = tv->get_diskstream()->speed();
2697 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2698 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2699 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2700 // we want a move threshold
2701 drag_info.want_move_threshold = true;
2703 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2705 begin_reversible_command (_("move region(s)"));
2709 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2711 cerr << "start region copy grab, selected regions = " << selection->regions.size() << endl;
2713 if (selection->regions.empty() || clicked_regionview == 0) {
2717 drag_info.copy = true;
2718 drag_info.item = item;
2719 drag_info.data = clicked_regionview;
2723 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2724 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2727 if (atv && atv->is_audio_track()) {
2728 speed = atv->get_diskstream()->speed();
2731 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2732 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2733 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2734 // we want a move threshold
2735 drag_info.want_move_threshold = true;
2736 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2737 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2738 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2742 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2744 if (selection->regions.empty() || clicked_regionview == 0) {
2748 drag_info.copy = false;
2749 drag_info.item = item;
2750 drag_info.data = clicked_regionview;
2751 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2752 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2757 TimeAxisView* tvp = clicked_trackview;
2758 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2760 if (tv && tv->is_audio_track()) {
2761 speed = tv->get_diskstream()->speed();
2764 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2765 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2766 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2767 // we want a move threshold
2768 drag_info.want_move_threshold = true;
2769 drag_info.brushing = true;
2771 begin_reversible_command (_("Drag region brush"));
2775 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2779 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2780 nframes_t pending_region_position = 0;
2781 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2782 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2783 bool clamp_y_axis = false;
2784 vector<int32_t> height_list(512) ;
2785 vector<int32_t>::iterator j;
2787 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2789 drag_info.want_move_threshold = false; // don't copy again
2791 /* this is committed in the grab finished callback. */
2793 begin_reversible_command (_("Drag region copy"));
2795 /* duplicate the region(s) */
2797 vector<RegionView*> new_regionviews;
2799 set<boost::shared_ptr<Playlist> > affected_playlists;
2800 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2802 // TODO: Crossfades need to be copied!
2803 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2808 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
2809 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2811 insert_result = affected_playlists.insert (to_playlist);
2812 if (insert_result.second) {
2813 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
2816 latest_regionview = 0;
2818 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2820 /* create a new region with the same name. */
2822 // FIXME: ew. need a (virtual) Region::duplicate() or something?
2824 boost::shared_ptr<Region> newregion;
2825 boost::shared_ptr<Region> ar;
2827 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
2828 newregion = RegionFactory::create (ar);
2830 assert(newregion != 0);
2832 /* if the original region was locked, we don't care */
2834 newregion->set_locked (false);
2836 to_playlist->add_region (newregion, (nframes_t) (rv->region()->position() * atv->get_diskstream()->speed()));
2840 if (latest_regionview) {
2841 new_regionviews.push_back (latest_regionview);
2847 if (new_regionviews.empty()) {
2851 /* reset selection to new regionviews */
2853 selection->set (new_regionviews);
2855 /* reset drag_info data to reflect the fact that we are dragging the copies */
2857 drag_info.data = new_regionviews.front();
2858 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2861 /* Which trackview is this ? */
2863 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2864 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2866 /* The region motion is only processed if the pointer is over
2870 if (!tv || !tv->is_audio_track()) {
2871 /* To make sure we hide the verbose canvas cursor when the mouse is
2872 not held over and audiotrack.
2874 hide_verbose_canvas_cursor ();
2878 original_pointer_order = drag_info.last_trackview->order;
2880 /************************************************************
2882 ************************************************************/
2884 if (drag_info.brushing) {
2885 clamp_y_axis = true;
2890 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2892 int32_t children = 0, numtracks = 0;
2893 // XXX hard coding track limit, oh my, so very very bad
2894 bitset <1024> tracks (0x00);
2895 /* get a bitmask representing the visible tracks */
2897 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2898 TimeAxisView *tracklist_timeview;
2899 tracklist_timeview = (*i);
2900 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2901 list<TimeAxisView*> children_list;
2903 /* zeroes are audio tracks. ones are other types. */
2905 if (!atv2->hidden()) {
2907 if (visible_y_high < atv2->order) {
2908 visible_y_high = atv2->order;
2910 if (visible_y_low > atv2->order) {
2911 visible_y_low = atv2->order;
2914 if (!atv2->is_audio_track()) {
2915 tracks = tracks |= (0x01 << atv2->order);
2918 height_list[atv2->order] = (*i)->height;
2920 if ((children_list = atv2->get_child_list()).size() > 0) {
2921 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2922 tracks = tracks |= (0x01 << (atv2->order + children));
2923 height_list[atv2->order + children] = (*j)->height;
2931 /* find the actual span according to the canvas */
2933 canvas_pointer_y_span = pointer_y_span;
2934 if (drag_info.last_trackview->order >= tv->order) {
2936 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2937 if (height_list[y] == 0 ) {
2938 canvas_pointer_y_span--;
2943 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2944 if ( height_list[y] == 0 ) {
2945 canvas_pointer_y_span++;
2950 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2951 RegionView* rv2 = (*i);
2952 double ix1, ix2, iy1, iy2;
2955 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2956 rv2->get_canvas_group()->i2w (ix1, iy1);
2957 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2958 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2960 if (atv2->order != original_pointer_order) {
2961 /* this isn't the pointer track */
2963 if (canvas_pointer_y_span > 0) {
2965 /* moving up the canvas */
2966 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2968 int32_t visible_tracks = 0;
2969 while (visible_tracks < canvas_pointer_y_span ) {
2972 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2973 /* we're passing through a hidden track */
2978 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2979 clamp_y_axis = true;
2983 clamp_y_axis = true;
2986 } else if (canvas_pointer_y_span < 0) {
2988 /*moving down the canvas*/
2990 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2993 int32_t visible_tracks = 0;
2995 while (visible_tracks > canvas_pointer_y_span ) {
2998 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3002 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3003 clamp_y_axis = true;
3008 clamp_y_axis = true;
3014 /* this is the pointer's track */
3015 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3016 clamp_y_axis = true;
3017 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3018 clamp_y_axis = true;
3026 } else if (drag_info.last_trackview == tv) {
3027 clamp_y_axis = true;
3031 if (!clamp_y_axis) {
3032 drag_info.last_trackview = tv;
3035 /************************************************************
3037 ************************************************************/
3039 /* compute the amount of pointer motion in frames, and where
3040 the region would be if we moved it by that much.
3043 if (drag_info.move_threshold_passed) {
3045 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3047 nframes_t sync_frame;
3048 nframes_t sync_offset;
3051 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3053 sync_offset = rv->region()->sync_offset (sync_dir);
3054 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3056 /* we snap if the snap modifier is not enabled.
3059 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3060 snap_to (sync_frame);
3063 if (sync_frame - sync_offset <= sync_frame) {
3064 pending_region_position = sync_frame - (sync_dir*sync_offset);
3066 pending_region_position = 0;
3070 pending_region_position = 0;
3073 if (pending_region_position > max_frames - rv->region()->length()) {
3074 pending_region_position = drag_info.last_frame_position;
3077 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3079 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3081 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3082 to make it appear at the new location.
3085 if (pending_region_position > drag_info.last_frame_position) {
3086 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3088 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3091 drag_info.last_frame_position = pending_region_position;
3098 /* threshold not passed */
3103 /*************************************************************
3105 ************************************************************/
3107 if (x_delta == 0 && (pointer_y_span == 0)) {
3108 /* haven't reached next snap point, and we're not switching
3109 trackviews. nothing to do.
3115 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3117 RegionView* rv2 = (*i);
3119 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3121 double ix1, ix2, iy1, iy2;
3122 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3123 rv2->get_canvas_group()->i2w (ix1, iy1);
3132 /*************************************************************
3134 ************************************************************/
3136 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3137 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3139 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3141 RegionView* rv = (*i);
3142 double ix1, ix2, iy1, iy2;
3143 int32_t temp_pointer_y_span = pointer_y_span;
3145 /* get item BBox, which will be relative to parent. so we have
3146 to query on a child, then convert to world coordinates using
3150 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3151 rv->get_canvas_group()->i2w (ix1, iy1);
3152 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3153 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3154 AudioTimeAxisView* temp_atv;
3156 if ((pointer_y_span != 0) && !clamp_y_axis) {
3159 for (j = height_list.begin(); j!= height_list.end(); j++) {
3160 if (x == canvas_atv->order) {
3161 /* we found the track the region is on */
3162 if (x != original_pointer_order) {
3163 /*this isn't from the same track we're dragging from */
3164 temp_pointer_y_span = canvas_pointer_y_span;
3166 while (temp_pointer_y_span > 0) {
3167 /* we're moving up canvas-wise,
3168 so we need to find the next track height
3170 if (j != height_list.begin()) {
3173 if (x != original_pointer_order) {
3174 /* we're not from the dragged track, so ignore hidden tracks. */
3176 temp_pointer_y_span++;
3180 temp_pointer_y_span--;
3182 while (temp_pointer_y_span < 0) {
3184 if (x != original_pointer_order) {
3186 temp_pointer_y_span--;
3190 if (j != height_list.end()) {
3193 temp_pointer_y_span++;
3195 /* find out where we'll be when we move and set height accordingly */
3197 tvp2 = trackview_by_y_position (iy1 + y_delta);
3198 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3199 rv->set_height (temp_atv->height);
3201 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3202 personally, i think this can confuse things, but never mind.
3205 //const GdkColor& col (temp_atv->view->get_region_color());
3206 //rv->set_color (const_cast<GdkColor&>(col));
3213 /* prevent the regionview from being moved to before
3214 the zero position on the canvas.
3219 if (-x_delta > ix1) {
3222 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3223 x_delta = max_frames - rv->region()->last_frame();
3226 if (drag_info.first_move) {
3228 /* hide any dependent views */
3230 // rv->get_time_axis_view().hide_dependent_views (*rv);
3232 /* this is subtle. raising the regionview itself won't help,
3233 because raise_to_top() just puts the item on the top of
3234 its parent's stack. so, we need to put the trackview canvas_display group
3235 on the top, since its parent is the whole canvas.
3238 rv->get_canvas_group()->raise_to_top();
3239 rv->get_time_axis_view().canvas_display->raise_to_top();
3240 cursor_group->raise_to_top();
3242 /* freeze the playlists from notifying till
3246 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3247 if (atv && atv->is_audio_track()) {
3248 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>(atv->get_diskstream()->playlist());
3250 /* only freeze and capture state once */
3252 insert_result = motion_frozen_playlists.insert (pl);
3253 if (insert_result.second) {
3255 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
3259 rv->region()->set_opaque(false);
3262 if (drag_info.brushing) {
3263 mouse_brush_insert_region (rv, pending_region_position);
3265 rv->move (x_delta, y_delta);
3269 if (drag_info.first_move) {
3270 cursor_group->raise_to_top();
3273 drag_info.first_move = false;
3275 if (x_delta != 0 && !drag_info.brushing) {
3276 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3282 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3285 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3286 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3287 bool nocommit = true;
3289 RouteTimeAxisView* atv;
3290 bool regionview_y_movement;
3291 bool regionview_x_movement;
3293 /* first_move is set to false if the regionview has been moved in the
3297 if (drag_info.first_move) {
3304 /* The regionview has been moved at some stage during the grab so we need
3305 to account for any mouse movement between this event and the last one.
3308 region_drag_motion_callback (item, event);
3310 if (drag_info.brushing) {
3311 /* all changes were made during motion event handlers */
3315 /* adjust for track speed */
3318 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3319 if (atv && atv->get_diskstream()) {
3320 speed = atv->get_diskstream()->speed();
3323 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3324 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3326 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3327 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3329 if (regionview_y_movement) {
3331 /* motion between tracks */
3333 list<RegionView*> new_selection;
3335 /* moved to a different audio track. */
3337 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3339 RegionView* rv2 = (*i);
3341 /* the region that used to be in the old playlist is not
3342 moved to the new one - we make a copy of it. as a result,
3343 any existing editor for the region should no longer be
3347 if (!drag_info.copy) {
3348 rv2->hide_region_editor();
3350 new_selection.push_back (rv2);
3354 /* first, freeze the target tracks */
3356 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3358 boost::shared_ptr<Playlist> from_playlist;
3359 boost::shared_ptr<Playlist> to_playlist;
3361 double ix1, ix2, iy1, iy2;
3363 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3364 (*i)->get_canvas_group()->i2w (ix1, iy1);
3365 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3366 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3368 (*i)->region()->set_opaque (true);
3370 from_playlist = (*i)->region()->playlist();
3371 to_playlist = atv2->playlist();
3373 /* the from_playlist was frozen in the "first_move" case
3374 of the motion handler. the insert can fail,
3375 but that doesn't matter. it just means
3376 we already have the playlist in the list.
3379 motion_frozen_playlists.insert (from_playlist);
3381 /* only freeze the to_playlist once */
3383 insert_result = motion_frozen_playlists.insert(to_playlist);
3384 if (insert_result.second) {
3385 to_playlist->freeze();
3386 session->add_command(new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3391 /* now do it again with the actual operations */
3393 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3395 boost::shared_ptr<Playlist> from_playlist;
3396 boost::shared_ptr<Playlist> to_playlist;
3398 double ix1, ix2, iy1, iy2;
3400 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3401 (*i)->get_canvas_group()->i2w (ix1, iy1);
3402 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3403 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3405 from_playlist = (*i)->region()->playlist();
3406 to_playlist = atv2->playlist();
3408 latest_regionview = 0;
3410 where = (nframes_t) (unit_to_frame (ix1) * speed);
3411 boost::shared_ptr<Region> new_region (RegionFactory::create ((*i)->region()));
3413 from_playlist->remove_region (((*i)->region()));
3415 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3416 to_playlist->add_region (new_region, where);
3419 if (latest_regionview) {
3420 selection->add (latest_regionview);
3426 /* motion within a single track */
3428 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3432 if (rv->region()->locked()) {
3436 if (regionview_x_movement) {
3437 double ownspeed = 1.0;
3438 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3440 if (atv && atv->get_diskstream()) {
3441 ownspeed = atv->get_diskstream()->speed();
3444 /* base the new region position on the current position of the regionview.*/
3446 double ix1, ix2, iy1, iy2;
3448 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3449 rv->get_canvas_group()->i2w (ix1, iy1);
3450 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3454 where = rv->region()->position();
3457 rv->get_time_axis_view().reveal_dependent_views (*rv);
3459 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3461 rv->region()->set_position (where, (void *) this);
3462 rv->region()->set_opaque (true);
3467 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3469 session->add_command (new MementoCommand<Playlist>(*((*p).get()), 0, & (*p)->get_state()));
3472 motion_frozen_playlists.clear ();
3475 commit_reversible_command ();
3480 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3482 /* Either add to or set the set the region selection, unless
3483 this is an alignment click (control used)
3486 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3487 TimeAxisView* tv = &rv.get_time_axis_view();
3488 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3490 if (atv && atv->is_audio_track()) {
3491 speed = atv->get_diskstream()->speed();
3494 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3496 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3498 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3500 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3504 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3510 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3516 nframes_t frame_rate;
3523 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3524 case AudioClock::BBT:
3525 session->bbt_time (frame, bbt);
3526 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3529 case AudioClock::SMPTE:
3530 session->smpte_time (frame, smpte);
3531 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3534 case AudioClock::MinSec:
3535 /* XXX this is copied from show_verbose_duration_cursor() */
3536 frame_rate = session->frame_rate();
3537 hours = frame / (frame_rate * 3600);
3538 frame = frame % (frame_rate * 3600);
3539 mins = frame / (frame_rate * 60);
3540 frame = frame % (frame_rate * 60);
3541 secs = (float) frame / (float) frame_rate;
3542 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3546 snprintf (buf, sizeof(buf), "%u", frame);
3550 if (xpos >= 0 && ypos >=0) {
3551 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3554 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3556 show_verbose_canvas_cursor ();
3560 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3567 nframes_t distance, frame_rate;
3569 Meter meter_at_start(session->tempo_map().meter_at(start));
3575 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3576 case AudioClock::BBT:
3577 session->bbt_time (start, sbbt);
3578 session->bbt_time (end, ebbt);
3581 /* XXX this computation won't work well if the
3582 user makes a selection that spans any meter changes.
3585 ebbt.bars -= sbbt.bars;
3586 if (ebbt.beats >= sbbt.beats) {
3587 ebbt.beats -= sbbt.beats;
3590 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3592 if (ebbt.ticks >= sbbt.ticks) {
3593 ebbt.ticks -= sbbt.ticks;
3596 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3599 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3602 case AudioClock::SMPTE:
3603 session->smpte_duration (end - start, smpte);
3604 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3607 case AudioClock::MinSec:
3608 /* XXX this stuff should be elsewhere.. */
3609 distance = end - start;
3610 frame_rate = session->frame_rate();
3611 hours = distance / (frame_rate * 3600);
3612 distance = distance % (frame_rate * 3600);
3613 mins = distance / (frame_rate * 60);
3614 distance = distance % (frame_rate * 60);
3615 secs = (float) distance / (float) frame_rate;
3616 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3620 snprintf (buf, sizeof(buf), "%u", end - start);
3624 if (xpos >= 0 && ypos >=0) {
3625 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3628 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3630 show_verbose_canvas_cursor ();
3634 Editor::collect_new_region_view (RegionView* rv)
3636 latest_regionview = rv;
3640 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3642 if (clicked_regionview == 0) {
3646 /* lets try to create new Region for the selection */
3648 vector<boost::shared_ptr<AudioRegion> > new_regions;
3649 create_region_from_selection (new_regions);
3651 if (new_regions.empty()) {
3655 /* XXX fix me one day to use all new regions */
3657 boost::shared_ptr<Region> region (new_regions.front());
3659 /* add it to the current stream/playlist.
3661 tricky: the streamview for the track will add a new regionview. we will
3662 catch the signal it sends when it creates the regionview to
3663 set the regionview we want to then drag.
3666 latest_regionview = 0;
3667 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3669 /* A selection grab currently creates two undo/redo operations, one for
3670 creating the new region and another for moving it.
3673 begin_reversible_command (_("selection grab"));
3675 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3677 XMLNode *before = &(playlist->get_state());
3678 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3679 XMLNode *after = &(playlist->get_state());
3680 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3682 commit_reversible_command ();
3686 if (latest_regionview == 0) {
3687 /* something went wrong */
3691 /* we need to deselect all other regionviews, and select this one
3692 i'm ignoring undo stuff, because the region creation will take care of it */
3693 selection->set (latest_regionview);
3695 drag_info.item = latest_regionview->get_canvas_group();
3696 drag_info.data = latest_regionview;
3697 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3698 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3702 drag_info.last_trackview = clicked_trackview;
3703 drag_info.last_frame_position = latest_regionview->region()->position();
3704 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3706 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3710 Editor::cancel_selection ()
3712 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3713 (*i)->hide_selection ();
3715 begin_reversible_command (_("cancel selection"));
3716 selection->clear ();
3717 clicked_selection = 0;
3718 commit_reversible_command ();
3722 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3724 nframes_t start = 0;
3731 drag_info.item = item;
3732 drag_info.motion_callback = &Editor::drag_selection;
3733 drag_info.finished_callback = &Editor::end_selection_op;
3738 case CreateSelection:
3739 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3740 drag_info.copy = true;
3742 drag_info.copy = false;
3744 start_grab (event, selector_cursor);
3747 case SelectionStartTrim:
3748 if (clicked_trackview) {
3749 clicked_trackview->order_selection_trims (item, true);
3751 start_grab (event, trimmer_cursor);
3752 start = selection->time[clicked_selection].start;
3753 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3756 case SelectionEndTrim:
3757 if (clicked_trackview) {
3758 clicked_trackview->order_selection_trims (item, false);
3760 start_grab (event, trimmer_cursor);
3761 end = selection->time[clicked_selection].end;
3762 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3766 start = selection->time[clicked_selection].start;
3768 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3772 if (selection_op == SelectionMove) {
3773 show_verbose_time_cursor(start, 10);
3775 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3780 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3782 nframes_t start = 0;
3785 nframes_t pending_position;
3787 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3788 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3791 pending_position = 0;
3794 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3795 snap_to (pending_position);
3798 /* only alter selection if the current frame is
3799 different from the last frame position (adjusted)
3802 if (pending_position == drag_info.last_pointer_frame) return;
3804 switch (selection_op) {
3805 case CreateSelection:
3807 if (drag_info.first_move) {
3808 snap_to (drag_info.grab_frame);
3811 if (pending_position < drag_info.grab_frame) {
3812 start = pending_position;
3813 end = drag_info.grab_frame;
3815 end = pending_position;
3816 start = drag_info.grab_frame;
3819 /* first drag: Either add to the selection
3820 or create a new selection->
3823 if (drag_info.first_move) {
3825 begin_reversible_command (_("range selection"));
3827 if (drag_info.copy) {
3828 /* adding to the selection */
3829 clicked_selection = selection->add (start, end);
3830 drag_info.copy = false;
3832 /* new selection-> */
3833 clicked_selection = selection->set (clicked_trackview, start, end);
3838 case SelectionStartTrim:
3840 if (drag_info.first_move) {
3841 begin_reversible_command (_("trim selection start"));
3844 start = selection->time[clicked_selection].start;
3845 end = selection->time[clicked_selection].end;
3847 if (pending_position > end) {
3850 start = pending_position;
3854 case SelectionEndTrim:
3856 if (drag_info.first_move) {
3857 begin_reversible_command (_("trim selection end"));
3860 start = selection->time[clicked_selection].start;
3861 end = selection->time[clicked_selection].end;
3863 if (pending_position < start) {
3866 end = pending_position;
3873 if (drag_info.first_move) {
3874 begin_reversible_command (_("move selection"));
3877 start = selection->time[clicked_selection].start;
3878 end = selection->time[clicked_selection].end;
3880 length = end - start;
3882 start = pending_position;
3885 end = start + length;
3890 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3891 start_canvas_autoscroll (1);
3895 selection->replace (clicked_selection, start, end);
3898 drag_info.last_pointer_frame = pending_position;
3899 drag_info.first_move = false;
3901 if (selection_op == SelectionMove) {
3902 show_verbose_time_cursor(start, 10);
3904 show_verbose_time_cursor(pending_position, 10);
3909 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3911 if (!drag_info.first_move) {
3912 drag_selection (item, event);
3913 /* XXX this is not object-oriented programming at all. ick */
3914 if (selection->time.consolidate()) {
3915 selection->TimeChanged ();
3917 commit_reversible_command ();
3919 /* just a click, no pointer movement.*/
3921 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3923 selection->clear_time();
3928 /* XXX what happens if its a music selection? */
3929 session->set_audio_range (selection->time);
3930 stop_canvas_autoscroll ();
3934 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3937 TimeAxisView* tvp = clicked_trackview;
3938 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3940 if (tv && tv->is_audio_track()) {
3941 speed = tv->get_diskstream()->speed();
3944 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3945 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3946 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3948 motion_frozen_playlists.clear();
3950 //drag_info.item = clicked_regionview->get_name_highlight();
3951 drag_info.item = item;
3952 drag_info.motion_callback = &Editor::trim_motion_callback;
3953 drag_info.finished_callback = &Editor::trim_finished_callback;
3955 start_grab (event, trimmer_cursor);
3957 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3958 trim_op = ContentsTrim;
3960 /* These will get overridden for a point trim.*/
3961 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3962 /* closer to start */
3963 trim_op = StartTrim;
3964 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3972 show_verbose_time_cursor(region_start, 10);
3975 show_verbose_time_cursor(region_end, 10);
3978 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3984 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3986 RegionView* rv = clicked_regionview;
3987 nframes_t frame_delta = 0;
3988 bool left_direction;
3989 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3991 /* snap modifier works differently here..
3992 its' current state has to be passed to the
3993 various trim functions in order to work properly
3997 TimeAxisView* tvp = clicked_trackview;
3998 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3999 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4001 if (tv && tv->is_audio_track()) {
4002 speed = tv->get_diskstream()->speed();
4005 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4006 left_direction = true;
4008 left_direction = false;
4012 snap_to (drag_info.current_pointer_frame);
4015 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4019 if (drag_info.first_move) {
4025 trim_type = "Region start trim";
4028 trim_type = "Region end trim";
4031 trim_type = "Region content trim";
4035 begin_reversible_command (trim_type);
4037 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4038 (*i)->region()->set_opaque(false);
4039 (*i)->region()->freeze ();
4041 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4043 arv->temporarily_hide_envelope ();
4045 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4046 insert_result = motion_frozen_playlists.insert (pl);
4047 if (insert_result.second) {
4048 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4053 if (left_direction) {
4054 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4056 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4061 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4064 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4065 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4071 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4074 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4075 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4082 bool swap_direction = false;
4084 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4085 swap_direction = true;
4088 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4089 i != selection->regions.by_layer().end(); ++i)
4091 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4099 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4102 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4105 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4109 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4110 drag_info.first_move = false;
4114 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4116 boost::shared_ptr<Region> region (rv.region());
4118 if (region->locked()) {
4122 nframes_t new_bound;
4125 TimeAxisView* tvp = clicked_trackview;
4126 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4128 if (tv && tv->is_audio_track()) {
4129 speed = tv->get_diskstream()->speed();
4132 if (left_direction) {
4133 if (swap_direction) {
4134 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4136 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4139 if (swap_direction) {
4140 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4142 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4147 snap_to (new_bound);
4149 region->trim_start ((nframes_t) (new_bound * speed), this);
4150 rv.region_changed (StartChanged);
4154 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4156 boost::shared_ptr<Region> region (rv.region());
4158 if (region->locked()) {
4162 nframes_t new_bound;
4165 TimeAxisView* tvp = clicked_trackview;
4166 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4168 if (tv && tv->is_audio_track()) {
4169 speed = tv->get_diskstream()->speed();
4172 if (left_direction) {
4173 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4175 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4179 snap_to (new_bound, (left_direction ? 0 : 1));
4182 region->trim_front ((nframes_t) (new_bound * speed), this);
4184 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4188 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4190 boost::shared_ptr<Region> region (rv.region());
4192 if (region->locked()) {
4196 nframes_t new_bound;
4199 TimeAxisView* tvp = clicked_trackview;
4200 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4202 if (tv && tv->is_audio_track()) {
4203 speed = tv->get_diskstream()->speed();
4206 if (left_direction) {
4207 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4209 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4213 snap_to (new_bound);
4215 region->trim_end ((nframes_t) (new_bound * speed), this);
4216 rv.region_changed (LengthChanged);
4220 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4222 if (!drag_info.first_move) {
4223 trim_motion_callback (item, event);
4225 if (!clicked_regionview->get_selected()) {
4226 thaw_region_after_trim (*clicked_regionview);
4229 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4230 i != selection->regions.by_layer().end(); ++i)
4232 thaw_region_after_trim (**i);
4233 (*i)->region()->set_opaque(true);
4237 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4239 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4242 motion_frozen_playlists.clear ();
4244 commit_reversible_command();
4246 /* no mouse movement */
4252 Editor::point_trim (GdkEvent* event)
4254 RegionView* rv = clicked_regionview;
4255 nframes_t new_bound = drag_info.current_pointer_frame;
4257 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4258 snap_to (new_bound);
4261 /* Choose action dependant on which button was pressed */
4262 switch (event->button.button) {
4264 trim_op = StartTrim;
4265 begin_reversible_command (_("Start point trim"));
4267 if (rv->get_selected()) {
4269 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4270 i != selection->regions.by_layer().end(); ++i)
4272 if (!(*i)->region()->locked()) {
4273 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4274 XMLNode &before = pl->get_state();
4275 (*i)->region()->trim_front (new_bound, this);
4276 XMLNode &after = pl->get_state();
4277 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4283 if (!rv->region()->locked()) {
4284 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4285 XMLNode &before = pl->get_state();
4286 rv->region()->trim_front (new_bound, this);
4287 XMLNode &after = pl->get_state();
4288 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4292 commit_reversible_command();
4297 begin_reversible_command (_("End point trim"));
4299 if (rv->get_selected()) {
4301 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4303 if (!(*i)->region()->locked()) {
4304 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4305 XMLNode &before = pl->get_state();
4306 (*i)->region()->trim_end (new_bound, this);
4307 XMLNode &after = pl->get_state();
4308 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4314 if (!rv->region()->locked()) {
4315 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4316 XMLNode &before = pl->get_state();
4317 rv->region()->trim_end (new_bound, this);
4318 XMLNode &after = pl->get_state();
4319 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4323 commit_reversible_command();
4332 Editor::thaw_region_after_trim (RegionView& rv)
4334 boost::shared_ptr<Region> region (rv.region());
4336 if (region->locked()) {
4340 region->thaw (_("trimmed region"));
4341 XMLNode &after = region->playlist()->get_state();
4342 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4344 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4346 arv->unhide_envelope ();
4350 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4355 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4356 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4360 Location* location = find_location_from_marker (marker, is_start);
4361 location->set_hidden (true, this);
4366 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4372 drag_info.item = item;
4373 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4374 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4376 range_marker_op = op;
4378 if (!temp_location) {
4379 temp_location = new Location;
4383 case CreateRangeMarker:
4384 case CreateTransportMarker:
4386 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4387 drag_info.copy = true;
4389 drag_info.copy = false;
4391 start_grab (event, selector_cursor);
4395 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4400 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4402 nframes_t start = 0;
4404 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4406 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4407 snap_to (drag_info.current_pointer_frame);
4410 /* only alter selection if the current frame is
4411 different from the last frame position.
4414 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4416 switch (range_marker_op) {
4417 case CreateRangeMarker:
4418 case CreateTransportMarker:
4419 if (drag_info.first_move) {
4420 snap_to (drag_info.grab_frame);
4423 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4424 start = drag_info.current_pointer_frame;
4425 end = drag_info.grab_frame;
4427 end = drag_info.current_pointer_frame;
4428 start = drag_info.grab_frame;
4431 /* first drag: Either add to the selection
4432 or create a new selection.
4435 if (drag_info.first_move) {
4437 temp_location->set (start, end);
4441 update_marker_drag_item (temp_location);
4442 range_marker_drag_rect->show();
4443 range_marker_drag_rect->raise_to_top();
4449 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4450 start_canvas_autoscroll (1);
4454 temp_location->set (start, end);
4456 double x1 = frame_to_pixel (start);
4457 double x2 = frame_to_pixel (end);
4458 crect->property_x1() = x1;
4459 crect->property_x2() = x2;
4461 update_marker_drag_item (temp_location);
4464 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4465 drag_info.first_move = false;
4467 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4472 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4474 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 newloc = new Location(temp_location->start(), temp_location->end(), "unnamed", Location::IsRangeMarker);
4485 session->locations()->add (newloc, true);
4486 XMLNode &after = session->locations()->get_state();
4487 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4488 commit_reversible_command ();
4490 range_bar_drag_rect->hide();
4491 range_marker_drag_rect->hide();
4495 case CreateTransportMarker:
4496 // popup menu to pick loop or punch
4497 new_transport_marker_context_menu (&event->button, item);
4502 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4504 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4509 start = session->locations()->first_mark_before (drag_info.grab_frame);
4510 end = session->locations()->first_mark_after (drag_info.grab_frame);
4512 if (end == max_frames) {
4513 end = session->current_end_frame ();
4517 start = session->current_start_frame ();
4520 switch (mouse_mode) {
4522 /* find the two markers on either side and then make the selection from it */
4523 cerr << "select between " << start << " .. " << end << endl;
4524 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4528 /* find the two markers on either side of the click and make the range out of it */
4529 selection->set (0, start, end);
4538 stop_canvas_autoscroll ();
4544 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4546 drag_info.item = item;
4547 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4548 drag_info.finished_callback = &Editor::end_mouse_zoom;
4550 start_grab (event, zoom_cursor);
4552 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4556 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4561 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4562 snap_to (drag_info.current_pointer_frame);
4564 if (drag_info.first_move) {
4565 snap_to (drag_info.grab_frame);
4569 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4571 /* base start and end on initial click position */
4572 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4573 start = drag_info.current_pointer_frame;
4574 end = drag_info.grab_frame;
4576 end = drag_info.current_pointer_frame;
4577 start = drag_info.grab_frame;
4582 if (drag_info.first_move) {
4584 zoom_rect->raise_to_top();
4587 reposition_zoom_rect(start, end);
4589 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4590 drag_info.first_move = false;
4592 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4597 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4599 if (!drag_info.first_move) {
4600 drag_mouse_zoom (item, event);
4602 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4603 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4605 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4608 temporal_zoom_to_frame (false, drag_info.grab_frame);
4610 temporal_zoom_step (false);
4611 center_screen (drag_info.grab_frame);
4619 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4621 double x1 = frame_to_pixel (start);
4622 double x2 = frame_to_pixel (end);
4623 double y2 = full_canvas_height - 1.0;
4625 zoom_rect->property_x1() = x1;
4626 zoom_rect->property_y1() = 1.0;
4627 zoom_rect->property_x2() = x2;
4628 zoom_rect->property_y2() = y2;
4632 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4634 drag_info.item = item;
4635 drag_info.motion_callback = &Editor::drag_rubberband_select;
4636 drag_info.finished_callback = &Editor::end_rubberband_select;
4638 start_grab (event, cross_hair_cursor);
4640 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4644 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4651 /* use a bigger drag threshold than the default */
4653 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4657 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4658 // snap_to (drag_info.current_pointer_frame);
4660 // if (drag_info.first_move) {
4661 // snap_to (drag_info.grab_frame);
4666 /* base start and end on initial click position */
4667 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4668 start = drag_info.current_pointer_frame;
4669 end = drag_info.grab_frame;
4671 end = drag_info.current_pointer_frame;
4672 start = drag_info.grab_frame;
4675 if (drag_info.current_pointer_y < drag_info.grab_y) {
4676 y1 = drag_info.current_pointer_y;
4677 y2 = drag_info.grab_y;
4680 y2 = drag_info.current_pointer_y;
4681 y1 = drag_info.grab_y;
4685 if (start != end || y1 != y2) {
4687 double x1 = frame_to_pixel (start);
4688 double x2 = frame_to_pixel (end);
4690 rubberband_rect->property_x1() = x1;
4691 rubberband_rect->property_y1() = y1;
4692 rubberband_rect->property_x2() = x2;
4693 rubberband_rect->property_y2() = y2;
4695 rubberband_rect->show();
4696 rubberband_rect->raise_to_top();
4698 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4699 drag_info.first_move = false;
4701 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4706 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4708 if (!drag_info.first_move) {
4710 drag_rubberband_select (item, event);
4713 if (drag_info.current_pointer_y < drag_info.grab_y) {
4714 y1 = drag_info.current_pointer_y;
4715 y2 = drag_info.grab_y;
4718 y2 = drag_info.current_pointer_y;
4719 y1 = drag_info.grab_y;
4723 Selection::Operation op = Keyboard::selection_type (event->button.state);
4726 begin_reversible_command (_("select regions"));
4728 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4729 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4731 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4735 commit_reversible_command ();
4739 selection->clear_regions();
4740 selection->clear_points ();
4741 selection->clear_lines ();
4744 rubberband_rect->hide();
4749 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4751 using namespace Gtkmm2ext;
4753 ArdourPrompter prompter (false);
4755 prompter.set_prompt (_("Name for region:"));
4756 prompter.set_initial_text (clicked_regionview->region()->name());
4757 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4758 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4759 prompter.show_all ();
4760 switch (prompter.run ()) {
4761 case Gtk::RESPONSE_ACCEPT:
4763 prompter.get_result(str);
4765 clicked_regionview->region()->set_name (str);
4773 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4775 drag_info.item = item;
4776 drag_info.motion_callback = &Editor::time_fx_motion;
4777 drag_info.finished_callback = &Editor::end_time_fx;
4781 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4785 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4787 RegionView* rv = clicked_regionview;
4789 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4790 snap_to (drag_info.current_pointer_frame);
4793 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4797 if (drag_info.current_pointer_frame > rv->region()->position()) {
4798 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4801 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4802 drag_info.first_move = false;
4804 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4808 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4810 clicked_regionview->get_time_axis_view().hide_timestretch ();
4812 if (drag_info.first_move) {
4816 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4817 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4819 begin_reversible_command (_("timestretch"));
4821 if (run_timestretch (selection->regions, percentage) == 0) {
4822 session->commit_reversible_command ();
4827 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4829 /* no brushing without a useful snap setting */
4832 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4835 switch (snap_mode) {
4837 return; /* can't work because it allows region to be placed anywhere */
4842 switch (snap_type) {
4845 case SnapToEditCursor:
4852 /* don't brush a copy over the original */
4854 if (pos == rv->region()->position()) {
4858 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4860 if (atv == 0 || !atv->is_audio_track()) {
4864 boost::shared_ptr<Playlist> playlist = atv->playlist();
4865 double speed = atv->get_diskstream()->speed();
4867 XMLNode &before = playlist->get_state();
4868 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4869 XMLNode &after = playlist->get_state();
4870 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4872 // playlist is frozen, so we have to update manually
4874 playlist->Modified(); /* EMIT SIGNAL */
4878 Editor::track_height_step_timeout ()
4881 struct timeval delta;
4883 gettimeofday (&now, 0);
4884 timersub (&now, &last_track_height_step_timestamp, &delta);
4886 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4887 current_stepping_trackview = 0;