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.
28 #include <pbd/error.h>
29 #include <gtkmm2ext/utils.h>
30 #include <pbd/memento_command.h>
32 #include "ardour_ui.h"
34 #include "time_axis_view.h"
35 #include "audio_time_axis.h"
36 #include "audio_region_view.h"
38 #include "streamview.h"
39 #include "region_gain_line.h"
40 #include "automation_time_axis.h"
43 #include "selection.h"
46 #include "rgb_macros.h"
48 #include <ardour/types.h>
49 #include <ardour/profile.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 /* almost no selection action on modified button-2 or button-3 events */
318 if (item_type != RegionItem && event->button.button != 2) {
324 Selection::Operation op = Keyboard::selection_type (event->button.state);
325 bool press = (event->type == GDK_BUTTON_PRESS);
327 // begin_reversible_command (_("select on click"));
331 if (mouse_mode != MouseRange) {
332 commit = set_selected_regionview_from_click (press, op, true);
333 } else if (event->type == GDK_BUTTON_PRESS) {
334 commit = set_selected_track_from_click (press, op, false);
338 case RegionViewNameHighlight:
340 if (mouse_mode != MouseRange) {
341 commit = set_selected_regionview_from_click (press, op, true);
342 } else if (event->type == GDK_BUTTON_PRESS) {
343 commit = set_selected_track_from_click (press, op, false);
347 case FadeInHandleItem:
349 case FadeOutHandleItem:
351 if (mouse_mode != MouseRange) {
352 commit = set_selected_regionview_from_click (press, op, true);
353 } else if (event->type == GDK_BUTTON_PRESS) {
354 commit = set_selected_track_from_click (press, op, false);
358 case GainAutomationControlPointItem:
359 case PanAutomationControlPointItem:
360 case RedirectAutomationControlPointItem:
361 commit = set_selected_track_from_click (press, op, true);
362 if (mouse_mode != MouseRange) {
363 commit |= set_selected_control_point_from_click (op, false);
368 /* for context click or range selection, select track */
369 if (event->button.button == 3) {
370 commit = set_selected_track_from_click (press, op, true);
371 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
372 commit = set_selected_track_from_click (press, op, false);
376 case AutomationTrackItem:
377 commit = set_selected_track_from_click (press, op, true);
385 // commit_reversible_command ();
390 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
392 track_canvas.grab_focus();
394 if (session && session->actively_recording()) {
398 button_selection (item, event, item_type);
400 if (drag_info.item == 0 &&
401 (Keyboard::is_delete_event (&event->button) ||
402 Keyboard::is_context_menu_event (&event->button) ||
403 Keyboard::is_edit_event (&event->button))) {
405 /* handled by button release */
409 switch (event->button.button) {
412 if (event->type == GDK_BUTTON_PRESS) {
414 if (drag_info.item) {
415 drag_info.item->ungrab (event->button.time);
418 /* single mouse clicks on any of these item types operate
419 independent of mouse mode, mostly because they are
420 not on the main track canvas or because we want
426 case PlayheadCursorItem:
427 start_cursor_grab (item, event);
431 if (Keyboard::modifier_state_equals (event->button.state,
432 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
433 hide_marker (item, event);
435 start_marker_grab (item, event);
439 case TempoMarkerItem:
440 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
441 start_tempo_marker_copy_grab (item, event);
443 start_tempo_marker_grab (item, event);
447 case MeterMarkerItem:
448 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
449 start_meter_marker_copy_grab (item, event);
451 start_meter_marker_grab (item, event);
461 case RangeMarkerBarItem:
462 start_range_markerbar_op (item, event, CreateRangeMarker);
466 case TransportMarkerBarItem:
467 start_range_markerbar_op (item, event, CreateTransportMarker);
476 switch (mouse_mode) {
479 case StartSelectionTrimItem:
480 start_selection_op (item, event, SelectionStartTrim);
483 case EndSelectionTrimItem:
484 start_selection_op (item, event, SelectionEndTrim);
488 if (Keyboard::modifier_state_contains
489 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
490 // contains and not equals because I can't use alt as a modifier alone.
491 start_selection_grab (item, event);
492 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
493 /* grab selection for moving */
494 start_selection_op (item, event, SelectionMove);
497 /* this was debated, but decided the more common action was to
498 make a new selection */
499 start_selection_op (item, event, CreateSelection);
504 start_selection_op (item, event, CreateSelection);
510 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
511 event->type == GDK_BUTTON_PRESS) {
513 start_rubberband_select (item, event);
515 } else if (event->type == GDK_BUTTON_PRESS) {
518 case FadeInHandleItem:
519 start_fade_in_grab (item, event);
522 case FadeOutHandleItem:
523 start_fade_out_grab (item, event);
527 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
528 start_region_copy_grab (item, event);
529 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
530 start_region_brush_grab (item, event);
532 start_region_grab (item, event);
536 case RegionViewNameHighlight:
537 start_trim (item, event);
542 /* rename happens on edit clicks */
543 start_trim (clicked_regionview->get_name_highlight(), event);
547 case GainAutomationControlPointItem:
548 case PanAutomationControlPointItem:
549 case RedirectAutomationControlPointItem:
550 start_control_point_grab (item, event);
554 case GainAutomationLineItem:
555 case PanAutomationLineItem:
556 case RedirectAutomationLineItem:
557 start_line_grab_from_line (item, event);
562 case AutomationTrackItem:
563 start_rubberband_select (item, event);
566 /* <CMT Additions> */
567 case ImageFrameHandleStartItem:
568 imageframe_start_handle_op(item, event) ;
571 case ImageFrameHandleEndItem:
572 imageframe_end_handle_op(item, event) ;
575 case MarkerViewHandleStartItem:
576 markerview_item_start_handle_op(item, event) ;
579 case MarkerViewHandleEndItem:
580 markerview_item_end_handle_op(item, event) ;
583 /* </CMT Additions> */
585 /* <CMT Additions> */
587 start_markerview_grab(item, event) ;
590 start_imageframe_grab(item, event) ;
592 /* </CMT Additions> */
608 // start_line_grab_from_regionview (item, event);
611 case GainControlPointItem:
612 start_control_point_grab (item, event);
616 start_line_grab_from_line (item, event);
619 case GainAutomationControlPointItem:
620 case PanAutomationControlPointItem:
621 case RedirectAutomationControlPointItem:
622 start_control_point_grab (item, event);
633 case GainAutomationControlPointItem:
634 case PanAutomationControlPointItem:
635 case RedirectAutomationControlPointItem:
636 start_control_point_grab (item, event);
639 case GainAutomationLineItem:
640 case PanAutomationLineItem:
641 case RedirectAutomationLineItem:
642 start_line_grab_from_line (item, event);
646 // XXX need automation mode to identify which
648 // start_line_grab_from_regionview (item, event);
658 if (event->type == GDK_BUTTON_PRESS) {
659 start_mouse_zoom (item, event);
666 if (item_type == RegionItem) {
667 start_time_fx (item, event);
672 /* handled in release */
681 switch (mouse_mode) {
683 if (event->type == GDK_BUTTON_PRESS) {
686 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
687 start_region_copy_grab (item, event);
689 start_region_grab (item, event);
693 case GainAutomationControlPointItem:
694 case PanAutomationControlPointItem:
695 case RedirectAutomationControlPointItem:
696 start_control_point_grab (item, event);
707 case RegionViewNameHighlight:
708 start_trim (item, event);
713 start_trim (clicked_regionview->get_name_highlight(), event);
724 if (event->type == GDK_BUTTON_PRESS) {
725 /* relax till release */
732 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
733 temporal_zoom_session();
735 temporal_zoom_to_frame (true, event_frame(event));
758 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
760 nframes_t where = event_frame (event, 0, 0);
762 /* no action if we're recording */
764 if (session && session->actively_recording()) {
768 /* first, see if we're finishing a drag ... */
770 if (drag_info.item) {
771 if (end_grab (item, event)) {
772 /* grab dragged, so do nothing else */
777 button_selection (item, event, item_type);
779 /* edit events get handled here */
781 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
787 case TempoMarkerItem:
788 edit_tempo_marker (item);
791 case MeterMarkerItem:
792 edit_meter_marker (item);
796 if (clicked_regionview->name_active()) {
797 return mouse_rename_region (item, event);
807 /* context menu events get handled here */
809 if (Keyboard::is_context_menu_event (&event->button)) {
811 if (drag_info.item == 0) {
813 /* no matter which button pops up the context menu, tell the menu
814 widget to use button 1 to drive menu selection.
819 case FadeInHandleItem:
821 case FadeOutHandleItem:
822 popup_fade_context_menu (1, event->button.time, item, item_type);
826 popup_track_context_menu (1, event->button.time, item_type, false, where);
830 case RegionViewNameHighlight:
832 popup_track_context_menu (1, event->button.time, item_type, false, where);
836 popup_track_context_menu (1, event->button.time, item_type, true, where);
839 case AutomationTrackItem:
840 popup_track_context_menu (1, event->button.time, item_type, false, where);
844 case RangeMarkerBarItem:
845 case TransportMarkerBarItem:
848 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
852 marker_context_menu (&event->button, item);
855 case TempoMarkerItem:
856 tm_marker_context_menu (&event->button, item);
859 case MeterMarkerItem:
860 tm_marker_context_menu (&event->button, item);
863 case CrossfadeViewItem:
864 popup_track_context_menu (1, event->button.time, item_type, false, where);
867 /* <CMT Additions> */
869 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
871 case ImageFrameTimeAxisItem:
872 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
875 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
877 case MarkerTimeAxisItem:
878 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
880 /* <CMT Additions> */
891 /* delete events get handled here */
893 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
896 case TempoMarkerItem:
897 remove_tempo_marker (item);
900 case MeterMarkerItem:
901 remove_meter_marker (item);
905 remove_marker (*item, event);
909 if (mouse_mode == MouseObject) {
910 remove_clicked_region ();
914 case GainControlPointItem:
915 if (mouse_mode == MouseGain) {
916 remove_gain_control_point (item, event);
920 case GainAutomationControlPointItem:
921 case PanAutomationControlPointItem:
922 case RedirectAutomationControlPointItem:
923 remove_control_point (item, event);
932 switch (event->button.button) {
936 /* see comments in button_press_handler */
938 case PlayheadCursorItem:
941 case GainAutomationLineItem:
942 case PanAutomationLineItem:
943 case RedirectAutomationLineItem:
944 case StartSelectionTrimItem:
945 case EndSelectionTrimItem:
949 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
950 snap_to (where, 0, true);
952 mouse_add_new_marker (where);
956 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
959 mouse_add_new_tempo_event (where);
963 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
971 switch (mouse_mode) {
974 case AutomationTrackItem:
975 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
989 // Gain only makes sense for audio regions
991 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
997 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1001 case AutomationTrackItem:
1002 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1003 add_automation_event (item, event, where, event->button.y);
1012 switch (item_type) {
1014 audition_selected_region ();
1031 switch (mouse_mode) {
1034 switch (item_type) {
1036 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1038 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1041 // Button2 click is unused
1054 // x_style_paste (where, 1.0);
1074 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1080 switch (item_type) {
1081 case GainControlPointItem:
1082 if (mouse_mode == MouseGain) {
1083 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1084 cp->set_visible (true);
1088 at_y = cp->get_y ();
1089 cp->item->i2w (at_x, at_y);
1093 fraction = 1.0 - (cp->get_y() / cp->line.height());
1095 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1096 show_verbose_canvas_cursor ();
1098 if (is_drawable()) {
1099 track_canvas.get_window()->set_cursor (*fader_cursor);
1104 case GainAutomationControlPointItem:
1105 case PanAutomationControlPointItem:
1106 case RedirectAutomationControlPointItem:
1107 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1108 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1109 cp->set_visible (true);
1113 at_y = cp->get_y ();
1114 cp->item->i2w (at_x, at_y);
1118 fraction = 1.0 - (cp->get_y() / cp->line.height());
1120 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1121 show_verbose_canvas_cursor ();
1123 if (is_drawable()) {
1124 track_canvas.get_window()->set_cursor (*fader_cursor);
1130 if (mouse_mode == MouseGain) {
1131 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1133 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1134 if (is_drawable()) {
1135 track_canvas.get_window()->set_cursor (*fader_cursor);
1140 case GainAutomationLineItem:
1141 case RedirectAutomationLineItem:
1142 case PanAutomationLineItem:
1143 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1145 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1147 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1149 if (is_drawable()) {
1150 track_canvas.get_window()->set_cursor (*fader_cursor);
1155 case RegionViewNameHighlight:
1156 if (is_drawable() && mouse_mode == MouseObject) {
1157 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1161 case StartSelectionTrimItem:
1162 case EndSelectionTrimItem:
1163 /* <CMT Additions> */
1164 case ImageFrameHandleStartItem:
1165 case ImageFrameHandleEndItem:
1166 case MarkerViewHandleStartItem:
1167 case MarkerViewHandleEndItem:
1168 /* </CMT Additions> */
1170 if (is_drawable()) {
1171 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1175 case EditCursorItem:
1176 case PlayheadCursorItem:
1177 if (is_drawable()) {
1178 track_canvas.get_window()->set_cursor (*grabber_cursor);
1182 case RegionViewName:
1184 /* when the name is not an active item, the entire name highlight is for trimming */
1186 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1187 if (mouse_mode == MouseObject && is_drawable()) {
1188 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1194 case AutomationTrackItem:
1195 if (is_drawable()) {
1196 Gdk::Cursor *cursor;
1197 switch (mouse_mode) {
1199 cursor = selector_cursor;
1202 cursor = zoom_cursor;
1205 cursor = cross_hair_cursor;
1209 track_canvas.get_window()->set_cursor (*cursor);
1211 AutomationTimeAxisView* atv;
1212 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1213 clear_entered_track = false;
1214 set_entered_track (atv);
1220 case RangeMarkerBarItem:
1221 case TransportMarkerBarItem:
1224 if (is_drawable()) {
1225 time_canvas.get_window()->set_cursor (*timebar_cursor);
1230 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1233 marker->set_color_rgba (color_map[cEnteredMarker]);
1235 case MeterMarkerItem:
1236 case TempoMarkerItem:
1237 if (is_drawable()) {
1238 time_canvas.get_window()->set_cursor (*timebar_cursor);
1241 case FadeInHandleItem:
1242 case FadeOutHandleItem:
1243 if (mouse_mode == MouseObject) {
1244 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1246 rect->property_fill_color_rgba() = 0;
1247 rect->property_outline_pixels() = 1;
1256 /* second pass to handle entered track status in a comprehensible way.
1259 switch (item_type) {
1261 case GainAutomationLineItem:
1262 case RedirectAutomationLineItem:
1263 case PanAutomationLineItem:
1264 case GainControlPointItem:
1265 case GainAutomationControlPointItem:
1266 case PanAutomationControlPointItem:
1267 case RedirectAutomationControlPointItem:
1268 /* these do not affect the current entered track state */
1269 clear_entered_track = false;
1272 case AutomationTrackItem:
1273 /* handled above already */
1277 set_entered_track (0);
1285 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1294 switch (item_type) {
1295 case GainControlPointItem:
1296 case GainAutomationControlPointItem:
1297 case PanAutomationControlPointItem:
1298 case RedirectAutomationControlPointItem:
1299 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1300 if (cp->line.npoints() > 1) {
1301 if (!cp->selected) {
1302 cp->set_visible (false);
1306 if (is_drawable()) {
1307 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1310 hide_verbose_canvas_cursor ();
1313 case RegionViewNameHighlight:
1314 case StartSelectionTrimItem:
1315 case EndSelectionTrimItem:
1316 case EditCursorItem:
1317 case PlayheadCursorItem:
1318 /* <CMT Additions> */
1319 case ImageFrameHandleStartItem:
1320 case ImageFrameHandleEndItem:
1321 case MarkerViewHandleStartItem:
1322 case MarkerViewHandleEndItem:
1323 /* </CMT Additions> */
1324 if (is_drawable()) {
1325 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1330 case GainAutomationLineItem:
1331 case RedirectAutomationLineItem:
1332 case PanAutomationLineItem:
1333 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1335 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1337 line->property_fill_color_rgba() = al->get_line_color();
1339 if (is_drawable()) {
1340 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1344 case RegionViewName:
1345 /* see enter_handler() for notes */
1346 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1347 if (is_drawable() && mouse_mode == MouseObject) {
1348 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1353 case RangeMarkerBarItem:
1354 case TransportMarkerBarItem:
1358 if (is_drawable()) {
1359 time_canvas.get_window()->set_cursor (*timebar_cursor);
1364 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1367 loc = find_location_from_marker (marker, is_start);
1368 if (loc) location_flags_changed (loc, this);
1370 case MeterMarkerItem:
1371 case TempoMarkerItem:
1373 if (is_drawable()) {
1374 time_canvas.get_window()->set_cursor (*timebar_cursor);
1379 case FadeInHandleItem:
1380 case FadeOutHandleItem:
1381 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1383 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1385 rect->property_fill_color_rgba() = rv->get_fill_color();
1386 rect->property_outline_pixels() = 0;
1391 case AutomationTrackItem:
1392 if (is_drawable()) {
1393 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1394 clear_entered_track = true;
1395 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1407 Editor::left_automation_track ()
1409 if (clear_entered_track) {
1410 set_entered_track (0);
1411 clear_entered_track = false;
1417 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1421 /* We call this so that MOTION_NOTIFY events continue to be
1422 delivered to the canvas. We need to do this because we set
1423 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1424 the density of the events, at the expense of a round-trip
1425 to the server. Given that this will mostly occur on cases
1426 where DISPLAY = :0.0, and given the cost of what the motion
1427 event might do, its a good tradeoff.
1430 track_canvas.get_pointer (x, y);
1432 if (current_stepping_trackview) {
1433 /* don't keep the persistent stepped trackview if the mouse moves */
1434 current_stepping_trackview = 0;
1435 step_timeout.disconnect ();
1438 if (session && session->actively_recording()) {
1439 /* Sorry. no dragging stuff around while we record */
1443 drag_info.item_type = item_type;
1444 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1445 &drag_info.current_pointer_y);
1447 if (!from_autoscroll && drag_info.item) {
1448 /* item != 0 is the best test i can think of for dragging.
1450 if (!drag_info.move_threshold_passed) {
1452 bool x_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1453 bool y_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1455 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1457 // and change the initial grab loc/frame if this drag info wants us to
1459 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1460 drag_info.grab_frame = drag_info.current_pointer_frame;
1461 drag_info.grab_x = drag_info.current_pointer_x;
1462 drag_info.grab_y = drag_info.current_pointer_y;
1463 drag_info.last_pointer_frame = drag_info.grab_frame;
1464 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1469 switch (item_type) {
1470 case PlayheadCursorItem:
1471 case EditCursorItem:
1473 case GainControlPointItem:
1474 case RedirectAutomationControlPointItem:
1475 case GainAutomationControlPointItem:
1476 case PanAutomationControlPointItem:
1477 case TempoMarkerItem:
1478 case MeterMarkerItem:
1479 case RegionViewNameHighlight:
1480 case StartSelectionTrimItem:
1481 case EndSelectionTrimItem:
1484 case RedirectAutomationLineItem:
1485 case GainAutomationLineItem:
1486 case PanAutomationLineItem:
1487 case FadeInHandleItem:
1488 case FadeOutHandleItem:
1489 /* <CMT Additions> */
1490 case ImageFrameHandleStartItem:
1491 case ImageFrameHandleEndItem:
1492 case MarkerViewHandleStartItem:
1493 case MarkerViewHandleEndItem:
1494 /* </CMT Additions> */
1495 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1496 (event->motion.state & Gdk::BUTTON2_MASK))) {
1497 if (!from_autoscroll) {
1498 maybe_autoscroll (event);
1500 (this->*(drag_info.motion_callback)) (item, event);
1509 switch (mouse_mode) {
1514 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1515 (event->motion.state & GDK_BUTTON2_MASK))) {
1516 if (!from_autoscroll) {
1517 maybe_autoscroll (event);
1519 (this->*(drag_info.motion_callback)) (item, event);
1530 track_canvas_motion (event);
1531 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1539 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1541 if (drag_info.item == 0) {
1542 fatal << _("programming error: start_grab called without drag item") << endmsg;
1548 cursor = grabber_cursor;
1551 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1553 if (event->button.button == 2) {
1554 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1555 drag_info.y_constrained = true;
1556 drag_info.x_constrained = false;
1558 drag_info.y_constrained = false;
1559 drag_info.x_constrained = true;
1562 drag_info.x_constrained = false;
1563 drag_info.y_constrained = false;
1566 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1567 drag_info.last_pointer_frame = drag_info.grab_frame;
1568 drag_info.current_pointer_frame = drag_info.grab_frame;
1569 drag_info.current_pointer_x = drag_info.grab_x;
1570 drag_info.current_pointer_y = drag_info.grab_y;
1571 drag_info.cumulative_x_drag = 0;
1572 drag_info.cumulative_y_drag = 0;
1573 drag_info.first_move = true;
1574 drag_info.move_threshold_passed = false;
1575 drag_info.want_move_threshold = false;
1576 drag_info.pointer_frame_offset = 0;
1577 drag_info.brushing = false;
1578 drag_info.copied_location = 0;
1580 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1582 event->button.time);
1584 if (session && session->transport_rolling()) {
1585 drag_info.was_rolling = true;
1587 drag_info.was_rolling = false;
1590 switch (snap_type) {
1591 case SnapToRegionStart:
1592 case SnapToRegionEnd:
1593 case SnapToRegionSync:
1594 case SnapToRegionBoundary:
1595 build_region_boundary_cache ();
1603 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1605 drag_info.item->ungrab (0);
1606 drag_info.item = new_item;
1609 cursor = grabber_cursor;
1612 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1616 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1618 bool did_drag = false;
1620 stop_canvas_autoscroll ();
1622 if (drag_info.item == 0) {
1626 drag_info.item->ungrab (event->button.time);
1628 if (drag_info.finished_callback) {
1629 (this->*(drag_info.finished_callback)) (item, event);
1632 did_drag = !drag_info.first_move;
1634 hide_verbose_canvas_cursor();
1637 drag_info.copy = false;
1638 drag_info.motion_callback = 0;
1639 drag_info.finished_callback = 0;
1640 drag_info.last_trackview = 0;
1641 drag_info.last_frame_position = 0;
1642 drag_info.grab_frame = 0;
1643 drag_info.last_pointer_frame = 0;
1644 drag_info.current_pointer_frame = 0;
1645 drag_info.brushing = false;
1647 if (drag_info.copied_location) {
1648 delete drag_info.copied_location;
1649 drag_info.copied_location = 0;
1656 Editor::set_edit_cursor (GdkEvent* event)
1658 nframes_t pointer_frame = event_frame (event);
1660 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1661 if (snap_type != SnapToEditCursor) {
1662 snap_to (pointer_frame);
1666 edit_cursor->set_position (pointer_frame);
1667 edit_cursor_clock.set (pointer_frame);
1671 Editor::set_playhead_cursor (GdkEvent* event)
1673 nframes_t pointer_frame = event_frame (event);
1675 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1676 snap_to (pointer_frame);
1680 session->request_locate (pointer_frame, session->transport_rolling());
1685 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1687 drag_info.item = item;
1688 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1689 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1693 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1694 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1698 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1700 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1704 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1706 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1708 nframes_t fade_length;
1710 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1711 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1717 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1721 if (pos < (arv->region()->position() + 64)) {
1722 fade_length = 64; // this should be a minimum defined somewhere
1723 } else if (pos > arv->region()->last_frame()) {
1724 fade_length = arv->region()->length();
1726 fade_length = pos - arv->region()->position();
1728 /* mapover the region selection */
1730 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1732 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1738 tmp->reset_fade_in_shape_width (fade_length);
1741 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1743 drag_info.first_move = false;
1747 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1749 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1751 nframes_t fade_length;
1753 if (drag_info.first_move) return;
1755 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1756 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1761 if (pos < (arv->region()->position() + 64)) {
1762 fade_length = 64; // this should be a minimum defined somewhere
1763 } else if (pos > arv->region()->last_frame()) {
1764 fade_length = arv->region()->length();
1766 fade_length = pos - arv->region()->position();
1769 begin_reversible_command (_("change fade in length"));
1771 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1773 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1779 AutomationList& alist = tmp->audio_region()->fade_in();
1780 XMLNode &before = alist.get_state();
1782 tmp->audio_region()->set_fade_in_length (fade_length);
1784 XMLNode &after = alist.get_state();
1785 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1788 commit_reversible_command ();
1792 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1794 drag_info.item = item;
1795 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1796 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1800 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1801 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1805 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1807 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1811 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1813 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1815 nframes_t fade_length;
1817 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1818 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1823 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1827 if (pos > (arv->region()->last_frame() - 64)) {
1828 fade_length = 64; // this should really be a minimum fade defined somewhere
1830 else if (pos < arv->region()->position()) {
1831 fade_length = arv->region()->length();
1834 fade_length = arv->region()->last_frame() - pos;
1837 /* mapover the region selection */
1839 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1841 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1847 tmp->reset_fade_out_shape_width (fade_length);
1850 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1852 drag_info.first_move = false;
1856 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1858 if (drag_info.first_move) return;
1860 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1862 nframes_t fade_length;
1864 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1865 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1871 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1875 if (pos > (arv->region()->last_frame() - 64)) {
1876 fade_length = 64; // this should really be a minimum fade defined somewhere
1878 else if (pos < arv->region()->position()) {
1879 fade_length = arv->region()->length();
1882 fade_length = arv->region()->last_frame() - pos;
1885 begin_reversible_command (_("change fade out length"));
1887 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1889 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1895 AutomationList& alist = tmp->audio_region()->fade_out();
1896 XMLNode &before = alist.get_state();
1898 tmp->audio_region()->set_fade_out_length (fade_length);
1900 XMLNode &after = alist.get_state();
1901 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1904 commit_reversible_command ();
1908 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1910 drag_info.item = item;
1911 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1912 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1916 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1917 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1921 Cursor* cursor = (Cursor *) drag_info.data;
1923 if (cursor == playhead_cursor) {
1924 _dragging_playhead = true;
1926 if (session && drag_info.was_rolling) {
1927 session->request_stop ();
1930 if (session && session->is_auditioning()) {
1931 session->cancel_audition ();
1935 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1937 show_verbose_time_cursor (cursor->current_frame, 10);
1941 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1943 Cursor* cursor = (Cursor *) drag_info.data;
1944 nframes_t adjusted_frame;
1946 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1947 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1953 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1954 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1955 snap_to (adjusted_frame);
1959 if (adjusted_frame == drag_info.last_pointer_frame) return;
1961 cursor->set_position (adjusted_frame);
1963 if (cursor == edit_cursor) {
1964 edit_cursor_clock.set (cursor->current_frame);
1966 UpdateAllTransportClocks (cursor->current_frame);
1969 show_verbose_time_cursor (cursor->current_frame, 10);
1971 drag_info.last_pointer_frame = adjusted_frame;
1972 drag_info.first_move = false;
1976 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1978 if (drag_info.first_move) return;
1980 cursor_drag_motion_callback (item, event);
1982 _dragging_playhead = false;
1984 if (item == &playhead_cursor->canvas_item) {
1986 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1988 } else if (item == &edit_cursor->canvas_item) {
1989 edit_cursor->set_position (edit_cursor->current_frame);
1990 edit_cursor_clock.set (edit_cursor->current_frame);
1995 Editor::update_marker_drag_item (Location *location)
1997 double x1 = frame_to_pixel (location->start());
1998 double x2 = frame_to_pixel (location->end());
2000 if (location->is_mark()) {
2001 marker_drag_line_points.front().set_x(x1);
2002 marker_drag_line_points.back().set_x(x1);
2003 marker_drag_line->property_points() = marker_drag_line_points;
2006 range_marker_drag_rect->property_x1() = x1;
2007 range_marker_drag_rect->property_x2() = x2;
2012 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2016 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2017 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2023 Location *location = find_location_from_marker (marker, is_start);
2025 drag_info.item = item;
2026 drag_info.data = marker;
2027 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2028 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2032 drag_info.copied_location = new Location (*location);
2033 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2035 update_marker_drag_item (location);
2037 if (location->is_mark()) {
2038 marker_drag_line->show();
2039 marker_drag_line->raise_to_top();
2042 range_marker_drag_rect->show();
2043 range_marker_drag_rect->raise_to_top();
2046 if (is_start) show_verbose_time_cursor (location->start(), 10);
2047 else show_verbose_time_cursor (location->end(), 10);
2051 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2054 Marker* marker = (Marker *) drag_info.data;
2055 Location *real_location;
2056 Location *copy_location;
2058 bool move_both = false;
2062 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2063 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2068 nframes_t next = newframe;
2070 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2071 snap_to (newframe, 0, true);
2074 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2078 /* call this to find out if its the start or end */
2080 real_location = find_location_from_marker (marker, is_start);
2082 /* use the copy that we're "dragging" around */
2084 copy_location = drag_info.copied_location;
2086 f_delta = copy_location->end() - copy_location->start();
2088 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2092 if (copy_location->is_mark()) {
2095 copy_location->set_start (newframe);
2099 if (is_start) { // start-of-range marker
2102 copy_location->set_start (newframe);
2103 copy_location->set_end (newframe + f_delta);
2104 } else if (newframe < copy_location->end()) {
2105 copy_location->set_start (newframe);
2107 snap_to (next, 1, true);
2108 copy_location->set_end (next);
2109 copy_location->set_start (newframe);
2112 } else { // end marker
2115 copy_location->set_end (newframe);
2116 copy_location->set_start (newframe - f_delta);
2117 } else if (newframe > copy_location->start()) {
2118 copy_location->set_end (newframe);
2120 } else if (newframe > 0) {
2121 snap_to (next, -1, true);
2122 copy_location->set_start (next);
2123 copy_location->set_end (newframe);
2128 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2129 drag_info.first_move = false;
2131 update_marker_drag_item (copy_location);
2133 LocationMarkers* lm = find_location_markers (real_location);
2134 lm->set_position (copy_location->start(), copy_location->end());
2136 show_verbose_time_cursor (newframe, 10);
2140 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2142 if (drag_info.first_move) {
2143 marker_drag_motion_callback (item, event);
2147 Marker* marker = (Marker *) drag_info.data;
2151 begin_reversible_command ( _("move marker") );
2152 XMLNode &before = session->locations()->get_state();
2154 Location * location = find_location_from_marker (marker, is_start);
2157 if (location->is_mark()) {
2158 location->set_start (drag_info.copied_location->start());
2160 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2164 XMLNode &after = session->locations()->get_state();
2165 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2166 commit_reversible_command ();
2168 marker_drag_line->hide();
2169 range_marker_drag_rect->hide();
2173 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2176 MeterMarker* meter_marker;
2178 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2179 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2183 meter_marker = dynamic_cast<MeterMarker*> (marker);
2185 MetricSection& section (meter_marker->meter());
2187 if (!section.movable()) {
2191 drag_info.item = item;
2192 drag_info.data = marker;
2193 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2194 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2198 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2200 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2204 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2207 MeterMarker* meter_marker;
2209 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2210 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2214 meter_marker = dynamic_cast<MeterMarker*> (marker);
2216 // create a dummy marker for visual representation of moving the copy.
2217 // The actual copying is not done before we reach the finish callback.
2219 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2220 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2221 *new MeterSection(meter_marker->meter()));
2223 drag_info.item = &new_marker->the_item();
2224 drag_info.copy = true;
2225 drag_info.data = new_marker;
2226 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2227 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2231 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2233 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2237 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2239 MeterMarker* marker = (MeterMarker *) drag_info.data;
2240 nframes_t adjusted_frame;
2242 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2243 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2249 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2250 snap_to (adjusted_frame);
2253 if (adjusted_frame == drag_info.last_pointer_frame) return;
2255 marker->set_position (adjusted_frame);
2258 drag_info.last_pointer_frame = adjusted_frame;
2259 drag_info.first_move = false;
2261 show_verbose_time_cursor (adjusted_frame, 10);
2265 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2267 if (drag_info.first_move) return;
2269 meter_marker_drag_motion_callback (drag_info.item, event);
2271 MeterMarker* marker = (MeterMarker *) drag_info.data;
2274 TempoMap& map (session->tempo_map());
2275 map.bbt_time (drag_info.last_pointer_frame, when);
2277 if (drag_info.copy == true) {
2278 begin_reversible_command (_("copy meter mark"));
2279 XMLNode &before = map.get_state();
2280 map.add_meter (marker->meter(), when);
2281 XMLNode &after = map.get_state();
2282 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2283 commit_reversible_command ();
2285 // delete the dummy marker we used for visual representation of copying.
2286 // a new visual marker will show up automatically.
2289 begin_reversible_command (_("move meter mark"));
2290 XMLNode &before = map.get_state();
2291 map.move_meter (marker->meter(), when);
2292 XMLNode &after = map.get_state();
2293 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2294 commit_reversible_command ();
2299 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2302 TempoMarker* tempo_marker;
2304 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2305 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2309 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2310 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2314 MetricSection& section (tempo_marker->tempo());
2316 if (!section.movable()) {
2320 drag_info.item = item;
2321 drag_info.data = marker;
2322 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2323 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2327 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2328 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2332 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2335 TempoMarker* tempo_marker;
2337 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2338 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2342 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2343 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2347 // create a dummy marker for visual representation of moving the copy.
2348 // The actual copying is not done before we reach the finish callback.
2350 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2351 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2352 *new TempoSection(tempo_marker->tempo()));
2354 drag_info.item = &new_marker->the_item();
2355 drag_info.copy = true;
2356 drag_info.data = new_marker;
2357 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2358 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2362 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2364 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2368 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2370 TempoMarker* marker = (TempoMarker *) drag_info.data;
2371 nframes_t adjusted_frame;
2373 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2374 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2380 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2381 snap_to (adjusted_frame);
2384 if (adjusted_frame == drag_info.last_pointer_frame) return;
2386 /* OK, we've moved far enough to make it worth actually move the thing. */
2388 marker->set_position (adjusted_frame);
2390 show_verbose_time_cursor (adjusted_frame, 10);
2392 drag_info.last_pointer_frame = adjusted_frame;
2393 drag_info.first_move = false;
2397 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2399 if (drag_info.first_move) return;
2401 tempo_marker_drag_motion_callback (drag_info.item, event);
2403 TempoMarker* marker = (TempoMarker *) drag_info.data;
2406 TempoMap& map (session->tempo_map());
2407 map.bbt_time (drag_info.last_pointer_frame, when);
2409 if (drag_info.copy == true) {
2410 begin_reversible_command (_("copy tempo mark"));
2411 XMLNode &before = map.get_state();
2412 map.add_tempo (marker->tempo(), when);
2413 XMLNode &after = map.get_state();
2414 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2415 commit_reversible_command ();
2417 // delete the dummy marker we used for visual representation of copying.
2418 // a new visual marker will show up automatically.
2421 begin_reversible_command (_("move tempo mark"));
2422 XMLNode &before = map.get_state();
2423 map.move_tempo (marker->tempo(), when);
2424 XMLNode &after = map.get_state();
2425 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2426 commit_reversible_command ();
2431 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2433 ControlPoint* control_point;
2435 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2436 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2440 // We shouldn't remove the first or last gain point
2441 if (control_point->line.is_last_point(*control_point) ||
2442 control_point->line.is_first_point(*control_point)) {
2446 control_point->line.remove_point (*control_point);
2450 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2452 ControlPoint* control_point;
2454 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2455 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2459 control_point->line.remove_point (*control_point);
2463 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2465 ControlPoint* control_point;
2467 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2468 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2472 drag_info.item = item;
2473 drag_info.data = control_point;
2474 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2475 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2477 start_grab (event, fader_cursor);
2479 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2481 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2482 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2483 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2485 show_verbose_canvas_cursor ();
2489 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2491 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2493 double cx = drag_info.current_pointer_x;
2494 double cy = drag_info.current_pointer_y;
2496 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2497 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2499 if (drag_info.x_constrained) {
2500 cx = drag_info.grab_x;
2502 if (drag_info.y_constrained) {
2503 cy = drag_info.grab_y;
2506 cp->line.parent_group().w2i (cx, cy);
2510 cy = min ((double) cp->line.height(), cy);
2512 //translate cx to frames
2513 nframes_t cx_frames = unit_to_frame (cx);
2515 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2516 snap_to (cx_frames);
2519 float fraction = 1.0 - (cy / cp->line.height());
2523 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2529 cp->line.point_drag (*cp, cx_frames , fraction, push);
2531 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2533 drag_info.first_move = false;
2537 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2539 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2541 if (drag_info.first_move) {
2545 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2546 reset_point_selection ();
2550 control_point_drag_motion_callback (item, event);
2552 cp->line.end_drag (cp);
2556 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2558 switch (mouse_mode) {
2560 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2561 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2569 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2573 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2574 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2578 start_line_grab (al, event);
2582 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2586 nframes_t frame_within_region;
2588 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2592 cx = event->button.x;
2593 cy = event->button.y;
2594 line->parent_group().w2i (cx, cy);
2595 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2597 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2598 current_line_drag_info.after)) {
2599 /* no adjacent points */
2603 drag_info.item = &line->grab_item();
2604 drag_info.data = line;
2605 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2606 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2608 start_grab (event, fader_cursor);
2610 double fraction = 1.0 - (cy / line->height());
2612 line->start_drag (0, drag_info.grab_frame, fraction);
2614 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2615 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2616 show_verbose_canvas_cursor ();
2620 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2622 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2623 double cx = drag_info.current_pointer_x;
2624 double cy = drag_info.current_pointer_y;
2626 line->parent_group().w2i (cx, cy);
2629 fraction = 1.0 - (cy / line->height());
2633 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2639 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2641 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2645 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2647 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2648 line_drag_motion_callback (item, event);
2653 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2655 if (selection->regions.empty() || clicked_regionview == 0) {
2659 drag_info.copy = false;
2660 drag_info.item = item;
2661 drag_info.data = clicked_regionview;
2662 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2663 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2668 TimeAxisView* tvp = clicked_trackview;
2669 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2671 if (tv && tv->is_audio_track()) {
2672 speed = tv->get_diskstream()->speed();
2675 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2676 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2677 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2678 // we want a move threshold
2679 drag_info.want_move_threshold = true;
2681 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2683 begin_reversible_command (_("move region(s)"));
2687 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2689 if (selection->regions.empty() || clicked_regionview == 0) {
2693 drag_info.copy = true;
2694 drag_info.item = item;
2695 drag_info.data = clicked_regionview;
2699 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2700 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2703 if (atv && atv->is_audio_track()) {
2704 speed = atv->get_diskstream()->speed();
2707 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2708 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2709 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2710 // we want a move threshold
2711 drag_info.want_move_threshold = true;
2712 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2713 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2714 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2718 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2720 if (selection->regions.empty() || clicked_regionview == 0) {
2724 drag_info.copy = false;
2725 drag_info.item = item;
2726 drag_info.data = clicked_regionview;
2727 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2728 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2733 TimeAxisView* tvp = clicked_trackview;
2734 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2736 if (tv && tv->is_audio_track()) {
2737 speed = tv->get_diskstream()->speed();
2740 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2741 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2742 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2743 // we want a move threshold
2744 drag_info.want_move_threshold = true;
2745 drag_info.brushing = true;
2747 begin_reversible_command (_("Drag region brush"));
2751 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2755 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2756 nframes_t pending_region_position = 0;
2757 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2758 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2759 bool clamp_y_axis = false;
2760 vector<int32_t> height_list(512) ;
2761 vector<int32_t>::iterator j;
2763 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2765 drag_info.want_move_threshold = false; // don't copy again
2767 /* duplicate the region(s) */
2769 vector<RegionView*> new_regionviews;
2771 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2774 AudioRegionView* arv;
2779 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
2780 /* XXX handle MIDI here */
2784 nrv = new AudioRegionView (*arv);
2785 nrv->get_canvas_group()->show ();
2787 new_regionviews.push_back (nrv);
2790 if (new_regionviews.empty()) {
2794 /* reset selection to new regionviews */
2796 selection->set (new_regionviews);
2798 /* reset drag_info data to reflect the fact that we are dragging the copies */
2800 drag_info.data = new_regionviews.front();
2802 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2805 /* Which trackview is this ? */
2807 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2808 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2810 /* The region motion is only processed if the pointer is over
2814 if (!tv || !tv->is_audio_track()) {
2815 /* To make sure we hide the verbose canvas cursor when the mouse is
2816 not held over and audiotrack.
2818 hide_verbose_canvas_cursor ();
2822 original_pointer_order = drag_info.last_trackview->order;
2824 /************************************************************
2826 ************************************************************/
2828 if (drag_info.brushing) {
2829 clamp_y_axis = true;
2834 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2836 int32_t children = 0, numtracks = 0;
2837 // XXX hard coding track limit, oh my, so very very bad
2838 bitset <1024> tracks (0x00);
2839 /* get a bitmask representing the visible tracks */
2841 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2842 TimeAxisView *tracklist_timeview;
2843 tracklist_timeview = (*i);
2844 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2845 list<TimeAxisView*> children_list;
2847 /* zeroes are audio tracks. ones are other types. */
2849 if (!atv2->hidden()) {
2851 if (visible_y_high < atv2->order) {
2852 visible_y_high = atv2->order;
2854 if (visible_y_low > atv2->order) {
2855 visible_y_low = atv2->order;
2858 if (!atv2->is_audio_track()) {
2859 tracks = tracks |= (0x01 << atv2->order);
2862 height_list[atv2->order] = (*i)->height;
2864 if ((children_list = atv2->get_child_list()).size() > 0) {
2865 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2866 tracks = tracks |= (0x01 << (atv2->order + children));
2867 height_list[atv2->order + children] = (*j)->height;
2875 /* find the actual span according to the canvas */
2877 canvas_pointer_y_span = pointer_y_span;
2878 if (drag_info.last_trackview->order >= tv->order) {
2880 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2881 if (height_list[y] == 0 ) {
2882 canvas_pointer_y_span--;
2887 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2888 if ( height_list[y] == 0 ) {
2889 canvas_pointer_y_span++;
2894 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2895 RegionView* rv2 = (*i);
2896 double ix1, ix2, iy1, iy2;
2899 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2900 rv2->get_canvas_group()->i2w (ix1, iy1);
2901 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2902 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2904 if (atv2->order != original_pointer_order) {
2905 /* this isn't the pointer track */
2907 if (canvas_pointer_y_span > 0) {
2909 /* moving up the canvas */
2910 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2912 int32_t visible_tracks = 0;
2913 while (visible_tracks < canvas_pointer_y_span ) {
2916 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2917 /* we're passing through a hidden track */
2922 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2923 clamp_y_axis = true;
2927 clamp_y_axis = true;
2930 } else if (canvas_pointer_y_span < 0) {
2932 /*moving down the canvas*/
2934 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2937 int32_t visible_tracks = 0;
2939 while (visible_tracks > canvas_pointer_y_span ) {
2942 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2946 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2947 clamp_y_axis = true;
2952 clamp_y_axis = true;
2958 /* this is the pointer's track */
2959 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2960 clamp_y_axis = true;
2961 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2962 clamp_y_axis = true;
2970 } else if (drag_info.last_trackview == tv) {
2971 clamp_y_axis = true;
2975 if (!clamp_y_axis) {
2976 drag_info.last_trackview = tv;
2979 /************************************************************
2981 ************************************************************/
2983 /* compute the amount of pointer motion in frames, and where
2984 the region would be if we moved it by that much.
2987 if (drag_info.move_threshold_passed) {
2989 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2991 nframes_t sync_frame;
2992 nframes_t sync_offset;
2995 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2997 sync_offset = rv->region()->sync_offset (sync_dir);
2998 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3000 /* we snap if the snap modifier is not enabled.
3003 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3004 snap_to (sync_frame);
3007 if (sync_frame - sync_offset <= sync_frame) {
3008 pending_region_position = sync_frame - (sync_dir*sync_offset);
3010 pending_region_position = 0;
3014 pending_region_position = 0;
3017 if (pending_region_position > max_frames - rv->region()->length()) {
3018 pending_region_position = drag_info.last_frame_position;
3021 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3023 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3025 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3026 to make it appear at the new location.
3029 if (pending_region_position > drag_info.last_frame_position) {
3030 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3032 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3035 drag_info.last_frame_position = pending_region_position;
3042 /* threshold not passed */
3047 /*************************************************************
3049 ************************************************************/
3051 if (x_delta == 0 && (pointer_y_span == 0)) {
3052 /* haven't reached next snap point, and we're not switching
3053 trackviews. nothing to do.
3060 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3062 RegionView* rv2 = (*i);
3064 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3066 double ix1, ix2, iy1, iy2;
3067 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3068 rv2->get_canvas_group()->i2w (ix1, iy1);
3077 /*************************************************************
3079 ************************************************************/
3083 if (drag_info.first_move) {
3084 if (drag_info.move_threshold_passed) {
3095 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3096 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3098 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3100 RegionView* rv = (*i);
3101 double ix1, ix2, iy1, iy2;
3102 int32_t temp_pointer_y_span = pointer_y_span;
3104 /* get item BBox, which will be relative to parent. so we have
3105 to query on a child, then convert to world coordinates using
3109 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3110 rv->get_canvas_group()->i2w (ix1, iy1);
3111 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3112 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3113 AudioTimeAxisView* temp_atv;
3115 if ((pointer_y_span != 0) && !clamp_y_axis) {
3118 for (j = height_list.begin(); j!= height_list.end(); j++) {
3119 if (x == canvas_atv->order) {
3120 /* we found the track the region is on */
3121 if (x != original_pointer_order) {
3122 /*this isn't from the same track we're dragging from */
3123 temp_pointer_y_span = canvas_pointer_y_span;
3125 while (temp_pointer_y_span > 0) {
3126 /* we're moving up canvas-wise,
3127 so we need to find the next track height
3129 if (j != height_list.begin()) {
3132 if (x != original_pointer_order) {
3133 /* we're not from the dragged track, so ignore hidden tracks. */
3135 temp_pointer_y_span++;
3139 temp_pointer_y_span--;
3141 while (temp_pointer_y_span < 0) {
3143 if (x != original_pointer_order) {
3145 temp_pointer_y_span--;
3149 if (j != height_list.end()) {
3152 temp_pointer_y_span++;
3154 /* find out where we'll be when we move and set height accordingly */
3156 tvp2 = trackview_by_y_position (iy1 + y_delta);
3157 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3158 rv->set_height (temp_atv->height);
3160 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3161 personally, i think this can confuse things, but never mind.
3164 //const GdkColor& col (temp_atv->view->get_region_color());
3165 //rv->set_color (const_cast<GdkColor&>(col));
3172 /* prevent the regionview from being moved to before
3173 the zero position on the canvas.
3178 if (-x_delta > ix1) {
3181 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3182 x_delta = max_frames - rv->region()->last_frame();
3186 if (drag_info.first_move) {
3188 /* hide any dependent views */
3190 rv->get_time_axis_view().hide_dependent_views (*rv);
3192 /* this is subtle. raising the regionview itself won't help,
3193 because raise_to_top() just puts the item on the top of
3194 its parent's stack. so, we need to put the trackview canvas_display group
3195 on the top, since its parent is the whole canvas.
3198 rv->get_canvas_group()->raise_to_top();
3199 rv->get_time_axis_view().canvas_display->raise_to_top();
3200 cursor_group->raise_to_top();
3202 rv->fake_set_opaque (true);
3205 if (drag_info.brushing) {
3206 mouse_brush_insert_region (rv, pending_region_position);
3208 rv->move (x_delta, y_delta);
3211 } /* foreach region */
3215 if (drag_info.first_move && drag_info.move_threshold_passed) {
3216 cursor_group->raise_to_top();
3217 drag_info.first_move = false;
3220 if (x_delta != 0 && !drag_info.brushing) {
3221 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3226 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3229 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3230 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3231 bool nocommit = true;
3233 RouteTimeAxisView* atv;
3234 bool regionview_y_movement;
3235 bool regionview_x_movement;
3236 vector<RegionView*> copies;
3238 /* first_move is set to false if the regionview has been moved in the
3242 if (drag_info.first_move) {
3249 /* The regionview has been moved at some stage during the grab so we need
3250 to account for any mouse movement between this event and the last one.
3253 region_drag_motion_callback (item, event);
3255 if (drag_info.brushing) {
3256 /* all changes were made during motion event handlers */
3258 if (drag_info.copy) {
3259 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3260 copies.push_back (*i);
3267 /* adjust for track speed */
3270 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3271 if (atv && atv->get_diskstream()) {
3272 speed = atv->get_diskstream()->speed();
3275 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3276 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3278 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3279 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3283 if (drag_info.copy) {
3284 if (drag_info.x_constrained) {
3285 op_string = _("fixed time region copy");
3287 op_string = _("region copy");
3290 if (drag_info.x_constrained) {
3291 op_string = _("fixed time region drag");
3293 op_string = _("region drag");
3297 begin_reversible_command (op_string);
3299 if (regionview_y_movement) {
3301 /* moved to a different audio track. */
3303 vector<RegionView*> new_selection;
3305 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3307 RegionView* rv = (*i);
3309 double ix1, ix2, iy1, iy2;
3311 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3312 rv->get_canvas_group()->i2w (ix1, iy1);
3313 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3314 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3316 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3317 boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
3319 where = (nframes_t) (unit_to_frame (ix1) * speed);
3320 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3322 /* undo the previous hide_dependent_views so that xfades don't
3323 disappear on copying regions
3326 rv->get_time_axis_view().reveal_dependent_views (*rv);
3328 if (!drag_info.copy) {
3330 /* the region that used to be in the old playlist is not
3331 moved to the new one - we make a copy of it. as a result,
3332 any existing editor for the region should no longer be
3336 rv->hide_region_editor();
3337 rv->fake_set_opaque (false);
3339 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3340 from_playlist->remove_region ((rv->region()));
3341 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3345 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3347 copies.push_back (rv);
3350 latest_regionview = 0;
3352 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3353 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3354 to_playlist->add_region (new_region, where);
3355 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3358 if (latest_regionview) {
3359 new_selection.push_back (latest_regionview);
3362 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3363 was selected in all of them, then removing it from the playlist will have removed all
3364 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3365 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3366 corresponding regionview, and the selection is now empty).
3368 this could have invalidated any and all iterators into the region selection.
3370 the heuristic we use here is: if the region selection is empty, break out of the loop
3371 here. if the region selection is not empty, then restart the loop because we know that
3372 we must have removed at least the region(view) we've just been working on as well as any
3373 that we processed on previous iterations.
3375 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3376 we can just iterate.
3379 if (drag_info.copy) {
3382 if (selection->regions.empty()) {
3385 i = selection->regions.by_layer().begin();
3390 selection->set (new_selection);
3394 /* motion within a single track */
3396 list<RegionView*> regions = selection->regions.by_layer();
3398 if (drag_info.copy) {
3399 selection->clear_regions();
3402 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3406 if (rv->region()->locked()) {
3411 if (regionview_x_movement) {
3412 double ownspeed = 1.0;
3413 atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3415 if (atv && atv->get_diskstream()) {
3416 ownspeed = atv->get_diskstream()->speed();
3419 /* base the new region position on the current position of the regionview.*/
3421 double ix1, ix2, iy1, iy2;
3423 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3424 rv->get_canvas_group()->i2w (ix1, iy1);
3425 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3429 where = rv->region()->position();
3432 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3434 assert (to_playlist);
3438 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3440 if (drag_info.copy) {
3442 boost::shared_ptr<Region> newregion;
3443 boost::shared_ptr<Region> ar;
3445 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3446 newregion = RegionFactory::create (ar);
3448 /* XXX MIDI HERE drobilla */
3454 latest_regionview = 0;
3455 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3456 to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
3459 if (latest_regionview) {
3460 atv->reveal_dependent_views (*latest_regionview);
3461 selection->add (latest_regionview);
3464 /* if the original region was locked, we don't care for the new one */
3466 newregion->set_locked (false);
3470 /* just change the model */
3472 rv->region()->set_position (where, (void*) this);
3478 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3480 if (drag_info.copy) {
3481 copies.push_back (rv);
3489 commit_reversible_command ();
3492 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3498 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3500 /* Either add to or set the set the region selection, unless
3501 this is an alignment click (control used)
3504 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3505 TimeAxisView* tv = &rv.get_time_axis_view();
3506 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3508 if (atv && atv->is_audio_track()) {
3509 speed = atv->get_diskstream()->speed();
3512 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3514 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3516 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3518 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3522 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3528 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3534 nframes_t frame_rate;
3541 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3542 case AudioClock::BBT:
3543 session->bbt_time (frame, bbt);
3544 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3547 case AudioClock::SMPTE:
3548 session->smpte_time (frame, smpte);
3549 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3552 case AudioClock::MinSec:
3553 /* XXX this is copied from show_verbose_duration_cursor() */
3554 frame_rate = session->frame_rate();
3555 hours = frame / (frame_rate * 3600);
3556 frame = frame % (frame_rate * 3600);
3557 mins = frame / (frame_rate * 60);
3558 frame = frame % (frame_rate * 60);
3559 secs = (float) frame / (float) frame_rate;
3560 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3564 snprintf (buf, sizeof(buf), "%u", frame);
3568 if (xpos >= 0 && ypos >=0) {
3569 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3572 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3574 show_verbose_canvas_cursor ();
3578 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3585 nframes_t distance, frame_rate;
3587 Meter meter_at_start(session->tempo_map().meter_at(start));
3593 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3594 case AudioClock::BBT:
3595 session->bbt_time (start, sbbt);
3596 session->bbt_time (end, ebbt);
3599 /* XXX this computation won't work well if the
3600 user makes a selection that spans any meter changes.
3603 ebbt.bars -= sbbt.bars;
3604 if (ebbt.beats >= sbbt.beats) {
3605 ebbt.beats -= sbbt.beats;
3608 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3610 if (ebbt.ticks >= sbbt.ticks) {
3611 ebbt.ticks -= sbbt.ticks;
3614 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3617 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3620 case AudioClock::SMPTE:
3621 session->smpte_duration (end - start, smpte);
3622 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3625 case AudioClock::MinSec:
3626 /* XXX this stuff should be elsewhere.. */
3627 distance = end - start;
3628 frame_rate = session->frame_rate();
3629 hours = distance / (frame_rate * 3600);
3630 distance = distance % (frame_rate * 3600);
3631 mins = distance / (frame_rate * 60);
3632 distance = distance % (frame_rate * 60);
3633 secs = (float) distance / (float) frame_rate;
3634 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3638 snprintf (buf, sizeof(buf), "%u", end - start);
3642 if (xpos >= 0 && ypos >=0) {
3643 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3646 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3648 show_verbose_canvas_cursor ();
3652 Editor::collect_new_region_view (RegionView* rv)
3654 latest_regionview = rv;
3658 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3660 if (clicked_regionview == 0) {
3664 /* lets try to create new Region for the selection */
3666 vector<boost::shared_ptr<AudioRegion> > new_regions;
3667 create_region_from_selection (new_regions);
3669 if (new_regions.empty()) {
3673 /* XXX fix me one day to use all new regions */
3675 boost::shared_ptr<Region> region (new_regions.front());
3677 /* add it to the current stream/playlist.
3679 tricky: the streamview for the track will add a new regionview. we will
3680 catch the signal it sends when it creates the regionview to
3681 set the regionview we want to then drag.
3684 latest_regionview = 0;
3685 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3687 /* A selection grab currently creates two undo/redo operations, one for
3688 creating the new region and another for moving it.
3691 begin_reversible_command (_("selection grab"));
3693 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3695 XMLNode *before = &(playlist->get_state());
3696 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3697 XMLNode *after = &(playlist->get_state());
3698 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3700 commit_reversible_command ();
3704 if (latest_regionview == 0) {
3705 /* something went wrong */
3709 /* we need to deselect all other regionviews, and select this one
3710 i'm ignoring undo stuff, because the region creation will take care of it */
3711 selection->set (latest_regionview);
3713 drag_info.item = latest_regionview->get_canvas_group();
3714 drag_info.data = latest_regionview;
3715 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3716 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3720 drag_info.last_trackview = clicked_trackview;
3721 drag_info.last_frame_position = latest_regionview->region()->position();
3722 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3724 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3728 Editor::cancel_selection ()
3730 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3731 (*i)->hide_selection ();
3733 begin_reversible_command (_("cancel selection"));
3734 selection->clear ();
3735 clicked_selection = 0;
3736 commit_reversible_command ();
3740 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3742 nframes_t start = 0;
3749 drag_info.item = item;
3750 drag_info.motion_callback = &Editor::drag_selection;
3751 drag_info.finished_callback = &Editor::end_selection_op;
3756 case CreateSelection:
3757 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3758 drag_info.copy = true;
3760 drag_info.copy = false;
3762 start_grab (event, selector_cursor);
3765 case SelectionStartTrim:
3766 if (clicked_trackview) {
3767 clicked_trackview->order_selection_trims (item, true);
3769 start_grab (event, trimmer_cursor);
3770 start = selection->time[clicked_selection].start;
3771 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3774 case SelectionEndTrim:
3775 if (clicked_trackview) {
3776 clicked_trackview->order_selection_trims (item, false);
3778 start_grab (event, trimmer_cursor);
3779 end = selection->time[clicked_selection].end;
3780 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3784 start = selection->time[clicked_selection].start;
3786 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3790 if (selection_op == SelectionMove) {
3791 show_verbose_time_cursor(start, 10);
3793 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3798 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3800 nframes_t start = 0;
3803 nframes_t pending_position;
3805 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3806 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3808 pending_position = 0;
3811 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3812 snap_to (pending_position);
3815 /* only alter selection if the current frame is
3816 different from the last frame position (adjusted)
3819 if (pending_position == drag_info.last_pointer_frame) return;
3821 switch (selection_op) {
3822 case CreateSelection:
3824 if (drag_info.first_move) {
3825 snap_to (drag_info.grab_frame);
3828 if (pending_position < drag_info.grab_frame) {
3829 start = pending_position;
3830 end = drag_info.grab_frame;
3832 end = pending_position;
3833 start = drag_info.grab_frame;
3836 /* first drag: Either add to the selection
3837 or create a new selection->
3840 if (drag_info.first_move) {
3842 begin_reversible_command (_("range selection"));
3844 if (drag_info.copy) {
3845 /* adding to the selection */
3846 clicked_selection = selection->add (start, end);
3847 drag_info.copy = false;
3849 /* new selection-> */
3850 clicked_selection = selection->set (clicked_trackview, start, end);
3855 case SelectionStartTrim:
3857 if (drag_info.first_move) {
3858 begin_reversible_command (_("trim selection start"));
3861 start = selection->time[clicked_selection].start;
3862 end = selection->time[clicked_selection].end;
3864 if (pending_position > end) {
3867 start = pending_position;
3871 case SelectionEndTrim:
3873 if (drag_info.first_move) {
3874 begin_reversible_command (_("trim selection end"));
3877 start = selection->time[clicked_selection].start;
3878 end = selection->time[clicked_selection].end;
3880 if (pending_position < start) {
3883 end = pending_position;
3890 if (drag_info.first_move) {
3891 begin_reversible_command (_("move selection"));
3894 start = selection->time[clicked_selection].start;
3895 end = selection->time[clicked_selection].end;
3897 length = end - start;
3899 start = pending_position;
3902 end = start + length;
3907 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3908 start_canvas_autoscroll (1);
3912 selection->replace (clicked_selection, start, end);
3915 drag_info.last_pointer_frame = pending_position;
3916 drag_info.first_move = false;
3918 if (selection_op == SelectionMove) {
3919 show_verbose_time_cursor(start, 10);
3921 show_verbose_time_cursor(pending_position, 10);
3926 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3928 if (!drag_info.first_move) {
3929 drag_selection (item, event);
3930 /* XXX this is not object-oriented programming at all. ick */
3931 if (selection->time.consolidate()) {
3932 selection->TimeChanged ();
3934 commit_reversible_command ();
3936 /* just a click, no pointer movement.*/
3938 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3940 selection->clear_time();
3945 /* XXX what happens if its a music selection? */
3946 session->set_audio_range (selection->time);
3947 stop_canvas_autoscroll ();
3951 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3954 TimeAxisView* tvp = clicked_trackview;
3955 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3957 if (tv && tv->is_audio_track()) {
3958 speed = tv->get_diskstream()->speed();
3961 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3962 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3963 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3965 //drag_info.item = clicked_regionview->get_name_highlight();
3966 drag_info.item = item;
3967 drag_info.motion_callback = &Editor::trim_motion_callback;
3968 drag_info.finished_callback = &Editor::trim_finished_callback;
3970 start_grab (event, trimmer_cursor);
3972 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3973 trim_op = ContentsTrim;
3975 /* These will get overridden for a point trim.*/
3976 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3977 /* closer to start */
3978 trim_op = StartTrim;
3979 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3987 show_verbose_time_cursor(region_start, 10);
3990 show_verbose_time_cursor(region_end, 10);
3993 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3999 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4001 RegionView* rv = clicked_regionview;
4002 nframes_t frame_delta = 0;
4003 bool left_direction;
4004 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4006 /* snap modifier works differently here..
4007 its' current state has to be passed to the
4008 various trim functions in order to work properly
4012 TimeAxisView* tvp = clicked_trackview;
4013 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4014 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4016 if (tv && tv->is_audio_track()) {
4017 speed = tv->get_diskstream()->speed();
4020 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4021 left_direction = true;
4023 left_direction = false;
4027 snap_to (drag_info.current_pointer_frame);
4030 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4034 if (drag_info.first_move) {
4040 trim_type = "Region start trim";
4043 trim_type = "Region end trim";
4046 trim_type = "Region content trim";
4050 begin_reversible_command (trim_type);
4052 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4053 (*i)->fake_set_opaque(false);
4054 (*i)->region()->freeze ();
4056 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4058 arv->temporarily_hide_envelope ();
4060 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4061 insert_result = motion_frozen_playlists.insert (pl);
4062 if (insert_result.second) {
4063 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4068 if (left_direction) {
4069 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4071 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4076 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4079 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4080 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4086 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4089 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4090 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4097 bool swap_direction = false;
4099 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4100 swap_direction = true;
4103 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4104 i != selection->regions.by_layer().end(); ++i)
4106 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4114 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4117 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4120 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4124 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4125 drag_info.first_move = false;
4129 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4131 boost::shared_ptr<Region> region (rv.region());
4133 if (region->locked()) {
4137 nframes_t new_bound;
4140 TimeAxisView* tvp = clicked_trackview;
4141 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4143 if (tv && tv->is_audio_track()) {
4144 speed = tv->get_diskstream()->speed();
4147 if (left_direction) {
4148 if (swap_direction) {
4149 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4151 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4154 if (swap_direction) {
4155 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4157 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4162 snap_to (new_bound);
4164 region->trim_start ((nframes_t) (new_bound * speed), this);
4165 rv.region_changed (StartChanged);
4169 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4171 boost::shared_ptr<Region> region (rv.region());
4173 if (region->locked()) {
4177 nframes_t new_bound;
4180 TimeAxisView* tvp = clicked_trackview;
4181 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4183 if (tv && tv->is_audio_track()) {
4184 speed = tv->get_diskstream()->speed();
4187 if (left_direction) {
4188 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4190 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4194 snap_to (new_bound, (left_direction ? 0 : 1));
4197 region->trim_front ((nframes_t) (new_bound * speed), this);
4199 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4203 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4205 boost::shared_ptr<Region> region (rv.region());
4207 if (region->locked()) {
4211 nframes_t new_bound;
4214 TimeAxisView* tvp = clicked_trackview;
4215 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4217 if (tv && tv->is_audio_track()) {
4218 speed = tv->get_diskstream()->speed();
4221 if (left_direction) {
4222 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4224 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4228 snap_to (new_bound);
4230 region->trim_end ((nframes_t) (new_bound * speed), this);
4231 rv.region_changed (LengthChanged);
4235 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4237 if (!drag_info.first_move) {
4238 trim_motion_callback (item, event);
4240 if (!clicked_regionview->get_selected()) {
4241 thaw_region_after_trim (*clicked_regionview);
4244 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4245 i != selection->regions.by_layer().end(); ++i)
4247 thaw_region_after_trim (**i);
4248 (*i)->fake_set_opaque (true);
4252 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4254 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4257 motion_frozen_playlists.clear ();
4259 commit_reversible_command();
4261 /* no mouse movement */
4267 Editor::point_trim (GdkEvent* event)
4269 RegionView* rv = clicked_regionview;
4270 nframes_t new_bound = drag_info.current_pointer_frame;
4272 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4273 snap_to (new_bound);
4276 /* Choose action dependant on which button was pressed */
4277 switch (event->button.button) {
4279 trim_op = StartTrim;
4280 begin_reversible_command (_("Start point trim"));
4282 if (rv->get_selected()) {
4284 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4285 i != selection->regions.by_layer().end(); ++i)
4287 if (!(*i)->region()->locked()) {
4288 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4289 XMLNode &before = pl->get_state();
4290 (*i)->region()->trim_front (new_bound, this);
4291 XMLNode &after = pl->get_state();
4292 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4298 if (!rv->region()->locked()) {
4299 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4300 XMLNode &before = pl->get_state();
4301 rv->region()->trim_front (new_bound, this);
4302 XMLNode &after = pl->get_state();
4303 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4307 commit_reversible_command();
4312 begin_reversible_command (_("End point trim"));
4314 if (rv->get_selected()) {
4316 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4318 if (!(*i)->region()->locked()) {
4319 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4320 XMLNode &before = pl->get_state();
4321 (*i)->region()->trim_end (new_bound, this);
4322 XMLNode &after = pl->get_state();
4323 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4329 if (!rv->region()->locked()) {
4330 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4331 XMLNode &before = pl->get_state();
4332 rv->region()->trim_end (new_bound, this);
4333 XMLNode &after = pl->get_state();
4334 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4338 commit_reversible_command();
4347 Editor::thaw_region_after_trim (RegionView& rv)
4349 boost::shared_ptr<Region> region (rv.region());
4351 if (region->locked()) {
4355 region->thaw (_("trimmed region"));
4356 XMLNode &after = region->playlist()->get_state();
4357 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4359 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4361 arv->unhide_envelope ();
4365 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4370 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4371 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4375 Location* location = find_location_from_marker (marker, is_start);
4376 location->set_hidden (true, this);
4381 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4387 drag_info.item = item;
4388 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4389 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4391 range_marker_op = op;
4393 if (!temp_location) {
4394 temp_location = new Location;
4398 case CreateRangeMarker:
4399 case CreateTransportMarker:
4401 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4402 drag_info.copy = true;
4404 drag_info.copy = false;
4406 start_grab (event, selector_cursor);
4410 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4415 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4417 nframes_t start = 0;
4419 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4421 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4422 snap_to (drag_info.current_pointer_frame);
4425 /* only alter selection if the current frame is
4426 different from the last frame position.
4429 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4431 switch (range_marker_op) {
4432 case CreateRangeMarker:
4433 case CreateTransportMarker:
4434 if (drag_info.first_move) {
4435 snap_to (drag_info.grab_frame);
4438 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4439 start = drag_info.current_pointer_frame;
4440 end = drag_info.grab_frame;
4442 end = drag_info.current_pointer_frame;
4443 start = drag_info.grab_frame;
4446 /* first drag: Either add to the selection
4447 or create a new selection.
4450 if (drag_info.first_move) {
4452 temp_location->set (start, end);
4456 update_marker_drag_item (temp_location);
4457 range_marker_drag_rect->show();
4458 range_marker_drag_rect->raise_to_top();
4464 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4465 start_canvas_autoscroll (1);
4469 temp_location->set (start, end);
4471 double x1 = frame_to_pixel (start);
4472 double x2 = frame_to_pixel (end);
4473 crect->property_x1() = x1;
4474 crect->property_x2() = x2;
4476 update_marker_drag_item (temp_location);
4479 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4480 drag_info.first_move = false;
4482 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4487 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4489 Location * newloc = 0;
4492 if (!drag_info.first_move) {
4493 drag_range_markerbar_op (item, event);
4495 switch (range_marker_op) {
4496 case CreateRangeMarker:
4498 begin_reversible_command (_("new range marker"));
4499 XMLNode &before = session->locations()->get_state();
4500 session->locations()->next_available_name(rangename,"unnamed");
4501 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4502 session->locations()->add (newloc, true);
4503 XMLNode &after = session->locations()->get_state();
4504 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4505 commit_reversible_command ();
4507 range_bar_drag_rect->hide();
4508 range_marker_drag_rect->hide();
4512 case CreateTransportMarker:
4513 // popup menu to pick loop or punch
4514 new_transport_marker_context_menu (&event->button, item);
4519 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4521 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4526 start = session->locations()->first_mark_before (drag_info.grab_frame);
4527 end = session->locations()->first_mark_after (drag_info.grab_frame);
4529 if (end == max_frames) {
4530 end = session->current_end_frame ();
4534 start = session->current_start_frame ();
4537 switch (mouse_mode) {
4539 /* find the two markers on either side and then make the selection from it */
4540 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4544 /* find the two markers on either side of the click and make the range out of it */
4545 selection->set (0, start, end);
4554 stop_canvas_autoscroll ();
4560 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4562 drag_info.item = item;
4563 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4564 drag_info.finished_callback = &Editor::end_mouse_zoom;
4566 start_grab (event, zoom_cursor);
4568 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4572 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4577 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4578 snap_to (drag_info.current_pointer_frame);
4580 if (drag_info.first_move) {
4581 snap_to (drag_info.grab_frame);
4585 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4587 /* base start and end on initial click position */
4588 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4589 start = drag_info.current_pointer_frame;
4590 end = drag_info.grab_frame;
4592 end = drag_info.current_pointer_frame;
4593 start = drag_info.grab_frame;
4598 if (drag_info.first_move) {
4600 zoom_rect->raise_to_top();
4603 reposition_zoom_rect(start, end);
4605 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4606 drag_info.first_move = false;
4608 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4613 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4615 if (!drag_info.first_move) {
4616 drag_mouse_zoom (item, event);
4618 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4619 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4621 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4624 temporal_zoom_to_frame (false, drag_info.grab_frame);
4626 temporal_zoom_step (false);
4627 center_screen (drag_info.grab_frame);
4635 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4637 double x1 = frame_to_pixel (start);
4638 double x2 = frame_to_pixel (end);
4639 double y2 = full_canvas_height - 1.0;
4641 zoom_rect->property_x1() = x1;
4642 zoom_rect->property_y1() = 1.0;
4643 zoom_rect->property_x2() = x2;
4644 zoom_rect->property_y2() = y2;
4648 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4650 drag_info.item = item;
4651 drag_info.motion_callback = &Editor::drag_rubberband_select;
4652 drag_info.finished_callback = &Editor::end_rubberband_select;
4654 start_grab (event, cross_hair_cursor);
4656 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4660 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4667 /* use a bigger drag threshold than the default */
4669 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4673 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4674 if (drag_info.first_move) {
4675 snap_to (drag_info.grab_frame);
4677 snap_to (drag_info.current_pointer_frame);
4680 /* base start and end on initial click position */
4682 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4683 start = drag_info.current_pointer_frame;
4684 end = drag_info.grab_frame;
4686 end = drag_info.current_pointer_frame;
4687 start = drag_info.grab_frame;
4690 if (drag_info.current_pointer_y < drag_info.grab_y) {
4691 y1 = drag_info.current_pointer_y;
4692 y2 = drag_info.grab_y;
4694 y2 = drag_info.current_pointer_y;
4695 y1 = drag_info.grab_y;
4699 if (start != end || y1 != y2) {
4701 double x1 = frame_to_pixel (start);
4702 double x2 = frame_to_pixel (end);
4704 rubberband_rect->property_x1() = x1;
4705 rubberband_rect->property_y1() = y1;
4706 rubberband_rect->property_x2() = x2;
4707 rubberband_rect->property_y2() = y2;
4709 rubberband_rect->show();
4710 rubberband_rect->raise_to_top();
4712 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4713 drag_info.first_move = false;
4715 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4720 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4722 if (!drag_info.first_move) {
4724 drag_rubberband_select (item, event);
4727 if (drag_info.current_pointer_y < drag_info.grab_y) {
4728 y1 = drag_info.current_pointer_y;
4729 y2 = drag_info.grab_y;
4732 y2 = drag_info.current_pointer_y;
4733 y1 = drag_info.grab_y;
4737 Selection::Operation op = Keyboard::selection_type (event->button.state);
4740 begin_reversible_command (_("rubberband selection"));
4742 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4743 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4745 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4749 commit_reversible_command ();
4753 selection->clear_tracks();
4754 selection->clear_regions();
4755 selection->clear_points ();
4756 selection->clear_lines ();
4759 rubberband_rect->hide();
4764 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4766 using namespace Gtkmm2ext;
4768 ArdourPrompter prompter (false);
4770 prompter.set_prompt (_("Name for region:"));
4771 prompter.set_initial_text (clicked_regionview->region()->name());
4772 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4773 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4774 prompter.show_all ();
4775 switch (prompter.run ()) {
4776 case Gtk::RESPONSE_ACCEPT:
4778 prompter.get_result(str);
4780 clicked_regionview->region()->set_name (str);
4788 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4790 drag_info.item = item;
4791 drag_info.motion_callback = &Editor::time_fx_motion;
4792 drag_info.finished_callback = &Editor::end_time_fx;
4796 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4800 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4802 RegionView* rv = clicked_regionview;
4804 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4805 snap_to (drag_info.current_pointer_frame);
4808 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4812 if (drag_info.current_pointer_frame > rv->region()->position()) {
4813 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4816 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4817 drag_info.first_move = false;
4819 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4823 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4825 clicked_regionview->get_time_axis_view().hide_timestretch ();
4827 if (drag_info.first_move) {
4831 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4832 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4834 begin_reversible_command (_("timestretch"));
4836 if (run_timestretch (selection->regions, percentage) == 0) {
4837 session->commit_reversible_command ();
4842 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4844 /* no brushing without a useful snap setting */
4847 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4850 switch (snap_mode) {
4852 return; /* can't work because it allows region to be placed anywhere */
4857 switch (snap_type) {
4860 case SnapToEditCursor:
4867 /* don't brush a copy over the original */
4869 if (pos == rv->region()->position()) {
4873 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4875 if (atv == 0 || !atv->is_audio_track()) {
4879 boost::shared_ptr<Playlist> playlist = atv->playlist();
4880 double speed = atv->get_diskstream()->speed();
4882 XMLNode &before = playlist->get_state();
4883 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4884 XMLNode &after = playlist->get_state();
4885 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4887 // playlist is frozen, so we have to update manually
4889 playlist->Modified(); /* EMIT SIGNAL */
4893 Editor::track_height_step_timeout ()
4896 struct timeval delta;
4898 gettimeofday (&now, 0);
4899 timersub (&now, &last_track_height_step_timestamp, &delta);
4901 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4902 current_stepping_trackview = 0;