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)
294 /* in object/audition/timefx mode, any button press sets
295 the selection if the object can be selected. this is a
296 bit of hack, because we want to avoid this if the
297 mouse operation is a region alignment.
299 note: not dbl-click or triple-click
302 if (((mouse_mode != MouseObject) &&
303 (mouse_mode != MouseAudition || item_type != RegionItem) &&
304 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
305 (mouse_mode != MouseRange)) ||
307 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
312 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
314 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
316 /* no selection action on modified button-2 or button-3 events */
322 Selection::Operation op = Keyboard::selection_type (event->button.state);
323 bool press = (event->type == GDK_BUTTON_PRESS);
325 begin_reversible_command (_("select on click"));
329 if (mouse_mode != MouseRange) {
330 commit = set_selected_regionview_from_click (press, op, true);
334 case RegionViewNameHighlight:
336 if (mouse_mode != MouseRange) {
337 commit = set_selected_regionview_from_click (press, op, true);
341 case FadeInHandleItem:
343 case FadeOutHandleItem:
345 if (mouse_mode != MouseRange) {
346 commit = set_selected_regionview_from_click (press, op, true);
350 case GainAutomationControlPointItem:
351 case PanAutomationControlPointItem:
352 case RedirectAutomationControlPointItem:
353 if (mouse_mode != MouseRange) {
354 commit = set_selected_control_point_from_click (op, false);
359 /* for context click or range selection, select track */
360 if (event->button.button == 3) {
361 commit = set_selected_track_from_click (press, op, true);
362 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
363 commit = set_selected_track_from_click (press, op, false);
367 case AutomationTrackItem:
368 commit = set_selected_track_from_click (press, op, true);
376 commit_reversible_command ();
381 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
383 nframes_t where = event_frame (event, 0, 0);
385 track_canvas.grab_focus();
387 if (session && session->actively_recording()) {
391 button_selection (item, event, item_type);
393 if (drag_info.item == 0 &&
394 (Keyboard::is_delete_event (&event->button) ||
395 Keyboard::is_context_menu_event (&event->button) ||
396 Keyboard::is_edit_event (&event->button))) {
398 /* handled by button release */
402 switch (event->button.button) {
405 if (event->type == GDK_BUTTON_PRESS) {
407 if (drag_info.item) {
408 drag_info.item->ungrab (event->button.time);
411 /* single mouse clicks on any of these item types operate
412 independent of mouse mode, mostly because they are
413 not on the main track canvas or because we want
419 case PlayheadCursorItem:
420 start_cursor_grab (item, event);
424 if (Keyboard::modifier_state_equals (event->button.state,
425 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
426 hide_marker (item, event);
428 start_marker_grab (item, event);
432 case TempoMarkerItem:
433 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
434 start_tempo_marker_copy_grab (item, event);
436 start_tempo_marker_grab (item, event);
440 case MeterMarkerItem:
441 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
442 start_meter_marker_copy_grab (item, event);
444 start_meter_marker_grab (item, event);
454 case RangeMarkerBarItem:
455 start_range_markerbar_op (item, event, CreateRangeMarker);
459 case TransportMarkerBarItem:
460 start_range_markerbar_op (item, event, CreateTransportMarker);
469 switch (mouse_mode) {
472 case StartSelectionTrimItem:
473 start_selection_op (item, event, SelectionStartTrim);
476 case EndSelectionTrimItem:
477 start_selection_op (item, event, SelectionEndTrim);
481 if (Keyboard::modifier_state_contains
482 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
483 // contains and not equals because I can't use alt as a modifier alone.
484 start_selection_grab (item, event);
485 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
486 /* grab selection for moving */
487 start_selection_op (item, event, SelectionMove);
490 /* this was debated, but decided the more common action was to
491 make a new selection */
492 start_selection_op (item, event, CreateSelection);
497 start_selection_op (item, event, CreateSelection);
503 if (Keyboard::modifier_state_contains (event->button.state, 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 ((int32_t)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();
1785 /* mapover the region selection */
1787 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1789 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1795 tmp->reset_fade_in_shape_width (fade_length);
1798 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1800 drag_info.first_move = false;
1804 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1806 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1808 nframes_t fade_length;
1810 if (drag_info.first_move) return;
1812 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1813 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1818 if (pos < (arv->region()->position() + 64)) {
1819 fade_length = 64; // this should be a minimum defined somewhere
1820 } else if (pos > arv->region()->last_frame()) {
1821 fade_length = arv->region()->length();
1823 fade_length = pos - arv->region()->position();
1826 begin_reversible_command (_("change fade in length"));
1828 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1830 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1836 AutomationList& alist = tmp->audio_region()->fade_in();
1837 XMLNode &before = alist.get_state();
1839 tmp->audio_region()->set_fade_in_length (fade_length);
1841 XMLNode &after = alist.get_state();
1842 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1845 commit_reversible_command ();
1849 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1851 drag_info.item = item;
1852 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1853 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1857 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1858 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1862 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1864 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1868 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1870 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1872 nframes_t fade_length;
1874 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1875 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1881 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1885 if (pos > (arv->region()->last_frame() - 64)) {
1886 fade_length = 64; // this should really be a minimum fade defined somewhere
1888 else if (pos < arv->region()->position()) {
1889 fade_length = arv->region()->length();
1892 fade_length = arv->region()->last_frame() - pos;
1895 /* mapover the region selection */
1897 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1899 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1905 tmp->reset_fade_out_shape_width (fade_length);
1908 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1910 drag_info.first_move = false;
1914 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1916 if (drag_info.first_move) return;
1918 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1920 nframes_t fade_length;
1922 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1923 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1929 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1933 if (pos > (arv->region()->last_frame() - 64)) {
1934 fade_length = 64; // this should really be a minimum fade defined somewhere
1936 else if (pos < arv->region()->position()) {
1937 fade_length = arv->region()->length();
1940 fade_length = arv->region()->last_frame() - pos;
1943 begin_reversible_command (_("change fade out length"));
1945 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1947 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1953 AutomationList& alist = tmp->audio_region()->fade_out();
1954 XMLNode &before = alist.get_state();
1956 tmp->audio_region()->set_fade_out_length (fade_length);
1958 XMLNode &after = alist.get_state();
1959 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1962 commit_reversible_command ();
1966 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1968 drag_info.item = item;
1969 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1970 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1974 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1975 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1979 Cursor* cursor = (Cursor *) drag_info.data;
1981 if (cursor == playhead_cursor) {
1982 _dragging_playhead = true;
1984 if (session && drag_info.was_rolling) {
1985 session->request_stop ();
1989 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1991 show_verbose_time_cursor (cursor->current_frame, 10);
1995 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1997 Cursor* cursor = (Cursor *) drag_info.data;
1998 nframes_t adjusted_frame;
2000 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2001 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2007 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2008 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
2009 snap_to (adjusted_frame);
2013 if (adjusted_frame == drag_info.last_pointer_frame) return;
2015 cursor->set_position (adjusted_frame);
2017 if (cursor == edit_cursor) {
2018 edit_cursor_clock.set (cursor->current_frame);
2020 UpdateAllTransportClocks (cursor->current_frame);
2023 show_verbose_time_cursor (cursor->current_frame, 10);
2025 drag_info.last_pointer_frame = adjusted_frame;
2026 drag_info.first_move = false;
2030 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2032 if (drag_info.first_move) return;
2034 cursor_drag_motion_callback (item, event);
2036 _dragging_playhead = false;
2038 if (item == &playhead_cursor->canvas_item) {
2040 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2042 } else if (item == &edit_cursor->canvas_item) {
2043 edit_cursor->set_position (edit_cursor->current_frame);
2044 edit_cursor_clock.set (edit_cursor->current_frame);
2049 Editor::update_marker_drag_item (Location *location)
2051 double x1 = frame_to_pixel (location->start());
2052 double x2 = frame_to_pixel (location->end());
2054 if (location->is_mark()) {
2055 marker_drag_line_points.front().set_x(x1);
2056 marker_drag_line_points.back().set_x(x1);
2057 marker_drag_line->property_points() = marker_drag_line_points;
2060 range_marker_drag_rect->property_x1() = x1;
2061 range_marker_drag_rect->property_x2() = x2;
2066 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2070 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2071 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2077 Location *location = find_location_from_marker (marker, is_start);
2079 drag_info.item = item;
2080 drag_info.data = marker;
2081 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2082 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2086 drag_info.copied_location = new Location (*location);
2087 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2089 update_marker_drag_item (location);
2091 if (location->is_mark()) {
2092 marker_drag_line->show();
2093 marker_drag_line->raise_to_top();
2096 range_marker_drag_rect->show();
2097 range_marker_drag_rect->raise_to_top();
2100 if (is_start) show_verbose_time_cursor (location->start(), 10);
2101 else show_verbose_time_cursor (location->end(), 10);
2105 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2108 Marker* marker = (Marker *) drag_info.data;
2109 Location *real_location;
2110 Location *copy_location;
2112 bool move_both = false;
2116 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2117 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2123 nframes_t next = newframe;
2125 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2126 snap_to (newframe, 0, true);
2129 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2133 /* call this to find out if its the start or end */
2135 real_location = find_location_from_marker (marker, is_start);
2137 /* use the copy that we're "dragging" around */
2139 copy_location = drag_info.copied_location;
2141 f_delta = copy_location->end() - copy_location->start();
2143 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2147 if (copy_location->is_mark()) {
2150 copy_location->set_start (newframe);
2154 if (is_start) { // start-of-range marker
2157 copy_location->set_start (newframe);
2158 copy_location->set_end (newframe + f_delta);
2159 } else if (newframe < copy_location->end()) {
2160 copy_location->set_start (newframe);
2162 snap_to (next, 1, true);
2163 copy_location->set_end (next);
2164 copy_location->set_start (newframe);
2167 } else { // end marker
2170 copy_location->set_end (newframe);
2171 copy_location->set_start (newframe - f_delta);
2172 } else if (newframe > copy_location->start()) {
2173 copy_location->set_end (newframe);
2175 } else if (newframe > 0) {
2176 snap_to (next, -1, true);
2177 copy_location->set_start (next);
2178 copy_location->set_end (newframe);
2183 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2184 drag_info.first_move = false;
2186 update_marker_drag_item (copy_location);
2188 LocationMarkers* lm = find_location_markers (real_location);
2189 lm->set_position (copy_location->start(), copy_location->end());
2191 show_verbose_time_cursor (newframe, 10);
2195 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2197 if (drag_info.first_move) {
2198 marker_drag_motion_callback (item, event);
2202 Marker* marker = (Marker *) drag_info.data;
2206 begin_reversible_command ( _("move marker") );
2207 XMLNode &before = session->locations()->get_state();
2209 Location * location = find_location_from_marker (marker, is_start);
2212 if (location->is_mark()) {
2213 location->set_start (drag_info.copied_location->start());
2215 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2219 XMLNode &after = session->locations()->get_state();
2220 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2221 commit_reversible_command ();
2223 marker_drag_line->hide();
2224 range_marker_drag_rect->hide();
2228 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2231 MeterMarker* meter_marker;
2233 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2234 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2238 meter_marker = dynamic_cast<MeterMarker*> (marker);
2240 MetricSection& section (meter_marker->meter());
2242 if (!section.movable()) {
2246 drag_info.item = item;
2247 drag_info.data = 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::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2262 MeterMarker* meter_marker;
2264 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2265 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2269 meter_marker = dynamic_cast<MeterMarker*> (marker);
2271 // create a dummy marker for visual representation of moving the copy.
2272 // The actual copying is not done before we reach the finish callback.
2274 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2275 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2276 *new MeterSection(meter_marker->meter()));
2278 drag_info.item = &new_marker->the_item();
2279 drag_info.copy = true;
2280 drag_info.data = new_marker;
2281 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2282 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2286 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2288 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2292 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2294 MeterMarker* marker = (MeterMarker *) drag_info.data;
2295 nframes_t adjusted_frame;
2297 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2298 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2304 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2305 snap_to (adjusted_frame);
2308 if (adjusted_frame == drag_info.last_pointer_frame) return;
2310 marker->set_position (adjusted_frame);
2313 drag_info.last_pointer_frame = adjusted_frame;
2314 drag_info.first_move = false;
2316 show_verbose_time_cursor (adjusted_frame, 10);
2320 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2322 if (drag_info.first_move) return;
2324 meter_marker_drag_motion_callback (drag_info.item, event);
2326 MeterMarker* marker = (MeterMarker *) drag_info.data;
2329 TempoMap& map (session->tempo_map());
2330 map.bbt_time (drag_info.last_pointer_frame, when);
2332 if (drag_info.copy == true) {
2333 begin_reversible_command (_("copy meter mark"));
2334 XMLNode &before = map.get_state();
2335 map.add_meter (marker->meter(), when);
2336 XMLNode &after = map.get_state();
2337 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2338 commit_reversible_command ();
2340 // delete the dummy marker we used for visual representation of copying.
2341 // a new visual marker will show up automatically.
2344 begin_reversible_command (_("move meter mark"));
2345 XMLNode &before = map.get_state();
2346 map.move_meter (marker->meter(), when);
2347 XMLNode &after = map.get_state();
2348 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2349 commit_reversible_command ();
2354 Editor::start_tempo_marker_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 MetricSection& section (tempo_marker->tempo());
2371 if (!section.movable()) {
2375 drag_info.item = item;
2376 drag_info.data = marker;
2377 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2378 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2382 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2383 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2387 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2390 TempoMarker* tempo_marker;
2392 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2393 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2397 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2398 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2402 // create a dummy marker for visual representation of moving the copy.
2403 // The actual copying is not done before we reach the finish callback.
2405 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2406 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2407 *new TempoSection(tempo_marker->tempo()));
2409 drag_info.item = &new_marker->the_item();
2410 drag_info.copy = true;
2411 drag_info.data = new_marker;
2412 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2413 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2417 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2419 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2423 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2425 TempoMarker* marker = (TempoMarker *) drag_info.data;
2426 nframes_t adjusted_frame;
2428 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2429 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2435 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2436 snap_to (adjusted_frame);
2439 if (adjusted_frame == drag_info.last_pointer_frame) return;
2441 /* OK, we've moved far enough to make it worth actually move the thing. */
2443 marker->set_position (adjusted_frame);
2445 show_verbose_time_cursor (adjusted_frame, 10);
2447 drag_info.last_pointer_frame = adjusted_frame;
2448 drag_info.first_move = false;
2452 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2454 if (drag_info.first_move) return;
2456 tempo_marker_drag_motion_callback (drag_info.item, event);
2458 TempoMarker* marker = (TempoMarker *) drag_info.data;
2461 TempoMap& map (session->tempo_map());
2462 map.bbt_time (drag_info.last_pointer_frame, when);
2464 if (drag_info.copy == true) {
2465 begin_reversible_command (_("copy tempo mark"));
2466 XMLNode &before = map.get_state();
2467 map.add_tempo (marker->tempo(), when);
2468 XMLNode &after = map.get_state();
2469 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2470 commit_reversible_command ();
2472 // delete the dummy marker we used for visual representation of copying.
2473 // a new visual marker will show up automatically.
2476 begin_reversible_command (_("move tempo mark"));
2477 XMLNode &before = map.get_state();
2478 map.move_tempo (marker->tempo(), when);
2479 XMLNode &after = map.get_state();
2480 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2481 commit_reversible_command ();
2486 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2488 ControlPoint* control_point;
2490 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2491 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2495 // We shouldn't remove the first or last gain point
2496 if (control_point->line.is_last_point(*control_point) ||
2497 control_point->line.is_first_point(*control_point)) {
2501 control_point->line.remove_point (*control_point);
2505 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2507 ControlPoint* control_point;
2509 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2510 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2514 control_point->line.remove_point (*control_point);
2518 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2520 ControlPoint* control_point;
2522 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2523 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2527 drag_info.item = item;
2528 drag_info.data = control_point;
2529 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2530 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2532 start_grab (event, fader_cursor);
2534 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2536 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2537 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2538 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2540 show_verbose_canvas_cursor ();
2544 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2546 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2548 double cx = drag_info.current_pointer_x;
2549 double cy = drag_info.current_pointer_y;
2551 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2552 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2554 if (drag_info.x_constrained) {
2555 cx = drag_info.grab_x;
2557 if (drag_info.y_constrained) {
2558 cy = drag_info.grab_y;
2561 cp->line.parent_group().w2i (cx, cy);
2565 cy = min ((double) cp->line.height(), cy);
2567 //translate cx to frames
2568 nframes_t cx_frames = unit_to_frame (cx);
2570 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2571 snap_to (cx_frames);
2574 float fraction = 1.0 - (cy / cp->line.height());
2578 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2584 cp->line.point_drag (*cp, cx_frames , fraction, push);
2586 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2588 drag_info.first_move = false;
2592 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2594 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2596 if (drag_info.first_move) {
2600 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2601 reset_point_selection ();
2605 control_point_drag_motion_callback (item, event);
2607 cp->line.end_drag (cp);
2611 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2613 switch (mouse_mode) {
2615 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2616 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2624 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2628 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2629 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2633 start_line_grab (al, event);
2637 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2641 nframes_t frame_within_region;
2643 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2647 cx = event->button.x;
2648 cy = event->button.y;
2649 line->parent_group().w2i (cx, cy);
2650 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2652 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2653 current_line_drag_info.after)) {
2654 /* no adjacent points */
2658 drag_info.item = &line->grab_item();
2659 drag_info.data = line;
2660 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2661 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2663 start_grab (event, fader_cursor);
2665 double fraction = 1.0 - (cy / line->height());
2667 line->start_drag (0, drag_info.grab_frame, fraction);
2669 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2670 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2671 show_verbose_canvas_cursor ();
2675 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2677 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2678 double cx = drag_info.current_pointer_x;
2679 double cy = drag_info.current_pointer_y;
2681 line->parent_group().w2i (cx, cy);
2684 fraction = 1.0 - (cy / line->height());
2688 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2694 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2696 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2700 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2702 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2703 line_drag_motion_callback (item, event);
2708 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2710 if (selection->regions.empty() || clicked_regionview == 0) {
2714 drag_info.copy = false;
2715 drag_info.item = item;
2716 drag_info.data = clicked_regionview;
2717 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2718 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2723 TimeAxisView* tvp = clicked_trackview;
2724 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2726 if (tv && tv->is_audio_track()) {
2727 speed = tv->get_diskstream()->speed();
2730 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2731 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2732 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2733 // we want a move threshold
2734 drag_info.want_move_threshold = true;
2736 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2738 begin_reversible_command (_("move region(s)"));
2742 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2744 if (selection->regions.empty() || clicked_regionview == 0) {
2748 drag_info.copy = true;
2749 drag_info.item = item;
2750 drag_info.data = clicked_regionview;
2754 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2755 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2758 if (atv && atv->is_audio_track()) {
2759 speed = atv->get_diskstream()->speed();
2762 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2763 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2764 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2765 // we want a move threshold
2766 drag_info.want_move_threshold = true;
2767 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2768 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2769 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2773 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2775 if (selection->regions.empty() || clicked_regionview == 0) {
2779 drag_info.copy = false;
2780 drag_info.item = item;
2781 drag_info.data = clicked_regionview;
2782 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2783 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2788 TimeAxisView* tvp = clicked_trackview;
2789 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2791 if (tv && tv->is_audio_track()) {
2792 speed = tv->get_diskstream()->speed();
2795 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2796 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2797 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2798 // we want a move threshold
2799 drag_info.want_move_threshold = true;
2800 drag_info.brushing = true;
2802 begin_reversible_command (_("Drag region brush"));
2806 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2810 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2811 nframes_t pending_region_position = 0;
2812 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2813 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2814 bool clamp_y_axis = false;
2815 vector<int32_t> height_list(512) ;
2816 vector<int32_t>::iterator j;
2818 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2820 drag_info.want_move_threshold = false; // don't copy again
2822 /* this is committed in the grab finished callback. */
2824 begin_reversible_command (_("Drag region copy"));
2826 /* duplicate the region(s) */
2828 vector<RegionView*> new_regionviews;
2830 set<boost::shared_ptr<Playlist> > affected_playlists;
2831 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2833 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2838 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
2839 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2841 insert_result = affected_playlists.insert (to_playlist);
2842 if (insert_result.second) {
2843 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
2846 latest_regionview = 0;
2848 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2850 /* create a new region with the same name. */
2852 // FIXME: ew. need a (virtual) Region::duplicate() or something?
2854 boost::shared_ptr<Region> newregion;
2855 boost::shared_ptr<Region> ar;
2857 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
2858 newregion = RegionFactory::create (ar);
2860 assert(newregion != 0);
2862 /* if the original region was locked, we don't care */
2864 newregion->set_locked (false);
2866 to_playlist->add_region (newregion, (nframes_t) (rv->region()->position() * atv->get_diskstream()->speed()));
2870 if (latest_regionview) {
2871 new_regionviews.push_back (latest_regionview);
2877 if (new_regionviews.empty()) {
2881 /* reset selection to new regionviews */
2883 selection->set (new_regionviews);
2885 /* reset drag_info data to reflect the fact that we are dragging the copies */
2887 drag_info.data = new_regionviews.front();
2888 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2891 /* Which trackview is this ? */
2893 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2894 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2896 /* The region motion is only processed if the pointer is over
2900 if (!tv || !tv->is_audio_track()) {
2901 /* To make sure we hide the verbose canvas cursor when the mouse is
2902 not held over and audiotrack.
2904 hide_verbose_canvas_cursor ();
2908 original_pointer_order = drag_info.last_trackview->order;
2910 /************************************************************
2912 ************************************************************/
2914 if (drag_info.brushing) {
2915 clamp_y_axis = true;
2920 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2922 int32_t children = 0, numtracks = 0;
2923 // XXX hard coding track limit, oh my, so very very bad
2924 bitset <1024> tracks (0x00);
2925 /* get a bitmask representing the visible tracks */
2927 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2928 TimeAxisView *tracklist_timeview;
2929 tracklist_timeview = (*i);
2930 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2931 list<TimeAxisView*> children_list;
2933 /* zeroes are audio tracks. ones are other types. */
2935 if (!atv2->hidden()) {
2937 if (visible_y_high < atv2->order) {
2938 visible_y_high = atv2->order;
2940 if (visible_y_low > atv2->order) {
2941 visible_y_low = atv2->order;
2944 if (!atv2->is_audio_track()) {
2945 tracks = tracks |= (0x01 << atv2->order);
2948 height_list[atv2->order] = (*i)->height;
2950 if ((children_list = atv2->get_child_list()).size() > 0) {
2951 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2952 tracks = tracks |= (0x01 << (atv2->order + children));
2953 height_list[atv2->order + children] = (*j)->height;
2961 /* find the actual span according to the canvas */
2963 canvas_pointer_y_span = pointer_y_span;
2964 if (drag_info.last_trackview->order >= tv->order) {
2966 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2967 if (height_list[y] == 0 ) {
2968 canvas_pointer_y_span--;
2973 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2974 if ( height_list[y] == 0 ) {
2975 canvas_pointer_y_span++;
2980 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2981 RegionView* rv2 = (*i);
2982 double ix1, ix2, iy1, iy2;
2985 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2986 rv2->get_canvas_group()->i2w (ix1, iy1);
2987 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2988 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2990 if (atv2->order != original_pointer_order) {
2991 /* this isn't the pointer track */
2993 if (canvas_pointer_y_span > 0) {
2995 /* moving up the canvas */
2996 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2998 int32_t visible_tracks = 0;
2999 while (visible_tracks < canvas_pointer_y_span ) {
3002 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3003 /* we're passing through a hidden track */
3008 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3009 clamp_y_axis = true;
3013 clamp_y_axis = true;
3016 } else if (canvas_pointer_y_span < 0) {
3018 /*moving down the canvas*/
3020 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3023 int32_t visible_tracks = 0;
3025 while (visible_tracks > canvas_pointer_y_span ) {
3028 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3032 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3033 clamp_y_axis = true;
3038 clamp_y_axis = true;
3044 /* this is the pointer's track */
3045 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3046 clamp_y_axis = true;
3047 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3048 clamp_y_axis = true;
3056 } else if (drag_info.last_trackview == tv) {
3057 clamp_y_axis = true;
3061 if (!clamp_y_axis) {
3062 drag_info.last_trackview = tv;
3065 /************************************************************
3067 ************************************************************/
3069 /* compute the amount of pointer motion in frames, and where
3070 the region would be if we moved it by that much.
3073 if (drag_info.move_threshold_passed) {
3075 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3077 nframes_t sync_frame;
3078 nframes_t sync_offset;
3081 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3083 sync_offset = rv->region()->sync_offset (sync_dir);
3084 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3086 /* we snap if the snap modifier is not enabled.
3089 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3090 snap_to (sync_frame);
3093 if (sync_frame - sync_offset <= sync_frame) {
3094 pending_region_position = sync_frame - (sync_dir*sync_offset);
3096 pending_region_position = 0;
3100 pending_region_position = 0;
3103 if (pending_region_position > max_frames - rv->region()->length()) {
3104 pending_region_position = drag_info.last_frame_position;
3107 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3109 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3111 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3112 to make it appear at the new location.
3115 if (pending_region_position > drag_info.last_frame_position) {
3116 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3118 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3121 drag_info.last_frame_position = pending_region_position;
3128 /* threshold not passed */
3133 /*************************************************************
3135 ************************************************************/
3137 if (x_delta == 0 && (pointer_y_span == 0)) {
3138 /* haven't reached next snap point, and we're not switching
3139 trackviews. nothing to do.
3145 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3147 RegionView* rv2 = (*i);
3149 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3151 double ix1, ix2, iy1, iy2;
3152 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3153 rv2->get_canvas_group()->i2w (ix1, iy1);
3162 /*************************************************************
3164 ************************************************************/
3166 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3167 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3169 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3171 RegionView* rv = (*i);
3172 double ix1, ix2, iy1, iy2;
3173 int32_t temp_pointer_y_span = pointer_y_span;
3175 /* get item BBox, which will be relative to parent. so we have
3176 to query on a child, then convert to world coordinates using
3180 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3181 rv->get_canvas_group()->i2w (ix1, iy1);
3182 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3183 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3184 AudioTimeAxisView* temp_atv;
3186 if ((pointer_y_span != 0) && !clamp_y_axis) {
3189 for (j = height_list.begin(); j!= height_list.end(); j++) {
3190 if (x == canvas_atv->order) {
3191 /* we found the track the region is on */
3192 if (x != original_pointer_order) {
3193 /*this isn't from the same track we're dragging from */
3194 temp_pointer_y_span = canvas_pointer_y_span;
3196 while (temp_pointer_y_span > 0) {
3197 /* we're moving up canvas-wise,
3198 so we need to find the next track height
3200 if (j != height_list.begin()) {
3203 if (x != original_pointer_order) {
3204 /* we're not from the dragged track, so ignore hidden tracks. */
3206 temp_pointer_y_span++;
3210 temp_pointer_y_span--;
3212 while (temp_pointer_y_span < 0) {
3214 if (x != original_pointer_order) {
3216 temp_pointer_y_span--;
3220 if (j != height_list.end()) {
3223 temp_pointer_y_span++;
3225 /* find out where we'll be when we move and set height accordingly */
3227 tvp2 = trackview_by_y_position (iy1 + y_delta);
3228 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3229 rv->set_height (temp_atv->height);
3231 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3232 personally, i think this can confuse things, but never mind.
3235 //const GdkColor& col (temp_atv->view->get_region_color());
3236 //rv->set_color (const_cast<GdkColor&>(col));
3243 /* prevent the regionview from being moved to before
3244 the zero position on the canvas.
3249 if (-x_delta > ix1) {
3252 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3253 x_delta = max_frames - rv->region()->last_frame();
3256 if (drag_info.first_move) {
3258 /* hide any dependent views */
3260 rv->get_time_axis_view().hide_dependent_views (*rv);
3262 /* this is subtle. raising the regionview itself won't help,
3263 because raise_to_top() just puts the item on the top of
3264 its parent's stack. so, we need to put the trackview canvas_display group
3265 on the top, since its parent is the whole canvas.
3268 rv->get_canvas_group()->raise_to_top();
3269 rv->get_time_axis_view().canvas_display->raise_to_top();
3270 cursor_group->raise_to_top();
3272 /* freeze the playlists from notifying till
3276 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3277 if (atv && atv->is_audio_track()) {
3278 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>(atv->get_diskstream()->playlist());
3280 /* only freeze and capture state once */
3282 insert_result = motion_frozen_playlists.insert (pl);
3283 if (insert_result.second) {
3285 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
3289 rv->region()->set_opaque(false);
3292 if (drag_info.brushing) {
3293 mouse_brush_insert_region (rv, pending_region_position);
3295 rv->move (x_delta, y_delta);
3299 if (drag_info.first_move) {
3300 cursor_group->raise_to_top();
3303 drag_info.first_move = false;
3305 if (x_delta != 0 && !drag_info.brushing) {
3306 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3312 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3315 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3316 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3317 bool nocommit = true;
3319 RouteTimeAxisView* atv;
3320 bool regionview_y_movement;
3321 bool regionview_x_movement;
3323 /* first_move is set to false if the regionview has been moved in the
3327 if (drag_info.first_move) {
3334 /* The regionview has been moved at some stage during the grab so we need
3335 to account for any mouse movement between this event and the last one.
3338 region_drag_motion_callback (item, event);
3340 if (drag_info.brushing) {
3341 /* all changes were made during motion event handlers */
3345 /* adjust for track speed */
3348 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3349 if (atv && atv->get_diskstream()) {
3350 speed = atv->get_diskstream()->speed();
3353 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3354 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3356 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3357 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3359 if (regionview_y_movement) {
3361 /* motion between tracks */
3363 list<RegionView*> new_selection;
3365 /* moved to a different audio track. */
3367 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3369 RegionView* rv2 = (*i);
3371 /* the region that used to be in the old playlist is not
3372 moved to the new one - we make a copy of it. as a result,
3373 any existing editor for the region should no longer be
3377 if (!drag_info.copy) {
3378 rv2->hide_region_editor();
3380 new_selection.push_back (rv2);
3384 /* first, freeze the target tracks */
3386 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3388 boost::shared_ptr<Playlist> from_playlist;
3389 boost::shared_ptr<Playlist> to_playlist;
3391 double ix1, ix2, iy1, iy2;
3393 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3394 (*i)->get_canvas_group()->i2w (ix1, iy1);
3395 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3396 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3398 (*i)->region()->set_opaque (true);
3400 from_playlist = (*i)->region()->playlist();
3401 to_playlist = atv2->playlist();
3403 /* the from_playlist was frozen in the "first_move" case
3404 of the motion handler. the insert can fail,
3405 but that doesn't matter. it just means
3406 we already have the playlist in the list.
3409 motion_frozen_playlists.insert (from_playlist);
3411 /* only freeze the to_playlist once */
3413 insert_result = motion_frozen_playlists.insert(to_playlist);
3414 if (insert_result.second) {
3415 to_playlist->freeze();
3416 session->add_command(new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3421 /* now do it again with the actual operations */
3423 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3425 boost::shared_ptr<Playlist> from_playlist;
3426 boost::shared_ptr<Playlist> to_playlist;
3428 double ix1, ix2, iy1, iy2;
3430 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3431 (*i)->get_canvas_group()->i2w (ix1, iy1);
3432 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3433 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3435 from_playlist = (*i)->region()->playlist();
3436 to_playlist = atv2->playlist();
3438 latest_regionview = 0;
3440 where = (nframes_t) (unit_to_frame (ix1) * speed);
3441 boost::shared_ptr<Region> new_region (RegionFactory::create ((*i)->region()));
3443 from_playlist->remove_region (((*i)->region()));
3445 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3446 to_playlist->add_region (new_region, where);
3449 if (latest_regionview) {
3450 selection->add (latest_regionview);
3456 /* motion within a single track */
3458 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3462 if (rv->region()->locked()) {
3466 if (regionview_x_movement) {
3467 double ownspeed = 1.0;
3468 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3470 if (atv && atv->get_diskstream()) {
3471 ownspeed = atv->get_diskstream()->speed();
3474 /* base the new region position on the current position of the regionview.*/
3476 double ix1, ix2, iy1, iy2;
3478 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3479 rv->get_canvas_group()->i2w (ix1, iy1);
3480 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3484 where = rv->region()->position();
3487 rv->get_time_axis_view().reveal_dependent_views (*rv);
3489 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3491 rv->region()->set_position (where, (void *) this);
3492 rv->region()->set_opaque (true);
3497 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3499 session->add_command (new MementoCommand<Playlist>(*((*p).get()), 0, & (*p)->get_state()));
3502 motion_frozen_playlists.clear ();
3505 commit_reversible_command ();
3510 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3512 /* Either add to or set the set the region selection, unless
3513 this is an alignment click (control used)
3516 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3517 TimeAxisView* tv = &rv.get_time_axis_view();
3518 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3520 if (atv && atv->is_audio_track()) {
3521 speed = atv->get_diskstream()->speed();
3524 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3526 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3528 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3530 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3534 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3540 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3546 nframes_t frame_rate;
3553 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3554 case AudioClock::BBT:
3555 session->bbt_time (frame, bbt);
3556 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3559 case AudioClock::SMPTE:
3560 session->smpte_time (frame, smpte);
3561 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3564 case AudioClock::MinSec:
3565 /* XXX this is copied from show_verbose_duration_cursor() */
3566 frame_rate = session->frame_rate();
3567 hours = frame / (frame_rate * 3600);
3568 frame = frame % (frame_rate * 3600);
3569 mins = frame / (frame_rate * 60);
3570 frame = frame % (frame_rate * 60);
3571 secs = (float) frame / (float) frame_rate;
3572 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3576 snprintf (buf, sizeof(buf), "%u", frame);
3580 if (xpos >= 0 && ypos >=0) {
3581 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3584 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3586 show_verbose_canvas_cursor ();
3590 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3597 nframes_t distance, frame_rate;
3599 Meter meter_at_start(session->tempo_map().meter_at(start));
3605 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3606 case AudioClock::BBT:
3607 session->bbt_time (start, sbbt);
3608 session->bbt_time (end, ebbt);
3611 /* XXX this computation won't work well if the
3612 user makes a selection that spans any meter changes.
3615 ebbt.bars -= sbbt.bars;
3616 if (ebbt.beats >= sbbt.beats) {
3617 ebbt.beats -= sbbt.beats;
3620 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3622 if (ebbt.ticks >= sbbt.ticks) {
3623 ebbt.ticks -= sbbt.ticks;
3626 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3629 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3632 case AudioClock::SMPTE:
3633 session->smpte_duration (end - start, smpte);
3634 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3637 case AudioClock::MinSec:
3638 /* XXX this stuff should be elsewhere.. */
3639 distance = end - start;
3640 frame_rate = session->frame_rate();
3641 hours = distance / (frame_rate * 3600);
3642 distance = distance % (frame_rate * 3600);
3643 mins = distance / (frame_rate * 60);
3644 distance = distance % (frame_rate * 60);
3645 secs = (float) distance / (float) frame_rate;
3646 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3650 snprintf (buf, sizeof(buf), "%u", end - start);
3654 if (xpos >= 0 && ypos >=0) {
3655 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3658 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3660 show_verbose_canvas_cursor ();
3664 Editor::collect_new_region_view (RegionView* rv)
3666 latest_regionview = rv;
3670 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3672 if (clicked_regionview == 0) {
3676 /* lets try to create new Region for the selection */
3678 vector<boost::shared_ptr<AudioRegion> > new_regions;
3679 create_region_from_selection (new_regions);
3681 if (new_regions.empty()) {
3685 /* XXX fix me one day to use all new regions */
3687 boost::shared_ptr<Region> region (new_regions.front());
3689 /* add it to the current stream/playlist.
3691 tricky: the streamview for the track will add a new regionview. we will
3692 catch the signal it sends when it creates the regionview to
3693 set the regionview we want to then drag.
3696 latest_regionview = 0;
3697 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3699 /* A selection grab currently creates two undo/redo operations, one for
3700 creating the new region and another for moving it.
3703 begin_reversible_command (_("selection grab"));
3705 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3707 XMLNode *before = &(playlist->get_state());
3708 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3709 XMLNode *after = &(playlist->get_state());
3710 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3712 commit_reversible_command ();
3716 if (latest_regionview == 0) {
3717 /* something went wrong */
3721 /* we need to deselect all other regionviews, and select this one
3722 i'm ignoring undo stuff, because the region creation will take care of it */
3723 selection->set (latest_regionview);
3725 drag_info.item = latest_regionview->get_canvas_group();
3726 drag_info.data = latest_regionview;
3727 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3728 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3732 drag_info.last_trackview = clicked_trackview;
3733 drag_info.last_frame_position = latest_regionview->region()->position();
3734 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3736 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3740 Editor::cancel_selection ()
3742 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3743 (*i)->hide_selection ();
3745 begin_reversible_command (_("cancel selection"));
3746 selection->clear ();
3747 clicked_selection = 0;
3748 commit_reversible_command ();
3752 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3754 nframes_t start = 0;
3761 drag_info.item = item;
3762 drag_info.motion_callback = &Editor::drag_selection;
3763 drag_info.finished_callback = &Editor::end_selection_op;
3768 case CreateSelection:
3769 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3770 drag_info.copy = true;
3772 drag_info.copy = false;
3774 start_grab (event, selector_cursor);
3777 case SelectionStartTrim:
3778 if (clicked_trackview) {
3779 clicked_trackview->order_selection_trims (item, true);
3781 start_grab (event, trimmer_cursor);
3782 start = selection->time[clicked_selection].start;
3783 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3786 case SelectionEndTrim:
3787 if (clicked_trackview) {
3788 clicked_trackview->order_selection_trims (item, false);
3790 start_grab (event, trimmer_cursor);
3791 end = selection->time[clicked_selection].end;
3792 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3796 start = selection->time[clicked_selection].start;
3798 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3802 if (selection_op == SelectionMove) {
3803 show_verbose_time_cursor(start, 10);
3805 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3810 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3812 nframes_t start = 0;
3815 nframes_t pending_position;
3817 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3818 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3821 pending_position = 0;
3824 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3825 snap_to (pending_position);
3828 /* only alter selection if the current frame is
3829 different from the last frame position (adjusted)
3832 if (pending_position == drag_info.last_pointer_frame) return;
3834 switch (selection_op) {
3835 case CreateSelection:
3837 if (drag_info.first_move) {
3838 snap_to (drag_info.grab_frame);
3841 if (pending_position < drag_info.grab_frame) {
3842 start = pending_position;
3843 end = drag_info.grab_frame;
3845 end = pending_position;
3846 start = drag_info.grab_frame;
3849 /* first drag: Either add to the selection
3850 or create a new selection->
3853 if (drag_info.first_move) {
3855 begin_reversible_command (_("range selection"));
3857 if (drag_info.copy) {
3858 /* adding to the selection */
3859 clicked_selection = selection->add (start, end);
3860 drag_info.copy = false;
3862 /* new selection-> */
3863 clicked_selection = selection->set (clicked_trackview, start, end);
3868 case SelectionStartTrim:
3870 if (drag_info.first_move) {
3871 begin_reversible_command (_("trim selection start"));
3874 start = selection->time[clicked_selection].start;
3875 end = selection->time[clicked_selection].end;
3877 if (pending_position > end) {
3880 start = pending_position;
3884 case SelectionEndTrim:
3886 if (drag_info.first_move) {
3887 begin_reversible_command (_("trim selection end"));
3890 start = selection->time[clicked_selection].start;
3891 end = selection->time[clicked_selection].end;
3893 if (pending_position < start) {
3896 end = pending_position;
3903 if (drag_info.first_move) {
3904 begin_reversible_command (_("move selection"));
3907 start = selection->time[clicked_selection].start;
3908 end = selection->time[clicked_selection].end;
3910 length = end - start;
3912 start = pending_position;
3915 end = start + length;
3920 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3921 start_canvas_autoscroll (1);
3925 selection->replace (clicked_selection, start, end);
3928 drag_info.last_pointer_frame = pending_position;
3929 drag_info.first_move = false;
3931 if (selection_op == SelectionMove) {
3932 show_verbose_time_cursor(start, 10);
3934 show_verbose_time_cursor(pending_position, 10);
3939 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3941 if (!drag_info.first_move) {
3942 drag_selection (item, event);
3943 /* XXX this is not object-oriented programming at all. ick */
3944 if (selection->time.consolidate()) {
3945 selection->TimeChanged ();
3947 commit_reversible_command ();
3949 /* just a click, no pointer movement.*/
3951 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3953 selection->clear_time();
3958 /* XXX what happens if its a music selection? */
3959 session->set_audio_range (selection->time);
3960 stop_canvas_autoscroll ();
3964 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3967 TimeAxisView* tvp = clicked_trackview;
3968 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3970 if (tv && tv->is_audio_track()) {
3971 speed = tv->get_diskstream()->speed();
3974 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3975 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3976 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3978 motion_frozen_playlists.clear();
3980 //drag_info.item = clicked_regionview->get_name_highlight();
3981 drag_info.item = item;
3982 drag_info.motion_callback = &Editor::trim_motion_callback;
3983 drag_info.finished_callback = &Editor::trim_finished_callback;
3985 start_grab (event, trimmer_cursor);
3987 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3988 trim_op = ContentsTrim;
3990 /* These will get overridden for a point trim.*/
3991 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3992 /* closer to start */
3993 trim_op = StartTrim;
3994 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4002 show_verbose_time_cursor(region_start, 10);
4005 show_verbose_time_cursor(region_end, 10);
4008 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4014 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4016 RegionView* rv = clicked_regionview;
4017 nframes_t frame_delta = 0;
4018 bool left_direction;
4019 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4021 /* snap modifier works differently here..
4022 its' current state has to be passed to the
4023 various trim functions in order to work properly
4027 TimeAxisView* tvp = clicked_trackview;
4028 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4029 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4031 if (tv && tv->is_audio_track()) {
4032 speed = tv->get_diskstream()->speed();
4035 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4036 left_direction = true;
4038 left_direction = false;
4042 snap_to (drag_info.current_pointer_frame);
4045 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4049 if (drag_info.first_move) {
4055 trim_type = "Region start trim";
4058 trim_type = "Region end trim";
4061 trim_type = "Region content trim";
4065 begin_reversible_command (trim_type);
4067 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4068 (*i)->region()->set_opaque(false);
4069 (*i)->region()->freeze ();
4071 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4073 arv->temporarily_hide_envelope ();
4075 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4076 insert_result = motion_frozen_playlists.insert (pl);
4077 if (insert_result.second) {
4078 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4083 if (left_direction) {
4084 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4086 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4091 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4094 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4095 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4101 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4104 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4105 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4112 bool swap_direction = false;
4114 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4115 swap_direction = true;
4118 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4119 i != selection->regions.by_layer().end(); ++i)
4121 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4129 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4132 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4135 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4139 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4140 drag_info.first_move = false;
4144 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4146 boost::shared_ptr<Region> region (rv.region());
4148 if (region->locked()) {
4152 nframes_t new_bound;
4155 TimeAxisView* tvp = clicked_trackview;
4156 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4158 if (tv && tv->is_audio_track()) {
4159 speed = tv->get_diskstream()->speed();
4162 if (left_direction) {
4163 if (swap_direction) {
4164 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4166 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4169 if (swap_direction) {
4170 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4172 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4177 snap_to (new_bound);
4179 region->trim_start ((nframes_t) (new_bound * speed), this);
4180 rv.region_changed (StartChanged);
4184 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4186 boost::shared_ptr<Region> region (rv.region());
4188 if (region->locked()) {
4192 nframes_t new_bound;
4195 TimeAxisView* tvp = clicked_trackview;
4196 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4198 if (tv && tv->is_audio_track()) {
4199 speed = tv->get_diskstream()->speed();
4202 if (left_direction) {
4203 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4205 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4209 snap_to (new_bound, (left_direction ? 0 : 1));
4212 region->trim_front ((nframes_t) (new_bound * speed), this);
4214 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4218 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4220 boost::shared_ptr<Region> region (rv.region());
4222 if (region->locked()) {
4226 nframes_t new_bound;
4229 TimeAxisView* tvp = clicked_trackview;
4230 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4232 if (tv && tv->is_audio_track()) {
4233 speed = tv->get_diskstream()->speed();
4236 if (left_direction) {
4237 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4239 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4243 snap_to (new_bound);
4245 region->trim_end ((nframes_t) (new_bound * speed), this);
4246 rv.region_changed (LengthChanged);
4250 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4252 if (!drag_info.first_move) {
4253 trim_motion_callback (item, event);
4255 if (!clicked_regionview->get_selected()) {
4256 thaw_region_after_trim (*clicked_regionview);
4259 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4260 i != selection->regions.by_layer().end(); ++i)
4262 thaw_region_after_trim (**i);
4263 (*i)->region()->set_opaque(true);
4267 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4269 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4272 motion_frozen_playlists.clear ();
4274 commit_reversible_command();
4276 /* no mouse movement */
4282 Editor::point_trim (GdkEvent* event)
4284 RegionView* rv = clicked_regionview;
4285 nframes_t new_bound = drag_info.current_pointer_frame;
4287 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4288 snap_to (new_bound);
4291 /* Choose action dependant on which button was pressed */
4292 switch (event->button.button) {
4294 trim_op = StartTrim;
4295 begin_reversible_command (_("Start point trim"));
4297 if (rv->get_selected()) {
4299 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4300 i != selection->regions.by_layer().end(); ++i)
4302 if (!(*i)->region()->locked()) {
4303 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4304 XMLNode &before = pl->get_state();
4305 (*i)->region()->trim_front (new_bound, this);
4306 XMLNode &after = pl->get_state();
4307 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4313 if (!rv->region()->locked()) {
4314 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4315 XMLNode &before = pl->get_state();
4316 rv->region()->trim_front (new_bound, this);
4317 XMLNode &after = pl->get_state();
4318 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4322 commit_reversible_command();
4327 begin_reversible_command (_("End point trim"));
4329 if (rv->get_selected()) {
4331 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4333 if (!(*i)->region()->locked()) {
4334 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4335 XMLNode &before = pl->get_state();
4336 (*i)->region()->trim_end (new_bound, this);
4337 XMLNode &after = pl->get_state();
4338 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4344 if (!rv->region()->locked()) {
4345 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4346 XMLNode &before = pl->get_state();
4347 rv->region()->trim_end (new_bound, this);
4348 XMLNode &after = pl->get_state();
4349 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4353 commit_reversible_command();
4362 Editor::thaw_region_after_trim (RegionView& rv)
4364 boost::shared_ptr<Region> region (rv.region());
4366 if (region->locked()) {
4370 region->thaw (_("trimmed region"));
4371 XMLNode &after = region->playlist()->get_state();
4372 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4374 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4376 arv->unhide_envelope ();
4380 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4385 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4386 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4390 Location* location = find_location_from_marker (marker, is_start);
4391 location->set_hidden (true, this);
4396 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4402 drag_info.item = item;
4403 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4404 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4406 range_marker_op = op;
4408 if (!temp_location) {
4409 temp_location = new Location;
4413 case CreateRangeMarker:
4414 case CreateTransportMarker:
4416 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4417 drag_info.copy = true;
4419 drag_info.copy = false;
4421 start_grab (event, selector_cursor);
4425 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4430 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4432 nframes_t start = 0;
4434 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4436 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4437 snap_to (drag_info.current_pointer_frame);
4440 /* only alter selection if the current frame is
4441 different from the last frame position.
4444 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4446 switch (range_marker_op) {
4447 case CreateRangeMarker:
4448 case CreateTransportMarker:
4449 if (drag_info.first_move) {
4450 snap_to (drag_info.grab_frame);
4453 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4454 start = drag_info.current_pointer_frame;
4455 end = drag_info.grab_frame;
4457 end = drag_info.current_pointer_frame;
4458 start = drag_info.grab_frame;
4461 /* first drag: Either add to the selection
4462 or create a new selection.
4465 if (drag_info.first_move) {
4467 temp_location->set (start, end);
4471 update_marker_drag_item (temp_location);
4472 range_marker_drag_rect->show();
4473 range_marker_drag_rect->raise_to_top();
4479 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4480 start_canvas_autoscroll (1);
4484 temp_location->set (start, end);
4486 double x1 = frame_to_pixel (start);
4487 double x2 = frame_to_pixel (end);
4488 crect->property_x1() = x1;
4489 crect->property_x2() = x2;
4491 update_marker_drag_item (temp_location);
4494 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4495 drag_info.first_move = false;
4497 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4502 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4504 Location * newloc = 0;
4507 if (!drag_info.first_move) {
4508 drag_range_markerbar_op (item, event);
4510 switch (range_marker_op) {
4511 case CreateRangeMarker:
4513 begin_reversible_command (_("new range marker"));
4514 XMLNode &before = session->locations()->get_state();
4515 session->locations()->next_available_name(rangename,"unnamed");
4516 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4517 session->locations()->add (newloc, true);
4518 XMLNode &after = session->locations()->get_state();
4519 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4520 commit_reversible_command ();
4522 range_bar_drag_rect->hide();
4523 range_marker_drag_rect->hide();
4527 case CreateTransportMarker:
4528 // popup menu to pick loop or punch
4529 new_transport_marker_context_menu (&event->button, item);
4534 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4536 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4541 start = session->locations()->first_mark_before (drag_info.grab_frame);
4542 end = session->locations()->first_mark_after (drag_info.grab_frame);
4544 if (end == max_frames) {
4545 end = session->current_end_frame ();
4549 start = session->current_start_frame ();
4552 switch (mouse_mode) {
4554 /* find the two markers on either side and then make the selection from it */
4555 cerr << "select between " << start << " .. " << end << endl;
4556 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4560 /* find the two markers on either side of the click and make the range out of it */
4561 selection->set (0, start, end);
4570 stop_canvas_autoscroll ();
4576 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4578 drag_info.item = item;
4579 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4580 drag_info.finished_callback = &Editor::end_mouse_zoom;
4582 start_grab (event, zoom_cursor);
4584 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4588 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4593 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4594 snap_to (drag_info.current_pointer_frame);
4596 if (drag_info.first_move) {
4597 snap_to (drag_info.grab_frame);
4601 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4603 /* base start and end on initial click position */
4604 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4605 start = drag_info.current_pointer_frame;
4606 end = drag_info.grab_frame;
4608 end = drag_info.current_pointer_frame;
4609 start = drag_info.grab_frame;
4614 if (drag_info.first_move) {
4616 zoom_rect->raise_to_top();
4619 reposition_zoom_rect(start, end);
4621 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4622 drag_info.first_move = false;
4624 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4629 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4631 if (!drag_info.first_move) {
4632 drag_mouse_zoom (item, event);
4634 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4635 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4637 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4640 temporal_zoom_to_frame (false, drag_info.grab_frame);
4642 temporal_zoom_step (false);
4643 center_screen (drag_info.grab_frame);
4651 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4653 double x1 = frame_to_pixel (start);
4654 double x2 = frame_to_pixel (end);
4655 double y2 = full_canvas_height - 1.0;
4657 zoom_rect->property_x1() = x1;
4658 zoom_rect->property_y1() = 1.0;
4659 zoom_rect->property_x2() = x2;
4660 zoom_rect->property_y2() = y2;
4664 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4666 drag_info.item = item;
4667 drag_info.motion_callback = &Editor::drag_rubberband_select;
4668 drag_info.finished_callback = &Editor::end_rubberband_select;
4670 start_grab (event, cross_hair_cursor);
4672 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4676 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4683 /* use a bigger drag threshold than the default */
4685 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4689 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4690 if (drag_info.first_move) {
4691 snap_to (drag_info.grab_frame);
4693 snap_to (drag_info.current_pointer_frame);
4696 /* base start and end on initial click position */
4698 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4699 start = drag_info.current_pointer_frame;
4700 end = drag_info.grab_frame;
4702 end = drag_info.current_pointer_frame;
4703 start = drag_info.grab_frame;
4706 if (drag_info.current_pointer_y < drag_info.grab_y) {
4707 y1 = drag_info.current_pointer_y;
4708 y2 = drag_info.grab_y;
4710 y2 = drag_info.current_pointer_y;
4711 y1 = drag_info.grab_y;
4715 if (start != end || y1 != y2) {
4717 double x1 = frame_to_pixel (start);
4718 double x2 = frame_to_pixel (end);
4720 rubberband_rect->property_x1() = x1;
4721 rubberband_rect->property_y1() = y1;
4722 rubberband_rect->property_x2() = x2;
4723 rubberband_rect->property_y2() = y2;
4725 rubberband_rect->show();
4726 rubberband_rect->raise_to_top();
4728 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4729 drag_info.first_move = false;
4731 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4736 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4738 if (!drag_info.first_move) {
4740 drag_rubberband_select (item, event);
4743 if (drag_info.current_pointer_y < drag_info.grab_y) {
4744 y1 = drag_info.current_pointer_y;
4745 y2 = drag_info.grab_y;
4748 y2 = drag_info.current_pointer_y;
4749 y1 = drag_info.grab_y;
4753 Selection::Operation op = Keyboard::selection_type (event->button.state);
4756 begin_reversible_command (_("rubberband selection"));
4758 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4759 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4761 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4765 commit_reversible_command ();
4769 selection->clear_tracks();
4770 selection->clear_regions();
4771 selection->clear_points ();
4772 selection->clear_lines ();
4775 rubberband_rect->hide();
4780 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4782 using namespace Gtkmm2ext;
4784 ArdourPrompter prompter (false);
4786 prompter.set_prompt (_("Name for region:"));
4787 prompter.set_initial_text (clicked_regionview->region()->name());
4788 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4789 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4790 prompter.show_all ();
4791 switch (prompter.run ()) {
4792 case Gtk::RESPONSE_ACCEPT:
4794 prompter.get_result(str);
4796 clicked_regionview->region()->set_name (str);
4804 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4806 drag_info.item = item;
4807 drag_info.motion_callback = &Editor::time_fx_motion;
4808 drag_info.finished_callback = &Editor::end_time_fx;
4812 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4816 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4818 RegionView* rv = clicked_regionview;
4820 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4821 snap_to (drag_info.current_pointer_frame);
4824 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4828 if (drag_info.current_pointer_frame > rv->region()->position()) {
4829 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4832 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4833 drag_info.first_move = false;
4835 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4839 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4841 clicked_regionview->get_time_axis_view().hide_timestretch ();
4843 if (drag_info.first_move) {
4847 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4848 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4850 begin_reversible_command (_("timestretch"));
4852 if (run_timestretch (selection->regions, percentage) == 0) {
4853 session->commit_reversible_command ();
4858 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4860 /* no brushing without a useful snap setting */
4863 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4866 switch (snap_mode) {
4868 return; /* can't work because it allows region to be placed anywhere */
4873 switch (snap_type) {
4876 case SnapToEditCursor:
4883 /* don't brush a copy over the original */
4885 if (pos == rv->region()->position()) {
4889 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4891 if (atv == 0 || !atv->is_audio_track()) {
4895 boost::shared_ptr<Playlist> playlist = atv->playlist();
4896 double speed = atv->get_diskstream()->speed();
4898 XMLNode &before = playlist->get_state();
4899 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4900 XMLNode &after = playlist->get_state();
4901 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4903 // playlist is frozen, so we have to update manually
4905 playlist->Modified(); /* EMIT SIGNAL */
4909 Editor::track_height_step_timeout ()
4912 struct timeval delta;
4914 gettimeofday (&now, 0);
4915 timersub (&now, &last_track_height_step_timestamp, &delta);
4917 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4918 current_stepping_trackview = 0;