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() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
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() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
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 (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1235 case MeterMarkerItem:
1236 case TempoMarkerItem:
1237 if (is_drawable()) {
1238 time_canvas.get_window()->set_cursor (*timebar_cursor);
1241 case FadeInHandleItem:
1242 case FadeOutHandleItem:
1243 if (mouse_mode == MouseObject) {
1244 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1246 rect->property_fill_color_rgba() = 0;
1247 rect->property_outline_pixels() = 1;
1256 /* second pass to handle entered track status in a comprehensible way.
1259 switch (item_type) {
1261 case GainAutomationLineItem:
1262 case RedirectAutomationLineItem:
1263 case PanAutomationLineItem:
1264 case GainControlPointItem:
1265 case GainAutomationControlPointItem:
1266 case PanAutomationControlPointItem:
1267 case RedirectAutomationControlPointItem:
1268 /* these do not affect the current entered track state */
1269 clear_entered_track = false;
1272 case AutomationTrackItem:
1273 /* handled above already */
1277 set_entered_track (0);
1285 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1294 switch (item_type) {
1295 case GainControlPointItem:
1296 case GainAutomationControlPointItem:
1297 case PanAutomationControlPointItem:
1298 case RedirectAutomationControlPointItem:
1299 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1300 if (cp->line.npoints() > 1) {
1301 if (!cp->selected) {
1302 cp->set_visible (false);
1306 if (is_drawable()) {
1307 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1310 hide_verbose_canvas_cursor ();
1313 case RegionViewNameHighlight:
1314 case StartSelectionTrimItem:
1315 case EndSelectionTrimItem:
1316 case EditCursorItem:
1317 case PlayheadCursorItem:
1318 /* <CMT Additions> */
1319 case ImageFrameHandleStartItem:
1320 case ImageFrameHandleEndItem:
1321 case MarkerViewHandleStartItem:
1322 case MarkerViewHandleEndItem:
1323 /* </CMT Additions> */
1324 if (is_drawable()) {
1325 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1330 case GainAutomationLineItem:
1331 case RedirectAutomationLineItem:
1332 case PanAutomationLineItem:
1333 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1335 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1337 line->property_fill_color_rgba() = al->get_line_color();
1339 if (is_drawable()) {
1340 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1344 case RegionViewName:
1345 /* see enter_handler() for notes */
1346 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1347 if (is_drawable() && mouse_mode == MouseObject) {
1348 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1353 case RangeMarkerBarItem:
1354 case TransportMarkerBarItem:
1358 if (is_drawable()) {
1359 time_canvas.get_window()->set_cursor (*timebar_cursor);
1364 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1367 loc = find_location_from_marker (marker, is_start);
1368 if (loc) location_flags_changed (loc, this);
1370 case MeterMarkerItem:
1371 case TempoMarkerItem:
1373 if (is_drawable()) {
1374 time_canvas.get_window()->set_cursor (*timebar_cursor);
1379 case FadeInHandleItem:
1380 case FadeOutHandleItem:
1381 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1383 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1385 rect->property_fill_color_rgba() = rv->get_fill_color();
1386 rect->property_outline_pixels() = 0;
1391 case AutomationTrackItem:
1392 if (is_drawable()) {
1393 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1394 clear_entered_track = true;
1395 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1407 Editor::left_automation_track ()
1409 if (clear_entered_track) {
1410 set_entered_track (0);
1411 clear_entered_track = false;
1417 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1421 /* We call this so that MOTION_NOTIFY events continue to be
1422 delivered to the canvas. We need to do this because we set
1423 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1424 the density of the events, at the expense of a round-trip
1425 to the server. Given that this will mostly occur on cases
1426 where DISPLAY = :0.0, and given the cost of what the motion
1427 event might do, its a good tradeoff.
1430 track_canvas.get_pointer (x, y);
1432 if (current_stepping_trackview) {
1433 /* don't keep the persistent stepped trackview if the mouse moves */
1434 current_stepping_trackview = 0;
1435 step_timeout.disconnect ();
1438 if (session && session->actively_recording()) {
1439 /* Sorry. no dragging stuff around while we record */
1443 drag_info.item_type = item_type;
1444 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1445 &drag_info.current_pointer_y);
1447 if (!from_autoscroll && drag_info.item) {
1448 /* item != 0 is the best test i can think of for dragging.
1450 if (!drag_info.move_threshold_passed) {
1452 bool x_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1453 bool y_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1455 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1457 // and change the initial grab loc/frame if this drag info wants us to
1459 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1460 drag_info.grab_frame = drag_info.current_pointer_frame;
1461 drag_info.grab_x = drag_info.current_pointer_x;
1462 drag_info.grab_y = drag_info.current_pointer_y;
1463 drag_info.last_pointer_frame = drag_info.grab_frame;
1464 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1469 switch (item_type) {
1470 case PlayheadCursorItem:
1471 case EditCursorItem:
1473 case GainControlPointItem:
1474 case RedirectAutomationControlPointItem:
1475 case GainAutomationControlPointItem:
1476 case PanAutomationControlPointItem:
1477 case TempoMarkerItem:
1478 case MeterMarkerItem:
1479 case RegionViewNameHighlight:
1480 case StartSelectionTrimItem:
1481 case EndSelectionTrimItem:
1484 case RedirectAutomationLineItem:
1485 case GainAutomationLineItem:
1486 case PanAutomationLineItem:
1487 case FadeInHandleItem:
1488 case FadeOutHandleItem:
1489 /* <CMT Additions> */
1490 case ImageFrameHandleStartItem:
1491 case ImageFrameHandleEndItem:
1492 case MarkerViewHandleStartItem:
1493 case MarkerViewHandleEndItem:
1494 /* </CMT Additions> */
1495 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1496 (event->motion.state & Gdk::BUTTON2_MASK))) {
1497 if (!from_autoscroll) {
1498 maybe_autoscroll (event);
1500 (this->*(drag_info.motion_callback)) (item, event);
1509 switch (mouse_mode) {
1514 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1515 (event->motion.state & GDK_BUTTON2_MASK))) {
1516 if (!from_autoscroll) {
1517 maybe_autoscroll (event);
1519 (this->*(drag_info.motion_callback)) (item, event);
1530 track_canvas_motion (event);
1531 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1539 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1541 if (drag_info.item == 0) {
1542 fatal << _("programming error: start_grab called without drag item") << endmsg;
1548 cursor = grabber_cursor;
1551 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1553 if (event->button.button == 2) {
1554 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1555 drag_info.y_constrained = true;
1556 drag_info.x_constrained = false;
1558 drag_info.y_constrained = false;
1559 drag_info.x_constrained = true;
1562 drag_info.x_constrained = false;
1563 drag_info.y_constrained = false;
1566 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1567 drag_info.last_pointer_frame = drag_info.grab_frame;
1568 drag_info.current_pointer_frame = drag_info.grab_frame;
1569 drag_info.current_pointer_x = drag_info.grab_x;
1570 drag_info.current_pointer_y = drag_info.grab_y;
1571 drag_info.cumulative_x_drag = 0;
1572 drag_info.cumulative_y_drag = 0;
1573 drag_info.first_move = true;
1574 drag_info.move_threshold_passed = false;
1575 drag_info.want_move_threshold = false;
1576 drag_info.pointer_frame_offset = 0;
1577 drag_info.brushing = false;
1578 drag_info.copied_location = 0;
1580 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1582 event->button.time);
1584 if (session && session->transport_rolling()) {
1585 drag_info.was_rolling = true;
1587 drag_info.was_rolling = false;
1590 switch (snap_type) {
1591 case SnapToRegionStart:
1592 case SnapToRegionEnd:
1593 case SnapToRegionSync:
1594 case SnapToRegionBoundary:
1595 build_region_boundary_cache ();
1603 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1605 drag_info.item->ungrab (0);
1606 drag_info.item = new_item;
1609 cursor = grabber_cursor;
1612 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1616 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1618 bool did_drag = false;
1620 stop_canvas_autoscroll ();
1622 if (drag_info.item == 0) {
1626 drag_info.item->ungrab (event->button.time);
1628 if (drag_info.finished_callback) {
1629 (this->*(drag_info.finished_callback)) (item, event);
1632 did_drag = !drag_info.first_move;
1634 hide_verbose_canvas_cursor();
1637 drag_info.copy = false;
1638 drag_info.motion_callback = 0;
1639 drag_info.finished_callback = 0;
1640 drag_info.last_trackview = 0;
1641 drag_info.last_frame_position = 0;
1642 drag_info.grab_frame = 0;
1643 drag_info.last_pointer_frame = 0;
1644 drag_info.current_pointer_frame = 0;
1645 drag_info.brushing = false;
1647 if (drag_info.copied_location) {
1648 delete drag_info.copied_location;
1649 drag_info.copied_location = 0;
1656 Editor::set_edit_cursor (GdkEvent* event)
1658 nframes_t pointer_frame = event_frame (event);
1660 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1661 if (snap_type != SnapToEditCursor) {
1662 snap_to (pointer_frame);
1666 edit_cursor->set_position (pointer_frame);
1667 edit_cursor_clock.set (pointer_frame);
1671 Editor::set_playhead_cursor (GdkEvent* event)
1673 nframes_t pointer_frame = event_frame (event);
1675 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1676 snap_to (pointer_frame);
1680 session->request_locate (pointer_frame, session->transport_rolling());
1685 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1687 drag_info.item = item;
1688 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1689 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1693 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1694 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1698 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1700 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1704 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1706 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1708 nframes_t fade_length;
1710 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1711 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1717 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1721 if (pos < (arv->region()->position() + 64)) {
1722 fade_length = 64; // this should be a minimum defined somewhere
1723 } else if (pos > arv->region()->last_frame()) {
1724 fade_length = arv->region()->length();
1726 fade_length = pos - arv->region()->position();
1728 /* mapover the region selection */
1730 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1732 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1738 tmp->reset_fade_in_shape_width (fade_length);
1741 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1743 drag_info.first_move = false;
1747 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1749 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1751 nframes_t fade_length;
1753 if (drag_info.first_move) return;
1755 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1756 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1761 if (pos < (arv->region()->position() + 64)) {
1762 fade_length = 64; // this should be a minimum defined somewhere
1763 } else if (pos > arv->region()->last_frame()) {
1764 fade_length = arv->region()->length();
1766 fade_length = pos - arv->region()->position();
1769 begin_reversible_command (_("change fade in length"));
1771 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1773 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1779 AutomationList& alist = tmp->audio_region()->fade_in();
1780 XMLNode &before = alist.get_state();
1782 tmp->audio_region()->set_fade_in_length (fade_length);
1784 XMLNode &after = alist.get_state();
1785 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1788 commit_reversible_command ();
1792 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1794 drag_info.item = item;
1795 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1796 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1800 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1801 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1805 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1807 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1811 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1813 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1815 nframes_t fade_length;
1817 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1818 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1823 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1827 if (pos > (arv->region()->last_frame() - 64)) {
1828 fade_length = 64; // this should really be a minimum fade defined somewhere
1830 else if (pos < arv->region()->position()) {
1831 fade_length = arv->region()->length();
1834 fade_length = arv->region()->last_frame() - pos;
1837 /* mapover the region selection */
1839 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1841 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1847 tmp->reset_fade_out_shape_width (fade_length);
1850 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1852 drag_info.first_move = false;
1856 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1858 if (drag_info.first_move) return;
1860 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1862 nframes_t fade_length;
1864 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1865 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1871 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1875 if (pos > (arv->region()->last_frame() - 64)) {
1876 fade_length = 64; // this should really be a minimum fade defined somewhere
1878 else if (pos < arv->region()->position()) {
1879 fade_length = arv->region()->length();
1882 fade_length = arv->region()->last_frame() - pos;
1885 begin_reversible_command (_("change fade out length"));
1887 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1889 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1895 AutomationList& alist = tmp->audio_region()->fade_out();
1896 XMLNode &before = alist.get_state();
1898 tmp->audio_region()->set_fade_out_length (fade_length);
1900 XMLNode &after = alist.get_state();
1901 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1904 commit_reversible_command ();
1908 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1910 drag_info.item = item;
1911 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1912 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1916 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1917 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1921 Cursor* cursor = (Cursor *) drag_info.data;
1923 if (cursor == playhead_cursor) {
1924 _dragging_playhead = true;
1926 if (session && drag_info.was_rolling) {
1927 session->request_stop ();
1930 if (session && session->is_auditioning()) {
1931 session->cancel_audition ();
1935 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1937 show_verbose_time_cursor (cursor->current_frame, 10);
1941 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1943 Cursor* cursor = (Cursor *) drag_info.data;
1944 nframes_t adjusted_frame;
1946 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1947 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1953 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1954 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1955 snap_to (adjusted_frame);
1959 if (adjusted_frame == drag_info.last_pointer_frame) return;
1961 cursor->set_position (adjusted_frame);
1963 if (cursor == edit_cursor) {
1964 edit_cursor_clock.set (cursor->current_frame);
1966 UpdateAllTransportClocks (cursor->current_frame);
1969 show_verbose_time_cursor (cursor->current_frame, 10);
1971 drag_info.last_pointer_frame = adjusted_frame;
1972 drag_info.first_move = false;
1976 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1978 if (drag_info.first_move) return;
1980 cursor_drag_motion_callback (item, event);
1982 _dragging_playhead = false;
1984 if (item == &playhead_cursor->canvas_item) {
1986 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1988 } else if (item == &edit_cursor->canvas_item) {
1989 edit_cursor->set_position (edit_cursor->current_frame);
1990 edit_cursor_clock.set (edit_cursor->current_frame);
1995 Editor::update_marker_drag_item (Location *location)
1997 double x1 = frame_to_pixel (location->start());
1998 double x2 = frame_to_pixel (location->end());
2000 if (location->is_mark()) {
2001 marker_drag_line_points.front().set_x(x1);
2002 marker_drag_line_points.back().set_x(x1);
2003 marker_drag_line->property_points() = marker_drag_line_points;
2006 range_marker_drag_rect->property_x1() = x1;
2007 range_marker_drag_rect->property_x2() = x2;
2012 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2016 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2017 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2023 Location *location = find_location_from_marker (marker, is_start);
2025 drag_info.item = item;
2026 drag_info.data = marker;
2027 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2028 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2032 drag_info.copied_location = new Location (*location);
2033 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2035 update_marker_drag_item (location);
2037 if (location->is_mark()) {
2038 marker_drag_line->show();
2039 marker_drag_line->raise_to_top();
2042 range_marker_drag_rect->show();
2043 range_marker_drag_rect->raise_to_top();
2046 if (is_start) show_verbose_time_cursor (location->start(), 10);
2047 else show_verbose_time_cursor (location->end(), 10);
2051 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2054 Marker* marker = (Marker *) drag_info.data;
2055 Location *real_location;
2056 Location *copy_location;
2058 bool move_both = false;
2062 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2063 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2068 nframes_t next = newframe;
2070 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2071 snap_to (newframe, 0, true);
2074 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2078 /* call this to find out if its the start or end */
2080 real_location = find_location_from_marker (marker, is_start);
2082 /* use the copy that we're "dragging" around */
2084 copy_location = drag_info.copied_location;
2086 f_delta = copy_location->end() - copy_location->start();
2088 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2092 if (copy_location->is_mark()) {
2095 copy_location->set_start (newframe);
2099 if (is_start) { // start-of-range marker
2102 copy_location->set_start (newframe);
2103 copy_location->set_end (newframe + f_delta);
2104 } else if (newframe < copy_location->end()) {
2105 copy_location->set_start (newframe);
2107 snap_to (next, 1, true);
2108 copy_location->set_end (next);
2109 copy_location->set_start (newframe);
2112 } else { // end marker
2115 copy_location->set_end (newframe);
2116 copy_location->set_start (newframe - f_delta);
2117 } else if (newframe > copy_location->start()) {
2118 copy_location->set_end (newframe);
2120 } else if (newframe > 0) {
2121 snap_to (next, -1, true);
2122 copy_location->set_start (next);
2123 copy_location->set_end (newframe);
2128 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2129 drag_info.first_move = false;
2131 update_marker_drag_item (copy_location);
2133 LocationMarkers* lm = find_location_markers (real_location);
2134 lm->set_position (copy_location->start(), copy_location->end());
2136 show_verbose_time_cursor (newframe, 10);
2140 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2142 if (drag_info.first_move) {
2143 marker_drag_motion_callback (item, event);
2147 Marker* marker = (Marker *) drag_info.data;
2151 begin_reversible_command ( _("move marker") );
2152 XMLNode &before = session->locations()->get_state();
2154 Location * location = find_location_from_marker (marker, is_start);
2157 if (location->is_mark()) {
2158 location->set_start (drag_info.copied_location->start());
2160 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2164 XMLNode &after = session->locations()->get_state();
2165 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2166 commit_reversible_command ();
2168 marker_drag_line->hide();
2169 range_marker_drag_rect->hide();
2173 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2176 MeterMarker* meter_marker;
2178 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2179 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2183 meter_marker = dynamic_cast<MeterMarker*> (marker);
2185 MetricSection& section (meter_marker->meter());
2187 if (!section.movable()) {
2191 drag_info.item = item;
2192 drag_info.data = marker;
2193 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2194 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2198 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2200 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2204 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2207 MeterMarker* meter_marker;
2209 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2210 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2214 meter_marker = dynamic_cast<MeterMarker*> (marker);
2216 // create a dummy marker for visual representation of moving the copy.
2217 // The actual copying is not done before we reach the finish callback.
2219 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2220 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2221 *new MeterSection(meter_marker->meter()));
2223 drag_info.item = &new_marker->the_item();
2224 drag_info.copy = true;
2225 drag_info.data = new_marker;
2226 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2227 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2231 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2233 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2237 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2239 MeterMarker* marker = (MeterMarker *) drag_info.data;
2240 nframes_t adjusted_frame;
2242 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2243 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2249 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2250 snap_to (adjusted_frame);
2253 if (adjusted_frame == drag_info.last_pointer_frame) return;
2255 marker->set_position (adjusted_frame);
2258 drag_info.last_pointer_frame = adjusted_frame;
2259 drag_info.first_move = false;
2261 show_verbose_time_cursor (adjusted_frame, 10);
2265 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2267 if (drag_info.first_move) return;
2269 meter_marker_drag_motion_callback (drag_info.item, event);
2271 MeterMarker* marker = (MeterMarker *) drag_info.data;
2274 TempoMap& map (session->tempo_map());
2275 map.bbt_time (drag_info.last_pointer_frame, when);
2277 if (drag_info.copy == true) {
2278 begin_reversible_command (_("copy meter mark"));
2279 XMLNode &before = map.get_state();
2280 map.add_meter (marker->meter(), when);
2281 XMLNode &after = map.get_state();
2282 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2283 commit_reversible_command ();
2285 // delete the dummy marker we used for visual representation of copying.
2286 // a new visual marker will show up automatically.
2289 begin_reversible_command (_("move meter mark"));
2290 XMLNode &before = map.get_state();
2291 map.move_meter (marker->meter(), when);
2292 XMLNode &after = map.get_state();
2293 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2294 commit_reversible_command ();
2299 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2302 TempoMarker* tempo_marker;
2304 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2305 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2309 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2310 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2314 MetricSection& section (tempo_marker->tempo());
2316 if (!section.movable()) {
2320 drag_info.item = item;
2321 drag_info.data = marker;
2322 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2323 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2327 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2328 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2332 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2335 TempoMarker* tempo_marker;
2337 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2338 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2342 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2343 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2347 // create a dummy marker for visual representation of moving the copy.
2348 // The actual copying is not done before we reach the finish callback.
2350 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2351 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2352 *new TempoSection(tempo_marker->tempo()));
2354 drag_info.item = &new_marker->the_item();
2355 drag_info.copy = true;
2356 drag_info.data = new_marker;
2357 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2358 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2362 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2364 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2368 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2370 TempoMarker* marker = (TempoMarker *) drag_info.data;
2371 nframes_t adjusted_frame;
2373 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2374 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2380 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2381 snap_to (adjusted_frame);
2384 if (adjusted_frame == drag_info.last_pointer_frame) return;
2386 /* OK, we've moved far enough to make it worth actually move the thing. */
2388 marker->set_position (adjusted_frame);
2390 show_verbose_time_cursor (adjusted_frame, 10);
2392 drag_info.last_pointer_frame = adjusted_frame;
2393 drag_info.first_move = false;
2397 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2399 if (drag_info.first_move) return;
2401 tempo_marker_drag_motion_callback (drag_info.item, event);
2403 TempoMarker* marker = (TempoMarker *) drag_info.data;
2406 TempoMap& map (session->tempo_map());
2407 map.bbt_time (drag_info.last_pointer_frame, when);
2409 if (drag_info.copy == true) {
2410 begin_reversible_command (_("copy tempo mark"));
2411 XMLNode &before = map.get_state();
2412 map.add_tempo (marker->tempo(), when);
2413 XMLNode &after = map.get_state();
2414 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2415 commit_reversible_command ();
2417 // delete the dummy marker we used for visual representation of copying.
2418 // a new visual marker will show up automatically.
2421 begin_reversible_command (_("move tempo mark"));
2422 XMLNode &before = map.get_state();
2423 map.move_tempo (marker->tempo(), when);
2424 XMLNode &after = map.get_state();
2425 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2426 commit_reversible_command ();
2431 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2433 ControlPoint* control_point;
2435 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2436 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2440 // We shouldn't remove the first or last gain point
2441 if (control_point->line.is_last_point(*control_point) ||
2442 control_point->line.is_first_point(*control_point)) {
2446 control_point->line.remove_point (*control_point);
2450 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2452 ControlPoint* control_point;
2454 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2455 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2459 control_point->line.remove_point (*control_point);
2463 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2465 ControlPoint* control_point;
2467 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2468 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2472 drag_info.item = item;
2473 drag_info.data = control_point;
2474 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2475 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2477 start_grab (event, fader_cursor);
2479 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2481 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2482 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2483 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2485 show_verbose_canvas_cursor ();
2489 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2491 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2493 double cx = drag_info.current_pointer_x;
2494 double cy = drag_info.current_pointer_y;
2496 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2497 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2499 if (drag_info.x_constrained) {
2500 cx = drag_info.grab_x;
2502 if (drag_info.y_constrained) {
2503 cy = drag_info.grab_y;
2506 cp->line.parent_group().w2i (cx, cy);
2510 cy = min ((double) cp->line.height(), cy);
2512 //translate cx to frames
2513 nframes_t cx_frames = unit_to_frame (cx);
2515 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2516 snap_to (cx_frames);
2519 float fraction = 1.0 - (cy / cp->line.height());
2523 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2529 cp->line.point_drag (*cp, cx_frames , fraction, push);
2531 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2533 drag_info.first_move = false;
2537 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2539 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2541 if (drag_info.first_move) {
2545 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2546 reset_point_selection ();
2550 control_point_drag_motion_callback (item, event);
2552 cp->line.end_drag (cp);
2556 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2558 switch (mouse_mode) {
2560 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2561 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2569 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2573 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2574 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2578 start_line_grab (al, event);
2582 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2586 nframes_t frame_within_region;
2588 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2592 cx = event->button.x;
2593 cy = event->button.y;
2594 line->parent_group().w2i (cx, cy);
2595 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2597 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2598 current_line_drag_info.after)) {
2599 /* no adjacent points */
2603 drag_info.item = &line->grab_item();
2604 drag_info.data = line;
2605 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2606 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2608 start_grab (event, fader_cursor);
2610 double fraction = 1.0 - (cy / line->height());
2612 line->start_drag (0, drag_info.grab_frame, fraction);
2614 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2615 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2616 show_verbose_canvas_cursor ();
2620 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2622 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2623 double cx = drag_info.current_pointer_x;
2624 double cy = drag_info.current_pointer_y;
2626 line->parent_group().w2i (cx, cy);
2629 fraction = 1.0 - (cy / line->height());
2633 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2639 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2641 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2645 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2647 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2648 line_drag_motion_callback (item, event);
2653 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2655 if (selection->regions.empty() || clicked_regionview == 0) {
2659 drag_info.copy = false;
2660 drag_info.item = item;
2661 drag_info.data = clicked_regionview;
2662 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2663 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2668 TimeAxisView* tvp = clicked_trackview;
2669 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2671 if (tv && tv->is_audio_track()) {
2672 speed = tv->get_diskstream()->speed();
2675 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2676 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2677 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2678 // we want a move threshold
2679 drag_info.want_move_threshold = true;
2681 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2683 begin_reversible_command (_("move region(s)"));
2687 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2689 if (selection->regions.empty() || clicked_regionview == 0) {
2693 drag_info.copy = true;
2694 drag_info.item = item;
2695 drag_info.data = clicked_regionview;
2699 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2700 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2703 if (atv && atv->is_audio_track()) {
2704 speed = atv->get_diskstream()->speed();
2707 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2708 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2709 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2710 // we want a move threshold
2711 drag_info.want_move_threshold = true;
2712 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2713 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2714 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2718 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2720 if (selection->regions.empty() || clicked_regionview == 0) {
2724 drag_info.copy = false;
2725 drag_info.item = item;
2726 drag_info.data = clicked_regionview;
2727 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2728 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2733 TimeAxisView* tvp = clicked_trackview;
2734 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2736 if (tv && tv->is_audio_track()) {
2737 speed = tv->get_diskstream()->speed();
2740 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2741 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2742 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2743 // we want a move threshold
2744 drag_info.want_move_threshold = true;
2745 drag_info.brushing = true;
2747 begin_reversible_command (_("Drag region brush"));
2751 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2755 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2756 nframes_t pending_region_position = 0;
2757 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2758 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2759 bool clamp_y_axis = false;
2760 vector<int32_t> height_list(512) ;
2761 vector<int32_t>::iterator j;
2763 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2765 drag_info.want_move_threshold = false; // don't copy again
2767 /* duplicate the region(s) */
2769 vector<RegionView*> new_regionviews;
2771 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2774 AudioRegionView* arv;
2779 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
2780 /* XXX handle MIDI here */
2784 nrv = new AudioRegionView (*arv);
2785 nrv->get_canvas_group()->show ();
2787 new_regionviews.push_back (nrv);
2790 if (new_regionviews.empty()) {
2794 /* reset selection to new regionviews */
2796 selection->set (new_regionviews);
2798 /* reset drag_info data to reflect the fact that we are dragging the copies */
2800 drag_info.data = new_regionviews.front();
2802 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2805 /* Which trackview is this ? */
2807 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2808 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2810 /* The region motion is only processed if the pointer is over
2814 if (!tv || !tv->is_audio_track()) {
2815 /* To make sure we hide the verbose canvas cursor when the mouse is
2816 not held over and audiotrack.
2818 hide_verbose_canvas_cursor ();
2822 original_pointer_order = drag_info.last_trackview->order;
2824 /************************************************************
2826 ************************************************************/
2828 if (drag_info.brushing) {
2829 clamp_y_axis = true;
2834 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2836 int32_t children = 0, numtracks = 0;
2837 // XXX hard coding track limit, oh my, so very very bad
2838 bitset <1024> tracks (0x00);
2839 /* get a bitmask representing the visible tracks */
2841 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2842 TimeAxisView *tracklist_timeview;
2843 tracklist_timeview = (*i);
2844 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2845 list<TimeAxisView*> children_list;
2847 /* zeroes are audio tracks. ones are other types. */
2849 if (!atv2->hidden()) {
2851 if (visible_y_high < atv2->order) {
2852 visible_y_high = atv2->order;
2854 if (visible_y_low > atv2->order) {
2855 visible_y_low = atv2->order;
2858 if (!atv2->is_audio_track()) {
2859 tracks = tracks |= (0x01 << atv2->order);
2862 height_list[atv2->order] = (*i)->height;
2864 if ((children_list = atv2->get_child_list()).size() > 0) {
2865 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2866 tracks = tracks |= (0x01 << (atv2->order + children));
2867 height_list[atv2->order + children] = (*j)->height;
2875 /* find the actual span according to the canvas */
2877 canvas_pointer_y_span = pointer_y_span;
2878 if (drag_info.last_trackview->order >= tv->order) {
2880 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2881 if (height_list[y] == 0 ) {
2882 canvas_pointer_y_span--;
2887 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2888 if ( height_list[y] == 0 ) {
2889 canvas_pointer_y_span++;
2894 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2895 RegionView* rv2 = (*i);
2896 double ix1, ix2, iy1, iy2;
2899 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2900 rv2->get_canvas_group()->i2w (ix1, iy1);
2901 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2902 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2904 if (atv2->order != original_pointer_order) {
2905 /* this isn't the pointer track */
2907 if (canvas_pointer_y_span > 0) {
2909 /* moving up the canvas */
2910 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2912 int32_t visible_tracks = 0;
2913 while (visible_tracks < canvas_pointer_y_span ) {
2916 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2917 /* we're passing through a hidden track */
2922 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2923 clamp_y_axis = true;
2927 clamp_y_axis = true;
2930 } else if (canvas_pointer_y_span < 0) {
2932 /*moving down the canvas*/
2934 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2937 int32_t visible_tracks = 0;
2939 while (visible_tracks > canvas_pointer_y_span ) {
2942 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2946 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2947 clamp_y_axis = true;
2952 clamp_y_axis = true;
2958 /* this is the pointer's track */
2959 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2960 clamp_y_axis = true;
2961 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2962 clamp_y_axis = true;
2970 } else if (drag_info.last_trackview == tv) {
2971 clamp_y_axis = true;
2975 if (!clamp_y_axis) {
2976 drag_info.last_trackview = tv;
2979 /************************************************************
2981 ************************************************************/
2983 /* compute the amount of pointer motion in frames, and where
2984 the region would be if we moved it by that much.
2987 if (drag_info.move_threshold_passed) {
2989 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2991 nframes_t sync_frame;
2992 nframes_t sync_offset;
2995 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2997 sync_offset = rv->region()->sync_offset (sync_dir);
2998 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3000 /* we snap if the snap modifier is not enabled.
3003 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3004 snap_to (sync_frame);
3007 if (sync_frame - sync_offset <= sync_frame) {
3008 pending_region_position = sync_frame - (sync_dir*sync_offset);
3010 pending_region_position = 0;
3014 pending_region_position = 0;
3017 if (pending_region_position > max_frames - rv->region()->length()) {
3018 pending_region_position = drag_info.last_frame_position;
3021 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3023 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3025 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3026 to make it appear at the new location.
3029 if (pending_region_position > drag_info.last_frame_position) {
3030 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3032 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3035 drag_info.last_frame_position = pending_region_position;
3042 /* threshold not passed */
3047 /*************************************************************
3049 ************************************************************/
3051 if (x_delta == 0 && (pointer_y_span == 0)) {
3052 /* haven't reached next snap point, and we're not switching
3053 trackviews. nothing to do.
3060 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3062 RegionView* rv2 = (*i);
3064 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3066 double ix1, ix2, iy1, iy2;
3067 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3068 rv2->get_canvas_group()->i2w (ix1, iy1);
3077 /*************************************************************
3079 ************************************************************/
3083 if (drag_info.first_move) {
3084 if (drag_info.move_threshold_passed) {
3095 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3096 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3098 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3100 RegionView* rv = (*i);
3101 double ix1, ix2, iy1, iy2;
3102 int32_t temp_pointer_y_span = pointer_y_span;
3104 /* get item BBox, which will be relative to parent. so we have
3105 to query on a child, then convert to world coordinates using
3109 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3110 rv->get_canvas_group()->i2w (ix1, iy1);
3111 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3112 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3113 AudioTimeAxisView* temp_atv;
3115 if ((pointer_y_span != 0) && !clamp_y_axis) {
3118 for (j = height_list.begin(); j!= height_list.end(); j++) {
3119 if (x == canvas_atv->order) {
3120 /* we found the track the region is on */
3121 if (x != original_pointer_order) {
3122 /*this isn't from the same track we're dragging from */
3123 temp_pointer_y_span = canvas_pointer_y_span;
3125 while (temp_pointer_y_span > 0) {
3126 /* we're moving up canvas-wise,
3127 so we need to find the next track height
3129 if (j != height_list.begin()) {
3132 if (x != original_pointer_order) {
3133 /* we're not from the dragged track, so ignore hidden tracks. */
3135 temp_pointer_y_span++;
3139 temp_pointer_y_span--;
3141 while (temp_pointer_y_span < 0) {
3143 if (x != original_pointer_order) {
3145 temp_pointer_y_span--;
3149 if (j != height_list.end()) {
3152 temp_pointer_y_span++;
3154 /* find out where we'll be when we move and set height accordingly */
3156 tvp2 = trackview_by_y_position (iy1 + y_delta);
3157 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3158 rv->set_height (temp_atv->height);
3160 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3161 personally, i think this can confuse things, but never mind.
3164 //const GdkColor& col (temp_atv->view->get_region_color());
3165 //rv->set_color (const_cast<GdkColor&>(col));
3172 /* prevent the regionview from being moved to before
3173 the zero position on the canvas.
3178 if (-x_delta > ix1) {
3181 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3182 x_delta = max_frames - rv->region()->last_frame();
3186 if (drag_info.first_move) {
3188 /* hide any dependent views */
3190 rv->get_time_axis_view().hide_dependent_views (*rv);
3192 /* this is subtle. raising the regionview itself won't help,
3193 because raise_to_top() just puts the item on the top of
3194 its parent's stack. so, we need to put the trackview canvas_display group
3195 on the top, since its parent is the whole canvas.
3198 rv->get_canvas_group()->raise_to_top();
3199 rv->get_time_axis_view().canvas_display->raise_to_top();
3200 cursor_group->raise_to_top();
3201 rv->fake_set_opaque (true);
3204 if (drag_info.brushing) {
3205 mouse_brush_insert_region (rv, pending_region_position);
3207 rv->move (x_delta, y_delta);
3210 } /* foreach region */
3214 if (drag_info.first_move && drag_info.move_threshold_passed) {
3215 cursor_group->raise_to_top();
3216 drag_info.first_move = false;
3219 if (x_delta != 0 && !drag_info.brushing) {
3220 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3225 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3228 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3229 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3230 bool nocommit = true;
3232 RouteTimeAxisView* atv;
3233 bool regionview_y_movement;
3234 bool regionview_x_movement;
3235 vector<RegionView*> copies;
3237 /* first_move is set to false if the regionview has been moved in the
3241 if (drag_info.first_move) {
3248 /* The regionview has been moved at some stage during the grab so we need
3249 to account for any mouse movement between this event and the last one.
3252 region_drag_motion_callback (item, event);
3254 if (drag_info.brushing) {
3255 /* all changes were made during motion event handlers */
3257 if (drag_info.copy) {
3258 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3259 copies.push_back (*i);
3266 /* adjust for track speed */
3269 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3270 if (atv && atv->get_diskstream()) {
3271 speed = atv->get_diskstream()->speed();
3274 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3275 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3277 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3278 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3282 if (drag_info.copy) {
3283 if (drag_info.x_constrained) {
3284 op_string = _("fixed time region copy");
3286 op_string = _("region copy");
3289 if (drag_info.x_constrained) {
3290 op_string = _("fixed time region drag");
3292 op_string = _("region drag");
3296 begin_reversible_command (op_string);
3298 if (regionview_y_movement) {
3300 /* moved to a different audio track. */
3302 vector<RegionView*> new_selection;
3304 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3306 RegionView* rv = (*i);
3308 double ix1, ix2, iy1, iy2;
3310 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3311 rv->get_canvas_group()->i2w (ix1, iy1);
3312 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3313 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3315 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3316 boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
3318 where = (nframes_t) (unit_to_frame (ix1) * speed);
3319 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3321 /* undo the previous hide_dependent_views so that xfades don't
3322 disappear on copying regions
3325 rv->get_time_axis_view().reveal_dependent_views (*rv);
3327 if (!drag_info.copy) {
3329 /* the region that used to be in the old playlist is not
3330 moved to the new one - we make a copy of it. as a result,
3331 any existing editor for the region should no longer be
3335 rv->hide_region_editor();
3336 rv->fake_set_opaque (false);
3338 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3339 from_playlist->remove_region ((rv->region()));
3340 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3344 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3346 copies.push_back (rv);
3349 latest_regionview = 0;
3351 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3352 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3353 to_playlist->add_region (new_region, where);
3354 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3357 if (latest_regionview) {
3358 new_selection.push_back (latest_regionview);
3361 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3362 was selected in all of them, then removing it from the playlist will have removed all
3363 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3364 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3365 corresponding regionview, and the selection is now empty).
3367 this could have invalidated any and all iterators into the region selection.
3369 the heuristic we use here is: if the region selection is empty, break out of the loop
3370 here. if the region selection is not empty, then restart the loop because we know that
3371 we must have removed at least the region(view) we've just been working on as well as any
3372 that we processed on previous iterations.
3374 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3375 we can just iterate.
3378 if (drag_info.copy) {
3381 if (selection->regions.empty()) {
3384 i = selection->regions.by_layer().begin();
3389 selection->set (new_selection);
3393 /* motion within a single track */
3395 list<RegionView*> regions = selection->regions.by_layer();
3397 if (drag_info.copy) {
3398 selection->clear_regions();
3401 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3405 if (rv->region()->locked()) {
3410 if (regionview_x_movement) {
3411 double ownspeed = 1.0;
3412 atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3414 if (atv && atv->get_diskstream()) {
3415 ownspeed = atv->get_diskstream()->speed();
3418 /* base the new region position on the current position of the regionview.*/
3420 double ix1, ix2, iy1, iy2;
3422 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3423 rv->get_canvas_group()->i2w (ix1, iy1);
3424 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3428 where = rv->region()->position();
3431 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3433 assert (to_playlist);
3437 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3439 if (drag_info.copy) {
3441 boost::shared_ptr<Region> newregion;
3442 boost::shared_ptr<Region> ar;
3444 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3445 newregion = RegionFactory::create (ar);
3447 /* XXX MIDI HERE drobilla */
3453 latest_regionview = 0;
3454 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3455 to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
3458 if (latest_regionview) {
3459 atv->reveal_dependent_views (*latest_regionview);
3460 selection->add (latest_regionview);
3463 /* if the original region was locked, we don't care for the new one */
3465 newregion->set_locked (false);
3469 /* just change the model */
3471 rv->region()->set_position (where, (void*) this);
3477 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3479 if (drag_info.copy) {
3480 copies.push_back (rv);
3488 commit_reversible_command ();
3491 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3497 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3499 /* Either add to or set the set the region selection, unless
3500 this is an alignment click (control used)
3503 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3504 TimeAxisView* tv = &rv.get_time_axis_view();
3505 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3507 if (atv && atv->is_audio_track()) {
3508 speed = atv->get_diskstream()->speed();
3511 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3513 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3515 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3517 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3521 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3527 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3533 nframes_t frame_rate;
3540 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3541 case AudioClock::BBT:
3542 session->bbt_time (frame, bbt);
3543 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3546 case AudioClock::SMPTE:
3547 session->smpte_time (frame, smpte);
3548 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3551 case AudioClock::MinSec:
3552 /* XXX this is copied from show_verbose_duration_cursor() */
3553 frame_rate = session->frame_rate();
3554 hours = frame / (frame_rate * 3600);
3555 frame = frame % (frame_rate * 3600);
3556 mins = frame / (frame_rate * 60);
3557 frame = frame % (frame_rate * 60);
3558 secs = (float) frame / (float) frame_rate;
3559 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3563 snprintf (buf, sizeof(buf), "%u", frame);
3567 if (xpos >= 0 && ypos >=0) {
3568 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3571 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3573 show_verbose_canvas_cursor ();
3577 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3584 nframes_t distance, frame_rate;
3586 Meter meter_at_start(session->tempo_map().meter_at(start));
3592 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3593 case AudioClock::BBT:
3594 session->bbt_time (start, sbbt);
3595 session->bbt_time (end, ebbt);
3598 /* XXX this computation won't work well if the
3599 user makes a selection that spans any meter changes.
3602 ebbt.bars -= sbbt.bars;
3603 if (ebbt.beats >= sbbt.beats) {
3604 ebbt.beats -= sbbt.beats;
3607 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3609 if (ebbt.ticks >= sbbt.ticks) {
3610 ebbt.ticks -= sbbt.ticks;
3613 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3616 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3619 case AudioClock::SMPTE:
3620 session->smpte_duration (end - start, smpte);
3621 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3624 case AudioClock::MinSec:
3625 /* XXX this stuff should be elsewhere.. */
3626 distance = end - start;
3627 frame_rate = session->frame_rate();
3628 hours = distance / (frame_rate * 3600);
3629 distance = distance % (frame_rate * 3600);
3630 mins = distance / (frame_rate * 60);
3631 distance = distance % (frame_rate * 60);
3632 secs = (float) distance / (float) frame_rate;
3633 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3637 snprintf (buf, sizeof(buf), "%u", end - start);
3641 if (xpos >= 0 && ypos >=0) {
3642 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3645 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3647 show_verbose_canvas_cursor ();
3651 Editor::collect_new_region_view (RegionView* rv)
3653 latest_regionview = rv;
3657 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3659 if (clicked_regionview == 0) {
3663 /* lets try to create new Region for the selection */
3665 vector<boost::shared_ptr<AudioRegion> > new_regions;
3666 create_region_from_selection (new_regions);
3668 if (new_regions.empty()) {
3672 /* XXX fix me one day to use all new regions */
3674 boost::shared_ptr<Region> region (new_regions.front());
3676 /* add it to the current stream/playlist.
3678 tricky: the streamview for the track will add a new regionview. we will
3679 catch the signal it sends when it creates the regionview to
3680 set the regionview we want to then drag.
3683 latest_regionview = 0;
3684 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3686 /* A selection grab currently creates two undo/redo operations, one for
3687 creating the new region and another for moving it.
3690 begin_reversible_command (_("selection grab"));
3692 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3694 XMLNode *before = &(playlist->get_state());
3695 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3696 XMLNode *after = &(playlist->get_state());
3697 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3699 commit_reversible_command ();
3703 if (latest_regionview == 0) {
3704 /* something went wrong */
3708 /* we need to deselect all other regionviews, and select this one
3709 i'm ignoring undo stuff, because the region creation will take care of it */
3710 selection->set (latest_regionview);
3712 drag_info.item = latest_regionview->get_canvas_group();
3713 drag_info.data = latest_regionview;
3714 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3715 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3719 drag_info.last_trackview = clicked_trackview;
3720 drag_info.last_frame_position = latest_regionview->region()->position();
3721 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3723 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3727 Editor::cancel_selection ()
3729 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3730 (*i)->hide_selection ();
3732 begin_reversible_command (_("cancel selection"));
3733 selection->clear ();
3734 clicked_selection = 0;
3735 commit_reversible_command ();
3739 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3741 nframes_t start = 0;
3748 drag_info.item = item;
3749 drag_info.motion_callback = &Editor::drag_selection;
3750 drag_info.finished_callback = &Editor::end_selection_op;
3755 case CreateSelection:
3756 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3757 drag_info.copy = true;
3759 drag_info.copy = false;
3761 start_grab (event, selector_cursor);
3764 case SelectionStartTrim:
3765 if (clicked_trackview) {
3766 clicked_trackview->order_selection_trims (item, true);
3768 start_grab (event, trimmer_cursor);
3769 start = selection->time[clicked_selection].start;
3770 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3773 case SelectionEndTrim:
3774 if (clicked_trackview) {
3775 clicked_trackview->order_selection_trims (item, false);
3777 start_grab (event, trimmer_cursor);
3778 end = selection->time[clicked_selection].end;
3779 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3783 start = selection->time[clicked_selection].start;
3785 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3789 if (selection_op == SelectionMove) {
3790 show_verbose_time_cursor(start, 10);
3792 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3797 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3799 nframes_t start = 0;
3802 nframes_t pending_position;
3804 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3805 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3807 pending_position = 0;
3810 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3811 snap_to (pending_position);
3814 /* only alter selection if the current frame is
3815 different from the last frame position (adjusted)
3818 if (pending_position == drag_info.last_pointer_frame) return;
3820 switch (selection_op) {
3821 case CreateSelection:
3823 if (drag_info.first_move) {
3824 snap_to (drag_info.grab_frame);
3827 if (pending_position < drag_info.grab_frame) {
3828 start = pending_position;
3829 end = drag_info.grab_frame;
3831 end = pending_position;
3832 start = drag_info.grab_frame;
3835 /* first drag: Either add to the selection
3836 or create a new selection->
3839 if (drag_info.first_move) {
3841 begin_reversible_command (_("range selection"));
3843 if (drag_info.copy) {
3844 /* adding to the selection */
3845 clicked_selection = selection->add (start, end);
3846 drag_info.copy = false;
3848 /* new selection-> */
3849 clicked_selection = selection->set (clicked_trackview, start, end);
3854 case SelectionStartTrim:
3856 if (drag_info.first_move) {
3857 begin_reversible_command (_("trim selection start"));
3860 start = selection->time[clicked_selection].start;
3861 end = selection->time[clicked_selection].end;
3863 if (pending_position > end) {
3866 start = pending_position;
3870 case SelectionEndTrim:
3872 if (drag_info.first_move) {
3873 begin_reversible_command (_("trim selection end"));
3876 start = selection->time[clicked_selection].start;
3877 end = selection->time[clicked_selection].end;
3879 if (pending_position < start) {
3882 end = pending_position;
3889 if (drag_info.first_move) {
3890 begin_reversible_command (_("move selection"));
3893 start = selection->time[clicked_selection].start;
3894 end = selection->time[clicked_selection].end;
3896 length = end - start;
3898 start = pending_position;
3901 end = start + length;
3906 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3907 start_canvas_autoscroll (1);
3911 selection->replace (clicked_selection, start, end);
3914 drag_info.last_pointer_frame = pending_position;
3915 drag_info.first_move = false;
3917 if (selection_op == SelectionMove) {
3918 show_verbose_time_cursor(start, 10);
3920 show_verbose_time_cursor(pending_position, 10);
3925 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3927 if (!drag_info.first_move) {
3928 drag_selection (item, event);
3929 /* XXX this is not object-oriented programming at all. ick */
3930 if (selection->time.consolidate()) {
3931 selection->TimeChanged ();
3933 commit_reversible_command ();
3935 /* just a click, no pointer movement.*/
3937 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3939 selection->clear_time();
3944 /* XXX what happens if its a music selection? */
3945 session->set_audio_range (selection->time);
3946 stop_canvas_autoscroll ();
3950 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3953 TimeAxisView* tvp = clicked_trackview;
3954 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3956 if (tv && tv->is_audio_track()) {
3957 speed = tv->get_diskstream()->speed();
3960 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3961 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3962 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3964 //drag_info.item = clicked_regionview->get_name_highlight();
3965 drag_info.item = item;
3966 drag_info.motion_callback = &Editor::trim_motion_callback;
3967 drag_info.finished_callback = &Editor::trim_finished_callback;
3969 start_grab (event, trimmer_cursor);
3971 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3972 trim_op = ContentsTrim;
3974 /* These will get overridden for a point trim.*/
3975 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3976 /* closer to start */
3977 trim_op = StartTrim;
3978 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3986 show_verbose_time_cursor(region_start, 10);
3989 show_verbose_time_cursor(region_end, 10);
3992 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3998 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4000 RegionView* rv = clicked_regionview;
4001 nframes_t frame_delta = 0;
4002 bool left_direction;
4003 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4005 /* snap modifier works differently here..
4006 its' current state has to be passed to the
4007 various trim functions in order to work properly
4011 TimeAxisView* tvp = clicked_trackview;
4012 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4013 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4015 if (tv && tv->is_audio_track()) {
4016 speed = tv->get_diskstream()->speed();
4019 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4020 left_direction = true;
4022 left_direction = false;
4026 snap_to (drag_info.current_pointer_frame);
4029 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4033 if (drag_info.first_move) {
4039 trim_type = "Region start trim";
4042 trim_type = "Region end trim";
4045 trim_type = "Region content trim";
4049 begin_reversible_command (trim_type);
4051 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4052 (*i)->fake_set_opaque(false);
4053 (*i)->region()->freeze ();
4055 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4057 arv->temporarily_hide_envelope ();
4059 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4060 insert_result = motion_frozen_playlists.insert (pl);
4061 if (insert_result.second) {
4062 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4067 if (left_direction) {
4068 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4070 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4075 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4078 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4079 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4085 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4088 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4089 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4096 bool swap_direction = false;
4098 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4099 swap_direction = true;
4102 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4103 i != selection->regions.by_layer().end(); ++i)
4105 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4113 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4116 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4119 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4123 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4124 drag_info.first_move = false;
4128 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4130 boost::shared_ptr<Region> region (rv.region());
4132 if (region->locked()) {
4136 nframes_t new_bound;
4139 TimeAxisView* tvp = clicked_trackview;
4140 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4142 if (tv && tv->is_audio_track()) {
4143 speed = tv->get_diskstream()->speed();
4146 if (left_direction) {
4147 if (swap_direction) {
4148 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4150 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4153 if (swap_direction) {
4154 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4156 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4161 snap_to (new_bound);
4163 region->trim_start ((nframes_t) (new_bound * speed), this);
4164 rv.region_changed (StartChanged);
4168 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4170 boost::shared_ptr<Region> region (rv.region());
4172 if (region->locked()) {
4176 nframes_t new_bound;
4179 TimeAxisView* tvp = clicked_trackview;
4180 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4182 if (tv && tv->is_audio_track()) {
4183 speed = tv->get_diskstream()->speed();
4186 if (left_direction) {
4187 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4189 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4193 snap_to (new_bound, (left_direction ? 0 : 1));
4196 region->trim_front ((nframes_t) (new_bound * speed), this);
4198 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4202 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4204 boost::shared_ptr<Region> region (rv.region());
4206 if (region->locked()) {
4210 nframes_t new_bound;
4213 TimeAxisView* tvp = clicked_trackview;
4214 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4216 if (tv && tv->is_audio_track()) {
4217 speed = tv->get_diskstream()->speed();
4220 if (left_direction) {
4221 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4223 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4227 snap_to (new_bound);
4229 region->trim_end ((nframes_t) (new_bound * speed), this);
4230 rv.region_changed (LengthChanged);
4234 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4236 if (!drag_info.first_move) {
4237 trim_motion_callback (item, event);
4239 if (!clicked_regionview->get_selected()) {
4240 thaw_region_after_trim (*clicked_regionview);
4243 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4244 i != selection->regions.by_layer().end(); ++i)
4246 thaw_region_after_trim (**i);
4247 (*i)->fake_set_opaque (true);
4251 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4253 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4256 motion_frozen_playlists.clear ();
4258 commit_reversible_command();
4260 /* no mouse movement */
4266 Editor::point_trim (GdkEvent* event)
4268 RegionView* rv = clicked_regionview;
4269 nframes_t new_bound = drag_info.current_pointer_frame;
4271 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4272 snap_to (new_bound);
4275 /* Choose action dependant on which button was pressed */
4276 switch (event->button.button) {
4278 trim_op = StartTrim;
4279 begin_reversible_command (_("Start point trim"));
4281 if (rv->get_selected()) {
4283 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4284 i != selection->regions.by_layer().end(); ++i)
4286 if (!(*i)->region()->locked()) {
4287 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4288 XMLNode &before = pl->get_state();
4289 (*i)->region()->trim_front (new_bound, this);
4290 XMLNode &after = pl->get_state();
4291 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4297 if (!rv->region()->locked()) {
4298 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4299 XMLNode &before = pl->get_state();
4300 rv->region()->trim_front (new_bound, this);
4301 XMLNode &after = pl->get_state();
4302 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4306 commit_reversible_command();
4311 begin_reversible_command (_("End point trim"));
4313 if (rv->get_selected()) {
4315 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4317 if (!(*i)->region()->locked()) {
4318 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4319 XMLNode &before = pl->get_state();
4320 (*i)->region()->trim_end (new_bound, this);
4321 XMLNode &after = pl->get_state();
4322 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4328 if (!rv->region()->locked()) {
4329 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4330 XMLNode &before = pl->get_state();
4331 rv->region()->trim_end (new_bound, this);
4332 XMLNode &after = pl->get_state();
4333 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4337 commit_reversible_command();
4346 Editor::thaw_region_after_trim (RegionView& rv)
4348 boost::shared_ptr<Region> region (rv.region());
4350 if (region->locked()) {
4354 region->thaw (_("trimmed region"));
4355 XMLNode &after = region->playlist()->get_state();
4356 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4358 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4360 arv->unhide_envelope ();
4364 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4369 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4370 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4374 Location* location = find_location_from_marker (marker, is_start);
4375 location->set_hidden (true, this);
4380 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4386 drag_info.item = item;
4387 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4388 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4390 range_marker_op = op;
4392 if (!temp_location) {
4393 temp_location = new Location;
4397 case CreateRangeMarker:
4398 case CreateTransportMarker:
4400 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4401 drag_info.copy = true;
4403 drag_info.copy = false;
4405 start_grab (event, selector_cursor);
4409 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4414 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4416 nframes_t start = 0;
4418 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4420 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4421 snap_to (drag_info.current_pointer_frame);
4424 /* only alter selection if the current frame is
4425 different from the last frame position.
4428 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4430 switch (range_marker_op) {
4431 case CreateRangeMarker:
4432 case CreateTransportMarker:
4433 if (drag_info.first_move) {
4434 snap_to (drag_info.grab_frame);
4437 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4438 start = drag_info.current_pointer_frame;
4439 end = drag_info.grab_frame;
4441 end = drag_info.current_pointer_frame;
4442 start = drag_info.grab_frame;
4445 /* first drag: Either add to the selection
4446 or create a new selection.
4449 if (drag_info.first_move) {
4451 temp_location->set (start, end);
4455 update_marker_drag_item (temp_location);
4456 range_marker_drag_rect->show();
4457 range_marker_drag_rect->raise_to_top();
4463 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4464 start_canvas_autoscroll (1);
4468 temp_location->set (start, end);
4470 double x1 = frame_to_pixel (start);
4471 double x2 = frame_to_pixel (end);
4472 crect->property_x1() = x1;
4473 crect->property_x2() = x2;
4475 update_marker_drag_item (temp_location);
4478 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4479 drag_info.first_move = false;
4481 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4486 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4488 Location * newloc = 0;
4491 if (!drag_info.first_move) {
4492 drag_range_markerbar_op (item, event);
4494 switch (range_marker_op) {
4495 case CreateRangeMarker:
4497 begin_reversible_command (_("new range marker"));
4498 XMLNode &before = session->locations()->get_state();
4499 session->locations()->next_available_name(rangename,"unnamed");
4500 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4501 session->locations()->add (newloc, true);
4502 XMLNode &after = session->locations()->get_state();
4503 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4504 commit_reversible_command ();
4506 range_bar_drag_rect->hide();
4507 range_marker_drag_rect->hide();
4511 case CreateTransportMarker:
4512 // popup menu to pick loop or punch
4513 new_transport_marker_context_menu (&event->button, item);
4518 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4520 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4525 start = session->locations()->first_mark_before (drag_info.grab_frame);
4526 end = session->locations()->first_mark_after (drag_info.grab_frame);
4528 if (end == max_frames) {
4529 end = session->current_end_frame ();
4533 start = session->current_start_frame ();
4536 switch (mouse_mode) {
4538 /* find the two markers on either side and then make the selection from it */
4539 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4543 /* find the two markers on either side of the click and make the range out of it */
4544 selection->set (0, start, end);
4553 stop_canvas_autoscroll ();
4559 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4561 drag_info.item = item;
4562 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4563 drag_info.finished_callback = &Editor::end_mouse_zoom;
4565 start_grab (event, zoom_cursor);
4567 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4571 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4576 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4577 snap_to (drag_info.current_pointer_frame);
4579 if (drag_info.first_move) {
4580 snap_to (drag_info.grab_frame);
4584 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4586 /* base start and end on initial click position */
4587 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4588 start = drag_info.current_pointer_frame;
4589 end = drag_info.grab_frame;
4591 end = drag_info.current_pointer_frame;
4592 start = drag_info.grab_frame;
4597 if (drag_info.first_move) {
4599 zoom_rect->raise_to_top();
4602 reposition_zoom_rect(start, end);
4604 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4605 drag_info.first_move = false;
4607 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4612 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4614 if (!drag_info.first_move) {
4615 drag_mouse_zoom (item, event);
4617 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4618 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4620 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4623 temporal_zoom_to_frame (false, drag_info.grab_frame);
4625 temporal_zoom_step (false);
4626 center_screen (drag_info.grab_frame);
4634 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4636 double x1 = frame_to_pixel (start);
4637 double x2 = frame_to_pixel (end);
4638 double y2 = full_canvas_height - 1.0;
4640 zoom_rect->property_x1() = x1;
4641 zoom_rect->property_y1() = 1.0;
4642 zoom_rect->property_x2() = x2;
4643 zoom_rect->property_y2() = y2;
4647 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4649 drag_info.item = item;
4650 drag_info.motion_callback = &Editor::drag_rubberband_select;
4651 drag_info.finished_callback = &Editor::end_rubberband_select;
4653 start_grab (event, cross_hair_cursor);
4655 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4659 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4666 /* use a bigger drag threshold than the default */
4668 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4672 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4673 if (drag_info.first_move) {
4674 snap_to (drag_info.grab_frame);
4676 snap_to (drag_info.current_pointer_frame);
4679 /* base start and end on initial click position */
4681 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4682 start = drag_info.current_pointer_frame;
4683 end = drag_info.grab_frame;
4685 end = drag_info.current_pointer_frame;
4686 start = drag_info.grab_frame;
4689 if (drag_info.current_pointer_y < drag_info.grab_y) {
4690 y1 = drag_info.current_pointer_y;
4691 y2 = drag_info.grab_y;
4693 y2 = drag_info.current_pointer_y;
4694 y1 = drag_info.grab_y;
4698 if (start != end || y1 != y2) {
4700 double x1 = frame_to_pixel (start);
4701 double x2 = frame_to_pixel (end);
4703 rubberband_rect->property_x1() = x1;
4704 rubberband_rect->property_y1() = y1;
4705 rubberband_rect->property_x2() = x2;
4706 rubberband_rect->property_y2() = y2;
4708 rubberband_rect->show();
4709 rubberband_rect->raise_to_top();
4711 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4712 drag_info.first_move = false;
4714 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4719 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4721 if (!drag_info.first_move) {
4723 drag_rubberband_select (item, event);
4726 if (drag_info.current_pointer_y < drag_info.grab_y) {
4727 y1 = drag_info.current_pointer_y;
4728 y2 = drag_info.grab_y;
4731 y2 = drag_info.current_pointer_y;
4732 y1 = drag_info.grab_y;
4736 Selection::Operation op = Keyboard::selection_type (event->button.state);
4739 begin_reversible_command (_("rubberband selection"));
4741 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4742 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4744 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4748 commit_reversible_command ();
4752 selection->clear_tracks();
4753 selection->clear_regions();
4754 selection->clear_points ();
4755 selection->clear_lines ();
4758 rubberband_rect->hide();
4763 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4765 using namespace Gtkmm2ext;
4767 ArdourPrompter prompter (false);
4769 prompter.set_prompt (_("Name for region:"));
4770 prompter.set_initial_text (clicked_regionview->region()->name());
4771 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4772 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4773 prompter.show_all ();
4774 switch (prompter.run ()) {
4775 case Gtk::RESPONSE_ACCEPT:
4777 prompter.get_result(str);
4779 clicked_regionview->region()->set_name (str);
4787 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4789 drag_info.item = item;
4790 drag_info.motion_callback = &Editor::time_fx_motion;
4791 drag_info.finished_callback = &Editor::end_time_fx;
4795 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4799 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4801 RegionView* rv = clicked_regionview;
4803 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4804 snap_to (drag_info.current_pointer_frame);
4807 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4811 if (drag_info.current_pointer_frame > rv->region()->position()) {
4812 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4815 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4816 drag_info.first_move = false;
4818 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4822 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4824 clicked_regionview->get_time_axis_view().hide_timestretch ();
4826 if (drag_info.first_move) {
4830 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4831 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4833 begin_reversible_command (_("timestretch"));
4835 if (run_timestretch (selection->regions, percentage) == 0) {
4836 session->commit_reversible_command ();
4841 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4843 /* no brushing without a useful snap setting */
4846 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4849 switch (snap_mode) {
4851 return; /* can't work because it allows region to be placed anywhere */
4856 switch (snap_type) {
4859 case SnapToEditCursor:
4866 /* don't brush a copy over the original */
4868 if (pos == rv->region()->position()) {
4872 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4874 if (atv == 0 || !atv->is_audio_track()) {
4878 boost::shared_ptr<Playlist> playlist = atv->playlist();
4879 double speed = atv->get_diskstream()->speed();
4881 XMLNode &before = playlist->get_state();
4882 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4883 XMLNode &after = playlist->get_state();
4884 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4886 // playlist is frozen, so we have to update manually
4888 playlist->Modified(); /* EMIT SIGNAL */
4892 Editor::track_height_step_timeout ()
4895 struct timeval delta;
4897 gettimeofday (&now, 0);
4898 timersub (&now, &last_track_height_step_timestamp, &delta);
4900 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4901 current_stepping_trackview = 0;