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 ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1453 bool y_threshold_passed = (abs ((int) (drag_info.current_pointer_y - drag_info.grab_y)) > 4);
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) {
1623 cerr << "end grab with no item\n";
1627 drag_info.item->ungrab (event->button.time);
1629 if (drag_info.finished_callback) {
1630 (this->*(drag_info.finished_callback)) (item, event);
1633 did_drag = !drag_info.first_move;
1635 hide_verbose_canvas_cursor();
1638 drag_info.copy = false;
1639 drag_info.motion_callback = 0;
1640 drag_info.finished_callback = 0;
1641 drag_info.last_trackview = 0;
1642 drag_info.last_frame_position = 0;
1643 drag_info.grab_frame = 0;
1644 drag_info.last_pointer_frame = 0;
1645 drag_info.current_pointer_frame = 0;
1646 drag_info.brushing = false;
1648 if (drag_info.copied_location) {
1649 delete drag_info.copied_location;
1650 drag_info.copied_location = 0;
1657 Editor::set_edit_cursor (GdkEvent* event)
1659 nframes_t pointer_frame = event_frame (event);
1661 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1662 if (snap_type != SnapToEditCursor) {
1663 snap_to (pointer_frame);
1667 edit_cursor->set_position (pointer_frame);
1668 edit_cursor_clock.set (pointer_frame);
1672 Editor::set_playhead_cursor (GdkEvent* event)
1674 nframes_t pointer_frame = event_frame (event);
1676 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1677 snap_to (pointer_frame);
1681 session->request_locate (pointer_frame, session->transport_rolling());
1686 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1688 drag_info.item = item;
1689 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1690 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1694 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1695 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1699 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1701 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1705 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1707 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1709 nframes_t fade_length;
1711 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1712 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1718 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1722 if (pos < (arv->region()->position() + 64)) {
1723 fade_length = 64; // this should be a minimum defined somewhere
1724 } else if (pos > arv->region()->last_frame()) {
1725 fade_length = arv->region()->length();
1727 fade_length = pos - arv->region()->position();
1729 /* mapover the region selection */
1731 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1733 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1739 tmp->reset_fade_in_shape_width (fade_length);
1742 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1744 drag_info.first_move = false;
1748 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1750 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1752 nframes_t fade_length;
1754 if (drag_info.first_move) return;
1756 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1757 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1762 if (pos < (arv->region()->position() + 64)) {
1763 fade_length = 64; // this should be a minimum defined somewhere
1764 } else if (pos > arv->region()->last_frame()) {
1765 fade_length = arv->region()->length();
1767 fade_length = pos - arv->region()->position();
1770 begin_reversible_command (_("change fade in length"));
1772 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1774 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1780 AutomationList& alist = tmp->audio_region()->fade_in();
1781 XMLNode &before = alist.get_state();
1783 tmp->audio_region()->set_fade_in_length (fade_length);
1785 XMLNode &after = alist.get_state();
1786 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1789 commit_reversible_command ();
1793 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1795 drag_info.item = item;
1796 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1797 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1801 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1802 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1806 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1808 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1812 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1814 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1816 nframes_t fade_length;
1818 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1819 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1825 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1829 if (pos > (arv->region()->last_frame() - 64)) {
1830 fade_length = 64; // this should really be a minimum fade defined somewhere
1832 else if (pos < arv->region()->position()) {
1833 fade_length = arv->region()->length();
1836 fade_length = arv->region()->last_frame() - pos;
1839 /* mapover the region selection */
1841 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1843 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1849 tmp->reset_fade_out_shape_width (fade_length);
1852 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1854 drag_info.first_move = false;
1858 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1860 if (drag_info.first_move) return;
1862 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1864 nframes_t fade_length;
1866 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1867 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1873 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1877 if (pos > (arv->region()->last_frame() - 64)) {
1878 fade_length = 64; // this should really be a minimum fade defined somewhere
1880 else if (pos < arv->region()->position()) {
1881 fade_length = arv->region()->length();
1884 fade_length = arv->region()->last_frame() - pos;
1887 begin_reversible_command (_("change fade out length"));
1889 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1891 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1897 AutomationList& alist = tmp->audio_region()->fade_out();
1898 XMLNode &before = alist.get_state();
1900 tmp->audio_region()->set_fade_out_length (fade_length);
1902 XMLNode &after = alist.get_state();
1903 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1906 commit_reversible_command ();
1910 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1912 drag_info.item = item;
1913 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1914 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1918 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1919 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1923 Cursor* cursor = (Cursor *) drag_info.data;
1925 if (cursor == playhead_cursor) {
1926 _dragging_playhead = true;
1928 if (session && drag_info.was_rolling) {
1929 session->request_stop ();
1932 if (session && session->is_auditioning()) {
1933 session->cancel_audition ();
1937 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1939 show_verbose_time_cursor (cursor->current_frame, 10);
1943 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1945 Cursor* cursor = (Cursor *) drag_info.data;
1946 nframes_t adjusted_frame;
1948 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1949 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1955 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1956 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1957 snap_to (adjusted_frame);
1961 if (adjusted_frame == drag_info.last_pointer_frame) return;
1963 cursor->set_position (adjusted_frame);
1965 if (cursor == edit_cursor) {
1966 edit_cursor_clock.set (cursor->current_frame);
1968 UpdateAllTransportClocks (cursor->current_frame);
1971 show_verbose_time_cursor (cursor->current_frame, 10);
1973 drag_info.last_pointer_frame = adjusted_frame;
1974 drag_info.first_move = false;
1978 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1980 if (drag_info.first_move) return;
1982 cursor_drag_motion_callback (item, event);
1984 _dragging_playhead = false;
1986 if (item == &playhead_cursor->canvas_item) {
1988 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1990 } else if (item == &edit_cursor->canvas_item) {
1991 edit_cursor->set_position (edit_cursor->current_frame);
1992 edit_cursor_clock.set (edit_cursor->current_frame);
1997 Editor::update_marker_drag_item (Location *location)
1999 double x1 = frame_to_pixel (location->start());
2000 double x2 = frame_to_pixel (location->end());
2002 if (location->is_mark()) {
2003 marker_drag_line_points.front().set_x(x1);
2004 marker_drag_line_points.back().set_x(x1);
2005 marker_drag_line->property_points() = marker_drag_line_points;
2008 range_marker_drag_rect->property_x1() = x1;
2009 range_marker_drag_rect->property_x2() = x2;
2014 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2018 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2019 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2025 Location *location = find_location_from_marker (marker, is_start);
2027 drag_info.item = item;
2028 drag_info.data = marker;
2029 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2030 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2034 drag_info.copied_location = new Location (*location);
2035 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2037 update_marker_drag_item (location);
2039 if (location->is_mark()) {
2040 marker_drag_line->show();
2041 marker_drag_line->raise_to_top();
2044 range_marker_drag_rect->show();
2045 range_marker_drag_rect->raise_to_top();
2048 if (is_start) show_verbose_time_cursor (location->start(), 10);
2049 else show_verbose_time_cursor (location->end(), 10);
2053 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2056 Marker* marker = (Marker *) drag_info.data;
2057 Location *real_location;
2058 Location *copy_location;
2060 bool move_both = false;
2064 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2065 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2071 nframes_t next = newframe;
2073 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2074 snap_to (newframe, 0, true);
2077 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2081 /* call this to find out if its the start or end */
2083 real_location = find_location_from_marker (marker, is_start);
2085 /* use the copy that we're "dragging" around */
2087 copy_location = drag_info.copied_location;
2089 f_delta = copy_location->end() - copy_location->start();
2091 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2095 if (copy_location->is_mark()) {
2098 copy_location->set_start (newframe);
2102 if (is_start) { // start-of-range marker
2105 copy_location->set_start (newframe);
2106 copy_location->set_end (newframe + f_delta);
2107 } else if (newframe < copy_location->end()) {
2108 copy_location->set_start (newframe);
2110 snap_to (next, 1, true);
2111 copy_location->set_end (next);
2112 copy_location->set_start (newframe);
2115 } else { // end marker
2118 copy_location->set_end (newframe);
2119 copy_location->set_start (newframe - f_delta);
2120 } else if (newframe > copy_location->start()) {
2121 copy_location->set_end (newframe);
2123 } else if (newframe > 0) {
2124 snap_to (next, -1, true);
2125 copy_location->set_start (next);
2126 copy_location->set_end (newframe);
2131 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2132 drag_info.first_move = false;
2134 update_marker_drag_item (copy_location);
2136 LocationMarkers* lm = find_location_markers (real_location);
2137 lm->set_position (copy_location->start(), copy_location->end());
2139 show_verbose_time_cursor (newframe, 10);
2143 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2145 if (drag_info.first_move) {
2146 marker_drag_motion_callback (item, event);
2150 Marker* marker = (Marker *) drag_info.data;
2154 begin_reversible_command ( _("move marker") );
2155 XMLNode &before = session->locations()->get_state();
2157 Location * location = find_location_from_marker (marker, is_start);
2160 if (location->is_mark()) {
2161 location->set_start (drag_info.copied_location->start());
2163 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2167 XMLNode &after = session->locations()->get_state();
2168 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2169 commit_reversible_command ();
2171 marker_drag_line->hide();
2172 range_marker_drag_rect->hide();
2176 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2179 MeterMarker* meter_marker;
2181 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2182 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2186 meter_marker = dynamic_cast<MeterMarker*> (marker);
2188 MetricSection& section (meter_marker->meter());
2190 if (!section.movable()) {
2194 drag_info.item = item;
2195 drag_info.data = marker;
2196 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2197 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2201 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2203 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2207 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2210 MeterMarker* meter_marker;
2212 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2213 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2217 meter_marker = dynamic_cast<MeterMarker*> (marker);
2219 // create a dummy marker for visual representation of moving the copy.
2220 // The actual copying is not done before we reach the finish callback.
2222 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2223 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2224 *new MeterSection(meter_marker->meter()));
2226 drag_info.item = &new_marker->the_item();
2227 drag_info.copy = true;
2228 drag_info.data = new_marker;
2229 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2230 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2234 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2236 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2240 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2242 MeterMarker* marker = (MeterMarker *) drag_info.data;
2243 nframes_t adjusted_frame;
2245 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2246 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2252 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2253 snap_to (adjusted_frame);
2256 if (adjusted_frame == drag_info.last_pointer_frame) return;
2258 marker->set_position (adjusted_frame);
2261 drag_info.last_pointer_frame = adjusted_frame;
2262 drag_info.first_move = false;
2264 show_verbose_time_cursor (adjusted_frame, 10);
2268 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2270 if (drag_info.first_move) return;
2272 meter_marker_drag_motion_callback (drag_info.item, event);
2274 MeterMarker* marker = (MeterMarker *) drag_info.data;
2277 TempoMap& map (session->tempo_map());
2278 map.bbt_time (drag_info.last_pointer_frame, when);
2280 if (drag_info.copy == true) {
2281 begin_reversible_command (_("copy meter mark"));
2282 XMLNode &before = map.get_state();
2283 map.add_meter (marker->meter(), when);
2284 XMLNode &after = map.get_state();
2285 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2286 commit_reversible_command ();
2288 // delete the dummy marker we used for visual representation of copying.
2289 // a new visual marker will show up automatically.
2292 begin_reversible_command (_("move meter mark"));
2293 XMLNode &before = map.get_state();
2294 map.move_meter (marker->meter(), when);
2295 XMLNode &after = map.get_state();
2296 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2297 commit_reversible_command ();
2302 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2305 TempoMarker* tempo_marker;
2307 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2308 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2312 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2313 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2317 MetricSection& section (tempo_marker->tempo());
2319 if (!section.movable()) {
2323 drag_info.item = item;
2324 drag_info.data = marker;
2325 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2326 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2330 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2331 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2335 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2338 TempoMarker* tempo_marker;
2340 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2341 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2345 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2346 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2350 // create a dummy marker for visual representation of moving the copy.
2351 // The actual copying is not done before we reach the finish callback.
2353 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2354 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2355 *new TempoSection(tempo_marker->tempo()));
2357 drag_info.item = &new_marker->the_item();
2358 drag_info.copy = true;
2359 drag_info.data = new_marker;
2360 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2361 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2365 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2367 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2371 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2373 TempoMarker* marker = (TempoMarker *) drag_info.data;
2374 nframes_t adjusted_frame;
2376 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2377 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2383 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2384 snap_to (adjusted_frame);
2387 if (adjusted_frame == drag_info.last_pointer_frame) return;
2389 /* OK, we've moved far enough to make it worth actually move the thing. */
2391 marker->set_position (adjusted_frame);
2393 show_verbose_time_cursor (adjusted_frame, 10);
2395 drag_info.last_pointer_frame = adjusted_frame;
2396 drag_info.first_move = false;
2400 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2402 if (drag_info.first_move) return;
2404 tempo_marker_drag_motion_callback (drag_info.item, event);
2406 TempoMarker* marker = (TempoMarker *) drag_info.data;
2409 TempoMap& map (session->tempo_map());
2410 map.bbt_time (drag_info.last_pointer_frame, when);
2412 if (drag_info.copy == true) {
2413 begin_reversible_command (_("copy tempo mark"));
2414 XMLNode &before = map.get_state();
2415 map.add_tempo (marker->tempo(), when);
2416 XMLNode &after = map.get_state();
2417 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2418 commit_reversible_command ();
2420 // delete the dummy marker we used for visual representation of copying.
2421 // a new visual marker will show up automatically.
2424 begin_reversible_command (_("move tempo mark"));
2425 XMLNode &before = map.get_state();
2426 map.move_tempo (marker->tempo(), when);
2427 XMLNode &after = map.get_state();
2428 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2429 commit_reversible_command ();
2434 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2436 ControlPoint* control_point;
2438 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2439 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2443 // We shouldn't remove the first or last gain point
2444 if (control_point->line.is_last_point(*control_point) ||
2445 control_point->line.is_first_point(*control_point)) {
2449 control_point->line.remove_point (*control_point);
2453 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2455 ControlPoint* control_point;
2457 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2458 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2462 control_point->line.remove_point (*control_point);
2466 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2468 ControlPoint* control_point;
2470 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2471 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2475 drag_info.item = item;
2476 drag_info.data = control_point;
2477 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2478 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2480 start_grab (event, fader_cursor);
2482 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2484 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2485 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2486 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2488 show_verbose_canvas_cursor ();
2492 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2494 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2496 double cx = drag_info.current_pointer_x;
2497 double cy = drag_info.current_pointer_y;
2499 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2500 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2502 if (drag_info.x_constrained) {
2503 cx = drag_info.grab_x;
2505 if (drag_info.y_constrained) {
2506 cy = drag_info.grab_y;
2509 cp->line.parent_group().w2i (cx, cy);
2513 cy = min ((double) cp->line.height(), cy);
2515 //translate cx to frames
2516 nframes_t cx_frames = unit_to_frame (cx);
2518 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2519 snap_to (cx_frames);
2522 float fraction = 1.0 - (cy / cp->line.height());
2526 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2532 cp->line.point_drag (*cp, cx_frames , fraction, push);
2534 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2536 drag_info.first_move = false;
2540 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2542 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2544 if (drag_info.first_move) {
2548 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2549 reset_point_selection ();
2553 control_point_drag_motion_callback (item, event);
2555 cp->line.end_drag (cp);
2559 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2561 switch (mouse_mode) {
2563 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2564 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2572 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2576 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2577 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2581 start_line_grab (al, event);
2585 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2589 nframes_t frame_within_region;
2591 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2595 cx = event->button.x;
2596 cy = event->button.y;
2597 line->parent_group().w2i (cx, cy);
2598 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2600 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2601 current_line_drag_info.after)) {
2602 /* no adjacent points */
2606 drag_info.item = &line->grab_item();
2607 drag_info.data = line;
2608 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2609 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2611 start_grab (event, fader_cursor);
2613 double fraction = 1.0 - (cy / line->height());
2615 line->start_drag (0, drag_info.grab_frame, fraction);
2617 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2618 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2619 show_verbose_canvas_cursor ();
2623 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2625 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2626 double cx = drag_info.current_pointer_x;
2627 double cy = drag_info.current_pointer_y;
2629 line->parent_group().w2i (cx, cy);
2632 fraction = 1.0 - (cy / line->height());
2636 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2642 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2644 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2648 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2650 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2651 line_drag_motion_callback (item, event);
2656 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2658 if (selection->regions.empty() || clicked_regionview == 0) {
2662 drag_info.copy = false;
2663 drag_info.item = item;
2664 drag_info.data = clicked_regionview;
2665 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2666 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2671 TimeAxisView* tvp = clicked_trackview;
2672 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2674 if (tv && tv->is_audio_track()) {
2675 speed = tv->get_diskstream()->speed();
2678 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2679 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2680 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2681 // we want a move threshold
2682 drag_info.want_move_threshold = true;
2684 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2686 begin_reversible_command (_("move region(s)"));
2690 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2692 if (selection->regions.empty() || clicked_regionview == 0) {
2696 drag_info.copy = true;
2697 drag_info.item = item;
2698 drag_info.data = clicked_regionview;
2702 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2703 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2706 if (atv && atv->is_audio_track()) {
2707 speed = atv->get_diskstream()->speed();
2710 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2711 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2712 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2713 // we want a move threshold
2714 drag_info.want_move_threshold = true;
2715 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2716 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2717 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2721 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2723 if (selection->regions.empty() || clicked_regionview == 0) {
2727 drag_info.copy = false;
2728 drag_info.item = item;
2729 drag_info.data = clicked_regionview;
2730 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2731 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2736 TimeAxisView* tvp = clicked_trackview;
2737 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2739 if (tv && tv->is_audio_track()) {
2740 speed = tv->get_diskstream()->speed();
2743 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2744 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2745 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2746 // we want a move threshold
2747 drag_info.want_move_threshold = true;
2748 drag_info.brushing = true;
2750 begin_reversible_command (_("Drag region brush"));
2754 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2758 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2759 nframes_t pending_region_position = 0;
2760 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2761 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2762 bool clamp_y_axis = false;
2763 vector<int32_t> height_list(512) ;
2764 vector<int32_t>::iterator j;
2766 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2768 drag_info.want_move_threshold = false; // don't copy again
2770 /* duplicate the region(s) */
2772 vector<RegionView*> new_regionviews;
2774 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2777 AudioRegionView* arv;
2782 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
2783 /* XXX handle MIDI here */
2787 nrv = new AudioRegionView (*arv);
2788 nrv->get_canvas_group()->show ();
2790 new_regionviews.push_back (nrv);
2793 if (new_regionviews.empty()) {
2797 /* reset selection to new regionviews */
2799 selection->set (new_regionviews);
2801 /* reset drag_info data to reflect the fact that we are dragging the copies */
2803 drag_info.data = new_regionviews.front();
2805 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2808 /* Which trackview is this ? */
2810 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2811 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2813 /* The region motion is only processed if the pointer is over
2817 if (!tv || !tv->is_audio_track()) {
2818 /* To make sure we hide the verbose canvas cursor when the mouse is
2819 not held over and audiotrack.
2821 hide_verbose_canvas_cursor ();
2825 original_pointer_order = drag_info.last_trackview->order;
2827 /************************************************************
2829 ************************************************************/
2831 if (drag_info.brushing) {
2832 clamp_y_axis = true;
2837 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2839 int32_t children = 0, numtracks = 0;
2840 // XXX hard coding track limit, oh my, so very very bad
2841 bitset <1024> tracks (0x00);
2842 /* get a bitmask representing the visible tracks */
2844 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2845 TimeAxisView *tracklist_timeview;
2846 tracklist_timeview = (*i);
2847 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2848 list<TimeAxisView*> children_list;
2850 /* zeroes are audio tracks. ones are other types. */
2852 if (!atv2->hidden()) {
2854 if (visible_y_high < atv2->order) {
2855 visible_y_high = atv2->order;
2857 if (visible_y_low > atv2->order) {
2858 visible_y_low = atv2->order;
2861 if (!atv2->is_audio_track()) {
2862 tracks = tracks |= (0x01 << atv2->order);
2865 height_list[atv2->order] = (*i)->height;
2867 if ((children_list = atv2->get_child_list()).size() > 0) {
2868 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2869 tracks = tracks |= (0x01 << (atv2->order + children));
2870 height_list[atv2->order + children] = (*j)->height;
2878 /* find the actual span according to the canvas */
2880 canvas_pointer_y_span = pointer_y_span;
2881 if (drag_info.last_trackview->order >= tv->order) {
2883 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2884 if (height_list[y] == 0 ) {
2885 canvas_pointer_y_span--;
2890 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2891 if ( height_list[y] == 0 ) {
2892 canvas_pointer_y_span++;
2897 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2898 RegionView* rv2 = (*i);
2899 double ix1, ix2, iy1, iy2;
2902 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2903 rv2->get_canvas_group()->i2w (ix1, iy1);
2904 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2905 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2907 if (atv2->order != original_pointer_order) {
2908 /* this isn't the pointer track */
2910 if (canvas_pointer_y_span > 0) {
2912 /* moving up the canvas */
2913 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2915 int32_t visible_tracks = 0;
2916 while (visible_tracks < canvas_pointer_y_span ) {
2919 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2920 /* we're passing through a hidden track */
2925 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2926 clamp_y_axis = true;
2930 clamp_y_axis = true;
2933 } else if (canvas_pointer_y_span < 0) {
2935 /*moving down the canvas*/
2937 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2940 int32_t visible_tracks = 0;
2942 while (visible_tracks > canvas_pointer_y_span ) {
2945 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2949 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2950 clamp_y_axis = true;
2955 clamp_y_axis = true;
2961 /* this is the pointer's track */
2962 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2963 clamp_y_axis = true;
2964 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2965 clamp_y_axis = true;
2973 } else if (drag_info.last_trackview == tv) {
2974 clamp_y_axis = true;
2978 if (!clamp_y_axis) {
2979 drag_info.last_trackview = tv;
2982 /************************************************************
2984 ************************************************************/
2986 /* compute the amount of pointer motion in frames, and where
2987 the region would be if we moved it by that much.
2990 if (drag_info.move_threshold_passed) {
2992 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2994 nframes_t sync_frame;
2995 nframes_t sync_offset;
2998 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3000 sync_offset = rv->region()->sync_offset (sync_dir);
3001 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3003 /* we snap if the snap modifier is not enabled.
3006 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3007 snap_to (sync_frame);
3010 if (sync_frame - sync_offset <= sync_frame) {
3011 pending_region_position = sync_frame - (sync_dir*sync_offset);
3013 pending_region_position = 0;
3017 pending_region_position = 0;
3020 if (pending_region_position > max_frames - rv->region()->length()) {
3021 pending_region_position = drag_info.last_frame_position;
3024 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3026 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3028 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3029 to make it appear at the new location.
3032 if (pending_region_position > drag_info.last_frame_position) {
3033 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3035 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3038 drag_info.last_frame_position = pending_region_position;
3045 /* threshold not passed */
3050 /*************************************************************
3052 ************************************************************/
3054 if (x_delta == 0 && (pointer_y_span == 0)) {
3055 /* haven't reached next snap point, and we're not switching
3056 trackviews. nothing to do.
3062 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3064 RegionView* rv2 = (*i);
3066 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3068 double ix1, ix2, iy1, iy2;
3069 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3070 rv2->get_canvas_group()->i2w (ix1, iy1);
3079 /*************************************************************
3081 ************************************************************/
3085 if (drag_info.first_move) {
3086 if (drag_info.move_threshold_passed) {
3097 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3098 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3100 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3102 RegionView* rv = (*i);
3103 double ix1, ix2, iy1, iy2;
3104 int32_t temp_pointer_y_span = pointer_y_span;
3106 /* get item BBox, which will be relative to parent. so we have
3107 to query on a child, then convert to world coordinates using
3111 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3112 rv->get_canvas_group()->i2w (ix1, iy1);
3113 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3114 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3115 AudioTimeAxisView* temp_atv;
3117 if ((pointer_y_span != 0) && !clamp_y_axis) {
3120 for (j = height_list.begin(); j!= height_list.end(); j++) {
3121 if (x == canvas_atv->order) {
3122 /* we found the track the region is on */
3123 if (x != original_pointer_order) {
3124 /*this isn't from the same track we're dragging from */
3125 temp_pointer_y_span = canvas_pointer_y_span;
3127 while (temp_pointer_y_span > 0) {
3128 /* we're moving up canvas-wise,
3129 so we need to find the next track height
3131 if (j != height_list.begin()) {
3134 if (x != original_pointer_order) {
3135 /* we're not from the dragged track, so ignore hidden tracks. */
3137 temp_pointer_y_span++;
3141 temp_pointer_y_span--;
3143 while (temp_pointer_y_span < 0) {
3145 if (x != original_pointer_order) {
3147 temp_pointer_y_span--;
3151 if (j != height_list.end()) {
3154 temp_pointer_y_span++;
3156 /* find out where we'll be when we move and set height accordingly */
3158 tvp2 = trackview_by_y_position (iy1 + y_delta);
3159 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3160 rv->set_height (temp_atv->height);
3162 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3163 personally, i think this can confuse things, but never mind.
3166 //const GdkColor& col (temp_atv->view->get_region_color());
3167 //rv->set_color (const_cast<GdkColor&>(col));
3174 /* prevent the regionview from being moved to before
3175 the zero position on the canvas.
3180 if (-x_delta > ix1) {
3183 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3184 x_delta = max_frames - rv->region()->last_frame();
3187 if (drag_info.first_move) {
3189 /* hide any dependent views */
3191 rv->get_time_axis_view().hide_dependent_views (*rv);
3193 /* this is subtle. raising the regionview itself won't help,
3194 because raise_to_top() just puts the item on the top of
3195 its parent's stack. so, we need to put the trackview canvas_display group
3196 on the top, since its parent is the whole canvas.
3199 rv->get_canvas_group()->raise_to_top();
3200 rv->get_time_axis_view().canvas_display->raise_to_top();
3201 cursor_group->raise_to_top();
3203 rv->fake_set_opaque (true);
3206 if (drag_info.brushing) {
3207 mouse_brush_insert_region (rv, pending_region_position);
3209 rv->move (x_delta, y_delta);
3212 } /* foreach region */
3216 if (drag_info.first_move && drag_info.move_threshold_passed) {
3217 cursor_group->raise_to_top();
3218 drag_info.first_move = false;
3221 if (x_delta != 0 && !drag_info.brushing) {
3222 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3227 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3230 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3231 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3232 bool nocommit = true;
3234 RouteTimeAxisView* atv;
3235 bool regionview_y_movement;
3236 bool regionview_x_movement;
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 */
3260 /* adjust for track speed */
3263 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3264 if (atv && atv->get_diskstream()) {
3265 speed = atv->get_diskstream()->speed();
3268 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3269 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3271 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3272 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3276 if (drag_info.copy) {
3277 if (drag_info.x_constrained) {
3278 op_string = _("fixed time region copy");
3280 op_string = _("region copy");
3283 if (drag_info.x_constrained) {
3284 op_string = _("fixed time region drag");
3286 op_string = _("region drag");
3290 begin_reversible_command (op_string);
3292 if (regionview_y_movement) {
3294 /* moved to a different audio track. */
3296 vector<RegionView*> new_selection;
3298 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3300 RegionView* rv = (*i);
3302 double ix1, ix2, iy1, iy2;
3304 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3305 rv->get_canvas_group()->i2w (ix1, iy1);
3306 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3307 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3309 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3310 boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
3312 where = (nframes_t) (unit_to_frame (ix1) * speed);
3313 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3315 /* undo the previous hide_dependent_views so that xfades don't
3316 disappear on copying regions
3319 rv->get_time_axis_view().reveal_dependent_views (*rv);
3321 if (!drag_info.copy) {
3323 /* the region that used to be in the old playlist is not
3324 moved to the new one - we make a copy of it. as a result,
3325 any existing editor for the region should no longer be
3329 rv->hide_region_editor();
3330 rv->fake_set_opaque (false);
3332 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3333 from_playlist->remove_region ((rv->region()));
3334 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3337 latest_regionview = 0;
3339 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3340 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3341 to_playlist->add_region (new_region, where);
3342 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3345 if (latest_regionview) {
3346 new_selection.push_back (latest_regionview);
3349 if (drag_info.copy) {
3350 // get rid of the copy
3354 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3355 was selected in all of them, then removing it from the playlist will have removed all
3356 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3357 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3358 corresponding regionview, and the selection is now empty).
3360 this could have invalidated any and all iterators into the region selection.
3362 the heuristic we use here is: if the region selection is empty, break out of the loop
3363 here. if the region selection is not empty, then restart the loop because we know that
3364 we must have removed at least the region(view) we've just been working on as well as any
3365 that we processed on previous iterations.
3368 if (selection->regions.empty()) {
3371 i = selection->regions.by_layer().begin();
3375 selection->set (new_selection);
3379 /* motion within a single track */
3381 list<RegionView*> regions = selection->regions.by_layer();
3383 if (drag_info.copy) {
3384 selection->clear_regions();
3387 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3391 if (rv->region()->locked()) {
3396 if (regionview_x_movement) {
3397 double ownspeed = 1.0;
3398 atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3400 if (atv && atv->get_diskstream()) {
3401 ownspeed = atv->get_diskstream()->speed();
3404 /* base the new region position on the current position of the regionview.*/
3406 double ix1, ix2, iy1, iy2;
3408 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3409 rv->get_canvas_group()->i2w (ix1, iy1);
3410 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3414 where = rv->region()->position();
3417 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3419 assert (to_playlist);
3423 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3425 if (drag_info.copy) {
3427 boost::shared_ptr<Region> newregion;
3428 boost::shared_ptr<Region> ar;
3430 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3431 newregion = RegionFactory::create (ar);
3433 /* XXX MIDI HERE drobilla */
3439 latest_regionview = 0;
3440 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3441 to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
3444 if (latest_regionview) {
3445 atv->reveal_dependent_views (*latest_regionview);
3446 selection->add (latest_regionview);
3449 /* if the original region was locked, we don't care for the new one */
3451 newregion->set_locked (false);
3455 /* just change the model */
3457 rv->region()->set_position (where, (void*) this);
3463 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3465 /* get rid of the copy */
3467 if (drag_info.copy) {
3476 commit_reversible_command ();
3481 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3483 /* Either add to or set the set the region selection, unless
3484 this is an alignment click (control used)
3487 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3488 TimeAxisView* tv = &rv.get_time_axis_view();
3489 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3491 if (atv && atv->is_audio_track()) {
3492 speed = atv->get_diskstream()->speed();
3495 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3497 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3499 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3501 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3505 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3511 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3517 nframes_t frame_rate;
3524 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3525 case AudioClock::BBT:
3526 session->bbt_time (frame, bbt);
3527 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3530 case AudioClock::SMPTE:
3531 session->smpte_time (frame, smpte);
3532 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3535 case AudioClock::MinSec:
3536 /* XXX this is copied from show_verbose_duration_cursor() */
3537 frame_rate = session->frame_rate();
3538 hours = frame / (frame_rate * 3600);
3539 frame = frame % (frame_rate * 3600);
3540 mins = frame / (frame_rate * 60);
3541 frame = frame % (frame_rate * 60);
3542 secs = (float) frame / (float) frame_rate;
3543 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3547 snprintf (buf, sizeof(buf), "%u", frame);
3551 if (xpos >= 0 && ypos >=0) {
3552 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3555 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3557 show_verbose_canvas_cursor ();
3561 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3568 nframes_t distance, frame_rate;
3570 Meter meter_at_start(session->tempo_map().meter_at(start));
3576 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3577 case AudioClock::BBT:
3578 session->bbt_time (start, sbbt);
3579 session->bbt_time (end, ebbt);
3582 /* XXX this computation won't work well if the
3583 user makes a selection that spans any meter changes.
3586 ebbt.bars -= sbbt.bars;
3587 if (ebbt.beats >= sbbt.beats) {
3588 ebbt.beats -= sbbt.beats;
3591 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3593 if (ebbt.ticks >= sbbt.ticks) {
3594 ebbt.ticks -= sbbt.ticks;
3597 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3600 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3603 case AudioClock::SMPTE:
3604 session->smpte_duration (end - start, smpte);
3605 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3608 case AudioClock::MinSec:
3609 /* XXX this stuff should be elsewhere.. */
3610 distance = end - start;
3611 frame_rate = session->frame_rate();
3612 hours = distance / (frame_rate * 3600);
3613 distance = distance % (frame_rate * 3600);
3614 mins = distance / (frame_rate * 60);
3615 distance = distance % (frame_rate * 60);
3616 secs = (float) distance / (float) frame_rate;
3617 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3621 snprintf (buf, sizeof(buf), "%u", end - start);
3625 if (xpos >= 0 && ypos >=0) {
3626 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3629 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3631 show_verbose_canvas_cursor ();
3635 Editor::collect_new_region_view (RegionView* rv)
3637 latest_regionview = rv;
3641 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3643 if (clicked_regionview == 0) {
3647 /* lets try to create new Region for the selection */
3649 vector<boost::shared_ptr<AudioRegion> > new_regions;
3650 create_region_from_selection (new_regions);
3652 if (new_regions.empty()) {
3656 /* XXX fix me one day to use all new regions */
3658 boost::shared_ptr<Region> region (new_regions.front());
3660 /* add it to the current stream/playlist.
3662 tricky: the streamview for the track will add a new regionview. we will
3663 catch the signal it sends when it creates the regionview to
3664 set the regionview we want to then drag.
3667 latest_regionview = 0;
3668 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3670 /* A selection grab currently creates two undo/redo operations, one for
3671 creating the new region and another for moving it.
3674 begin_reversible_command (_("selection grab"));
3676 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3678 XMLNode *before = &(playlist->get_state());
3679 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3680 XMLNode *after = &(playlist->get_state());
3681 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3683 commit_reversible_command ();
3687 if (latest_regionview == 0) {
3688 /* something went wrong */
3692 /* we need to deselect all other regionviews, and select this one
3693 i'm ignoring undo stuff, because the region creation will take care of it */
3694 selection->set (latest_regionview);
3696 drag_info.item = latest_regionview->get_canvas_group();
3697 drag_info.data = latest_regionview;
3698 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3699 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3703 drag_info.last_trackview = clicked_trackview;
3704 drag_info.last_frame_position = latest_regionview->region()->position();
3705 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3707 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3711 Editor::cancel_selection ()
3713 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3714 (*i)->hide_selection ();
3716 begin_reversible_command (_("cancel selection"));
3717 selection->clear ();
3718 clicked_selection = 0;
3719 commit_reversible_command ();
3723 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3725 nframes_t start = 0;
3732 drag_info.item = item;
3733 drag_info.motion_callback = &Editor::drag_selection;
3734 drag_info.finished_callback = &Editor::end_selection_op;
3739 case CreateSelection:
3740 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3741 drag_info.copy = true;
3743 drag_info.copy = false;
3745 start_grab (event, selector_cursor);
3748 case SelectionStartTrim:
3749 if (clicked_trackview) {
3750 clicked_trackview->order_selection_trims (item, true);
3752 start_grab (event, trimmer_cursor);
3753 start = selection->time[clicked_selection].start;
3754 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3757 case SelectionEndTrim:
3758 if (clicked_trackview) {
3759 clicked_trackview->order_selection_trims (item, false);
3761 start_grab (event, trimmer_cursor);
3762 end = selection->time[clicked_selection].end;
3763 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3767 start = selection->time[clicked_selection].start;
3769 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3773 if (selection_op == SelectionMove) {
3774 show_verbose_time_cursor(start, 10);
3776 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3781 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3783 nframes_t start = 0;
3786 nframes_t pending_position;
3788 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3789 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3792 pending_position = 0;
3795 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3796 snap_to (pending_position);
3799 /* only alter selection if the current frame is
3800 different from the last frame position (adjusted)
3803 if (pending_position == drag_info.last_pointer_frame) return;
3805 switch (selection_op) {
3806 case CreateSelection:
3808 if (drag_info.first_move) {
3809 snap_to (drag_info.grab_frame);
3812 if (pending_position < drag_info.grab_frame) {
3813 start = pending_position;
3814 end = drag_info.grab_frame;
3816 end = pending_position;
3817 start = drag_info.grab_frame;
3820 /* first drag: Either add to the selection
3821 or create a new selection->
3824 if (drag_info.first_move) {
3826 begin_reversible_command (_("range selection"));
3828 if (drag_info.copy) {
3829 /* adding to the selection */
3830 clicked_selection = selection->add (start, end);
3831 drag_info.copy = false;
3833 /* new selection-> */
3834 clicked_selection = selection->set (clicked_trackview, start, end);
3839 case SelectionStartTrim:
3841 if (drag_info.first_move) {
3842 begin_reversible_command (_("trim selection start"));
3845 start = selection->time[clicked_selection].start;
3846 end = selection->time[clicked_selection].end;
3848 if (pending_position > end) {
3851 start = pending_position;
3855 case SelectionEndTrim:
3857 if (drag_info.first_move) {
3858 begin_reversible_command (_("trim selection end"));
3861 start = selection->time[clicked_selection].start;
3862 end = selection->time[clicked_selection].end;
3864 if (pending_position < start) {
3867 end = pending_position;
3874 if (drag_info.first_move) {
3875 begin_reversible_command (_("move selection"));
3878 start = selection->time[clicked_selection].start;
3879 end = selection->time[clicked_selection].end;
3881 length = end - start;
3883 start = pending_position;
3886 end = start + length;
3891 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3892 start_canvas_autoscroll (1);
3896 selection->replace (clicked_selection, start, end);
3899 drag_info.last_pointer_frame = pending_position;
3900 drag_info.first_move = false;
3902 if (selection_op == SelectionMove) {
3903 show_verbose_time_cursor(start, 10);
3905 show_verbose_time_cursor(pending_position, 10);
3910 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3912 if (!drag_info.first_move) {
3913 drag_selection (item, event);
3914 /* XXX this is not object-oriented programming at all. ick */
3915 if (selection->time.consolidate()) {
3916 selection->TimeChanged ();
3918 commit_reversible_command ();
3920 /* just a click, no pointer movement.*/
3922 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3924 selection->clear_time();
3929 /* XXX what happens if its a music selection? */
3930 session->set_audio_range (selection->time);
3931 stop_canvas_autoscroll ();
3935 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3938 TimeAxisView* tvp = clicked_trackview;
3939 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3941 if (tv && tv->is_audio_track()) {
3942 speed = tv->get_diskstream()->speed();
3945 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3946 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3947 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3949 //drag_info.item = clicked_regionview->get_name_highlight();
3950 drag_info.item = item;
3951 drag_info.motion_callback = &Editor::trim_motion_callback;
3952 drag_info.finished_callback = &Editor::trim_finished_callback;
3954 start_grab (event, trimmer_cursor);
3956 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3957 trim_op = ContentsTrim;
3959 /* These will get overridden for a point trim.*/
3960 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3961 /* closer to start */
3962 trim_op = StartTrim;
3963 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3971 show_verbose_time_cursor(region_start, 10);
3974 show_verbose_time_cursor(region_end, 10);
3977 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3983 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3985 RegionView* rv = clicked_regionview;
3986 nframes_t frame_delta = 0;
3987 bool left_direction;
3988 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3990 /* snap modifier works differently here..
3991 its' current state has to be passed to the
3992 various trim functions in order to work properly
3996 TimeAxisView* tvp = clicked_trackview;
3997 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3998 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4000 if (tv && tv->is_audio_track()) {
4001 speed = tv->get_diskstream()->speed();
4004 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4005 left_direction = true;
4007 left_direction = false;
4011 snap_to (drag_info.current_pointer_frame);
4014 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4018 if (drag_info.first_move) {
4024 trim_type = "Region start trim";
4027 trim_type = "Region end trim";
4030 trim_type = "Region content trim";
4034 begin_reversible_command (trim_type);
4036 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4037 (*i)->fake_set_opaque(false);
4038 (*i)->region()->freeze ();
4040 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4042 arv->temporarily_hide_envelope ();
4044 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4045 insert_result = motion_frozen_playlists.insert (pl);
4046 if (insert_result.second) {
4047 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4052 if (left_direction) {
4053 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4055 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4060 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4063 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4064 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4070 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4073 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4074 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4081 bool swap_direction = false;
4083 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4084 swap_direction = true;
4087 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4088 i != selection->regions.by_layer().end(); ++i)
4090 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4098 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4101 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4104 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4108 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4109 drag_info.first_move = false;
4113 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4115 boost::shared_ptr<Region> region (rv.region());
4117 if (region->locked()) {
4121 nframes_t new_bound;
4124 TimeAxisView* tvp = clicked_trackview;
4125 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4127 if (tv && tv->is_audio_track()) {
4128 speed = tv->get_diskstream()->speed();
4131 if (left_direction) {
4132 if (swap_direction) {
4133 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4135 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4138 if (swap_direction) {
4139 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4141 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4146 snap_to (new_bound);
4148 region->trim_start ((nframes_t) (new_bound * speed), this);
4149 rv.region_changed (StartChanged);
4153 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4155 boost::shared_ptr<Region> region (rv.region());
4157 if (region->locked()) {
4161 nframes_t new_bound;
4164 TimeAxisView* tvp = clicked_trackview;
4165 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4167 if (tv && tv->is_audio_track()) {
4168 speed = tv->get_diskstream()->speed();
4171 if (left_direction) {
4172 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4174 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4178 snap_to (new_bound, (left_direction ? 0 : 1));
4181 region->trim_front ((nframes_t) (new_bound * speed), this);
4183 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4187 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4189 boost::shared_ptr<Region> region (rv.region());
4191 if (region->locked()) {
4195 nframes_t new_bound;
4198 TimeAxisView* tvp = clicked_trackview;
4199 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4201 if (tv && tv->is_audio_track()) {
4202 speed = tv->get_diskstream()->speed();
4205 if (left_direction) {
4206 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4208 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4212 snap_to (new_bound);
4214 region->trim_end ((nframes_t) (new_bound * speed), this);
4215 rv.region_changed (LengthChanged);
4219 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4221 if (!drag_info.first_move) {
4222 trim_motion_callback (item, event);
4224 if (!clicked_regionview->get_selected()) {
4225 thaw_region_after_trim (*clicked_regionview);
4228 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4229 i != selection->regions.by_layer().end(); ++i)
4231 thaw_region_after_trim (**i);
4232 (*i)->fake_set_opaque (true);
4236 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4238 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4241 motion_frozen_playlists.clear ();
4243 commit_reversible_command();
4245 /* no mouse movement */
4251 Editor::point_trim (GdkEvent* event)
4253 RegionView* rv = clicked_regionview;
4254 nframes_t new_bound = drag_info.current_pointer_frame;
4256 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4257 snap_to (new_bound);
4260 /* Choose action dependant on which button was pressed */
4261 switch (event->button.button) {
4263 trim_op = StartTrim;
4264 begin_reversible_command (_("Start point trim"));
4266 if (rv->get_selected()) {
4268 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4269 i != selection->regions.by_layer().end(); ++i)
4271 if (!(*i)->region()->locked()) {
4272 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4273 XMLNode &before = pl->get_state();
4274 (*i)->region()->trim_front (new_bound, this);
4275 XMLNode &after = pl->get_state();
4276 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4282 if (!rv->region()->locked()) {
4283 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4284 XMLNode &before = pl->get_state();
4285 rv->region()->trim_front (new_bound, this);
4286 XMLNode &after = pl->get_state();
4287 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4291 commit_reversible_command();
4296 begin_reversible_command (_("End point trim"));
4298 if (rv->get_selected()) {
4300 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4302 if (!(*i)->region()->locked()) {
4303 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4304 XMLNode &before = pl->get_state();
4305 (*i)->region()->trim_end (new_bound, this);
4306 XMLNode &after = pl->get_state();
4307 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4313 if (!rv->region()->locked()) {
4314 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4315 XMLNode &before = pl->get_state();
4316 rv->region()->trim_end (new_bound, this);
4317 XMLNode &after = pl->get_state();
4318 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4322 commit_reversible_command();
4331 Editor::thaw_region_after_trim (RegionView& rv)
4333 boost::shared_ptr<Region> region (rv.region());
4335 if (region->locked()) {
4339 region->thaw (_("trimmed region"));
4340 XMLNode &after = region->playlist()->get_state();
4341 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4343 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4345 arv->unhide_envelope ();
4349 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4354 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4355 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4359 Location* location = find_location_from_marker (marker, is_start);
4360 location->set_hidden (true, this);
4365 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4371 drag_info.item = item;
4372 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4373 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4375 range_marker_op = op;
4377 if (!temp_location) {
4378 temp_location = new Location;
4382 case CreateRangeMarker:
4383 case CreateTransportMarker:
4385 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4386 drag_info.copy = true;
4388 drag_info.copy = false;
4390 start_grab (event, selector_cursor);
4394 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4399 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4401 nframes_t start = 0;
4403 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4405 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4406 snap_to (drag_info.current_pointer_frame);
4409 /* only alter selection if the current frame is
4410 different from the last frame position.
4413 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4415 switch (range_marker_op) {
4416 case CreateRangeMarker:
4417 case CreateTransportMarker:
4418 if (drag_info.first_move) {
4419 snap_to (drag_info.grab_frame);
4422 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4423 start = drag_info.current_pointer_frame;
4424 end = drag_info.grab_frame;
4426 end = drag_info.current_pointer_frame;
4427 start = drag_info.grab_frame;
4430 /* first drag: Either add to the selection
4431 or create a new selection.
4434 if (drag_info.first_move) {
4436 temp_location->set (start, end);
4440 update_marker_drag_item (temp_location);
4441 range_marker_drag_rect->show();
4442 range_marker_drag_rect->raise_to_top();
4448 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4449 start_canvas_autoscroll (1);
4453 temp_location->set (start, end);
4455 double x1 = frame_to_pixel (start);
4456 double x2 = frame_to_pixel (end);
4457 crect->property_x1() = x1;
4458 crect->property_x2() = x2;
4460 update_marker_drag_item (temp_location);
4463 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4464 drag_info.first_move = false;
4466 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4471 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4473 Location * newloc = 0;
4476 if (!drag_info.first_move) {
4477 drag_range_markerbar_op (item, event);
4479 switch (range_marker_op) {
4480 case CreateRangeMarker:
4482 begin_reversible_command (_("new range marker"));
4483 XMLNode &before = session->locations()->get_state();
4484 session->locations()->next_available_name(rangename,"unnamed");
4485 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4486 session->locations()->add (newloc, true);
4487 XMLNode &after = session->locations()->get_state();
4488 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4489 commit_reversible_command ();
4491 range_bar_drag_rect->hide();
4492 range_marker_drag_rect->hide();
4496 case CreateTransportMarker:
4497 // popup menu to pick loop or punch
4498 new_transport_marker_context_menu (&event->button, item);
4503 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4505 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4510 start = session->locations()->first_mark_before (drag_info.grab_frame);
4511 end = session->locations()->first_mark_after (drag_info.grab_frame);
4513 if (end == max_frames) {
4514 end = session->current_end_frame ();
4518 start = session->current_start_frame ();
4521 switch (mouse_mode) {
4523 /* find the two markers on either side and then make the selection from it */
4524 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4528 /* find the two markers on either side of the click and make the range out of it */
4529 selection->set (0, start, end);
4538 stop_canvas_autoscroll ();
4544 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4546 drag_info.item = item;
4547 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4548 drag_info.finished_callback = &Editor::end_mouse_zoom;
4550 start_grab (event, zoom_cursor);
4552 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4556 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4561 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4562 snap_to (drag_info.current_pointer_frame);
4564 if (drag_info.first_move) {
4565 snap_to (drag_info.grab_frame);
4569 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4571 /* base start and end on initial click position */
4572 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4573 start = drag_info.current_pointer_frame;
4574 end = drag_info.grab_frame;
4576 end = drag_info.current_pointer_frame;
4577 start = drag_info.grab_frame;
4582 if (drag_info.first_move) {
4584 zoom_rect->raise_to_top();
4587 reposition_zoom_rect(start, end);
4589 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4590 drag_info.first_move = false;
4592 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4597 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4599 if (!drag_info.first_move) {
4600 drag_mouse_zoom (item, event);
4602 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4603 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4605 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4608 temporal_zoom_to_frame (false, drag_info.grab_frame);
4610 temporal_zoom_step (false);
4611 center_screen (drag_info.grab_frame);
4619 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4621 double x1 = frame_to_pixel (start);
4622 double x2 = frame_to_pixel (end);
4623 double y2 = full_canvas_height - 1.0;
4625 zoom_rect->property_x1() = x1;
4626 zoom_rect->property_y1() = 1.0;
4627 zoom_rect->property_x2() = x2;
4628 zoom_rect->property_y2() = y2;
4632 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4634 drag_info.item = item;
4635 drag_info.motion_callback = &Editor::drag_rubberband_select;
4636 drag_info.finished_callback = &Editor::end_rubberband_select;
4638 start_grab (event, cross_hair_cursor);
4640 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4644 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4651 /* use a bigger drag threshold than the default */
4653 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4657 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4658 if (drag_info.first_move) {
4659 snap_to (drag_info.grab_frame);
4661 snap_to (drag_info.current_pointer_frame);
4664 /* base start and end on initial click position */
4666 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4667 start = drag_info.current_pointer_frame;
4668 end = drag_info.grab_frame;
4670 end = drag_info.current_pointer_frame;
4671 start = drag_info.grab_frame;
4674 if (drag_info.current_pointer_y < drag_info.grab_y) {
4675 y1 = drag_info.current_pointer_y;
4676 y2 = drag_info.grab_y;
4678 y2 = drag_info.current_pointer_y;
4679 y1 = drag_info.grab_y;
4683 if (start != end || y1 != y2) {
4685 double x1 = frame_to_pixel (start);
4686 double x2 = frame_to_pixel (end);
4688 rubberband_rect->property_x1() = x1;
4689 rubberband_rect->property_y1() = y1;
4690 rubberband_rect->property_x2() = x2;
4691 rubberband_rect->property_y2() = y2;
4693 rubberband_rect->show();
4694 rubberband_rect->raise_to_top();
4696 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4697 drag_info.first_move = false;
4699 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4704 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4706 if (!drag_info.first_move) {
4708 drag_rubberband_select (item, event);
4711 if (drag_info.current_pointer_y < drag_info.grab_y) {
4712 y1 = drag_info.current_pointer_y;
4713 y2 = drag_info.grab_y;
4716 y2 = drag_info.current_pointer_y;
4717 y1 = drag_info.grab_y;
4721 Selection::Operation op = Keyboard::selection_type (event->button.state);
4724 begin_reversible_command (_("rubberband selection"));
4726 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4727 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4729 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4733 commit_reversible_command ();
4737 selection->clear_tracks();
4738 selection->clear_regions();
4739 selection->clear_points ();
4740 selection->clear_lines ();
4743 rubberband_rect->hide();
4748 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4750 using namespace Gtkmm2ext;
4752 ArdourPrompter prompter (false);
4754 prompter.set_prompt (_("Name for region:"));
4755 prompter.set_initial_text (clicked_regionview->region()->name());
4756 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4757 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4758 prompter.show_all ();
4759 switch (prompter.run ()) {
4760 case Gtk::RESPONSE_ACCEPT:
4762 prompter.get_result(str);
4764 clicked_regionview->region()->set_name (str);
4772 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4774 drag_info.item = item;
4775 drag_info.motion_callback = &Editor::time_fx_motion;
4776 drag_info.finished_callback = &Editor::end_time_fx;
4780 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4784 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4786 RegionView* rv = clicked_regionview;
4788 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4789 snap_to (drag_info.current_pointer_frame);
4792 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4796 if (drag_info.current_pointer_frame > rv->region()->position()) {
4797 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4800 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4801 drag_info.first_move = false;
4803 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4807 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4809 clicked_regionview->get_time_axis_view().hide_timestretch ();
4811 if (drag_info.first_move) {
4815 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4816 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4818 begin_reversible_command (_("timestretch"));
4820 if (run_timestretch (selection->regions, percentage) == 0) {
4821 session->commit_reversible_command ();
4826 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4828 /* no brushing without a useful snap setting */
4831 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4834 switch (snap_mode) {
4836 return; /* can't work because it allows region to be placed anywhere */
4841 switch (snap_type) {
4844 case SnapToEditCursor:
4851 /* don't brush a copy over the original */
4853 if (pos == rv->region()->position()) {
4857 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4859 if (atv == 0 || !atv->is_audio_track()) {
4863 boost::shared_ptr<Playlist> playlist = atv->playlist();
4864 double speed = atv->get_diskstream()->speed();
4866 XMLNode &before = playlist->get_state();
4867 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4868 XMLNode &after = playlist->get_state();
4869 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4871 // playlist is frozen, so we have to update manually
4873 playlist->Modified(); /* EMIT SIGNAL */
4877 Editor::track_height_step_timeout ()
4880 struct timeval delta;
4882 gettimeofday (&now, 0);
4883 timersub (&now, &last_track_height_step_timestamp, &delta);
4885 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4886 current_stepping_trackview = 0;