2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include <pbd/error.h>
29 #include <gtkmm2ext/utils.h>
30 #include <pbd/memento_command.h>
32 #include "ardour_ui.h"
34 #include "time_axis_view.h"
35 #include "audio_time_axis.h"
36 #include "audio_region_view.h"
38 #include "streamview.h"
39 #include "region_gain_line.h"
40 #include "automation_time_axis.h"
43 #include "selection.h"
46 #include "rgb_macros.h"
48 #include <ardour/types.h>
49 #include <ardour/profile.h>
50 #include <ardour/route.h>
51 #include <ardour/audio_track.h>
52 #include <ardour/audio_diskstream.h>
53 #include <ardour/playlist.h>
54 #include <ardour/audioplaylist.h>
55 #include <ardour/audioregion.h>
56 #include <ardour/dB.h>
57 #include <ardour/utils.h>
58 #include <ardour/region_factory.h>
65 using namespace ARDOUR;
69 using namespace Editing;
72 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
86 switch (event->type) {
87 case GDK_BUTTON_RELEASE:
88 case GDK_BUTTON_PRESS:
89 case GDK_2BUTTON_PRESS:
90 case GDK_3BUTTON_PRESS:
91 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
93 case GDK_MOTION_NOTIFY:
94 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
96 case GDK_ENTER_NOTIFY:
97 case GDK_LEAVE_NOTIFY:
98 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
101 case GDK_KEY_RELEASE:
102 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
105 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
109 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
110 position is negative (as can be the case with motion events in particular),
111 the frame location is always positive.
114 return pixel_to_frame (*pcx);
118 Editor::mouse_mode_toggled (MouseMode m)
120 if (ignore_mouse_mode_toggle) {
126 if (mouse_select_button.get_active()) {
132 if (mouse_move_button.get_active()) {
138 if (mouse_gain_button.get_active()) {
144 if (mouse_zoom_button.get_active()) {
150 if (mouse_timefx_button.get_active()) {
156 if (mouse_audition_button.get_active()) {
167 Editor::set_mouse_mode (MouseMode m, bool force)
169 if (drag_info.item) {
173 if (!force && m == mouse_mode) {
181 if (mouse_mode != MouseRange) {
183 /* in all modes except range, hide the range selection,
184 show the object (region) selection.
187 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
188 (*i)->set_should_show_selection (true);
190 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
191 (*i)->hide_selection ();
197 in range mode,show the range selection.
200 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
201 if ((*i)->get_selected()) {
202 (*i)->show_selection (selection->time);
207 /* XXX the hack of unsetting all other buttongs should go
208 away once GTK2 allows us to use regular radio buttons drawn like
209 normal buttons, rather than my silly GroupedButton hack.
212 ignore_mouse_mode_toggle = true;
214 switch (mouse_mode) {
216 mouse_select_button.set_active (true);
217 current_canvas_cursor = selector_cursor;
221 mouse_move_button.set_active (true);
222 current_canvas_cursor = grabber_cursor;
226 mouse_gain_button.set_active (true);
227 current_canvas_cursor = cross_hair_cursor;
231 mouse_zoom_button.set_active (true);
232 current_canvas_cursor = zoom_cursor;
236 mouse_timefx_button.set_active (true);
237 current_canvas_cursor = time_fx_cursor; // just use playhead
241 mouse_audition_button.set_active (true);
242 current_canvas_cursor = speaker_cursor;
246 ignore_mouse_mode_toggle = false;
249 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
254 Editor::step_mouse_mode (bool next)
256 switch (current_mouse_mode()) {
258 if (next) set_mouse_mode (MouseRange);
259 else set_mouse_mode (MouseTimeFX);
263 if (next) set_mouse_mode (MouseZoom);
264 else set_mouse_mode (MouseObject);
268 if (next) set_mouse_mode (MouseGain);
269 else set_mouse_mode (MouseRange);
273 if (next) set_mouse_mode (MouseTimeFX);
274 else set_mouse_mode (MouseZoom);
278 if (next) set_mouse_mode (MouseAudition);
279 else set_mouse_mode (MouseGain);
283 if (next) set_mouse_mode (MouseObject);
284 else set_mouse_mode (MouseTimeFX);
290 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
294 /* in object/audition/timefx mode, any button press sets
295 the selection if the object can be selected. this is a
296 bit of hack, because we want to avoid this if the
297 mouse operation is a region alignment.
299 note: not dbl-click or triple-click
302 if (((mouse_mode != MouseObject) &&
303 (mouse_mode != MouseAudition || item_type != RegionItem) &&
304 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
305 (mouse_mode != MouseRange)) ||
307 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
312 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
314 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
316 /* almost no selection action on modified button-2 or button-3 events */
318 if (item_type != RegionItem && event->button.button != 2) {
324 Selection::Operation op = Keyboard::selection_type (event->button.state);
325 bool press = (event->type == GDK_BUTTON_PRESS);
327 // begin_reversible_command (_("select on click"));
331 if (mouse_mode != MouseRange) {
332 commit = set_selected_regionview_from_click (press, op, true);
333 } else if (event->type == GDK_BUTTON_PRESS) {
334 commit = set_selected_track_from_click (press, op, false);
338 case RegionViewNameHighlight:
340 if (mouse_mode != MouseRange) {
341 commit = set_selected_regionview_from_click (press, op, true);
342 } else if (event->type == GDK_BUTTON_PRESS) {
343 commit = set_selected_track_from_click (press, op, false);
347 case FadeInHandleItem:
349 case FadeOutHandleItem:
351 if (mouse_mode != MouseRange) {
352 commit = set_selected_regionview_from_click (press, op, true);
353 } else if (event->type == GDK_BUTTON_PRESS) {
354 commit = set_selected_track_from_click (press, op, false);
358 case GainAutomationControlPointItem:
359 case PanAutomationControlPointItem:
360 case RedirectAutomationControlPointItem:
361 commit = set_selected_track_from_click (press, op, true);
362 if (mouse_mode != MouseRange) {
363 commit |= set_selected_control_point_from_click (op, false);
368 /* for context click or range selection, select track */
369 if (event->button.button == 3) {
370 commit = set_selected_track_from_click (press, op, true);
371 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
372 commit = set_selected_track_from_click (press, op, false);
376 case AutomationTrackItem:
377 commit = set_selected_track_from_click (press, op, true);
385 // commit_reversible_command ();
390 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
392 track_canvas.grab_focus();
394 if (session && session->actively_recording()) {
398 button_selection (item, event, item_type);
400 if (drag_info.item == 0 &&
401 (Keyboard::is_delete_event (&event->button) ||
402 Keyboard::is_context_menu_event (&event->button) ||
403 Keyboard::is_edit_event (&event->button))) {
405 /* handled by button release */
409 switch (event->button.button) {
412 if (event->type == GDK_BUTTON_PRESS) {
414 if (drag_info.item) {
415 drag_info.item->ungrab (event->button.time);
418 /* single mouse clicks on any of these item types operate
419 independent of mouse mode, mostly because they are
420 not on the main track canvas or because we want
426 case PlayheadCursorItem:
427 start_cursor_grab (item, event);
431 if (Keyboard::modifier_state_equals (event->button.state,
432 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
433 hide_marker (item, event);
435 start_marker_grab (item, event);
439 case TempoMarkerItem:
440 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
441 start_tempo_marker_copy_grab (item, event);
443 start_tempo_marker_grab (item, event);
447 case MeterMarkerItem:
448 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
449 start_meter_marker_copy_grab (item, event);
451 start_meter_marker_grab (item, event);
461 case RangeMarkerBarItem:
462 start_range_markerbar_op (item, event, CreateRangeMarker);
466 case TransportMarkerBarItem:
467 start_range_markerbar_op (item, event, CreateTransportMarker);
476 switch (mouse_mode) {
479 case StartSelectionTrimItem:
480 start_selection_op (item, event, SelectionStartTrim);
483 case EndSelectionTrimItem:
484 start_selection_op (item, event, SelectionEndTrim);
488 if (Keyboard::modifier_state_contains
489 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
490 // contains and not equals because I can't use alt as a modifier alone.
491 start_selection_grab (item, event);
492 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
493 /* grab selection for moving */
494 start_selection_op (item, event, SelectionMove);
497 /* this was debated, but decided the more common action was to
498 make a new selection */
499 start_selection_op (item, event, CreateSelection);
504 start_selection_op (item, event, CreateSelection);
510 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
511 event->type == GDK_BUTTON_PRESS) {
513 start_rubberband_select (item, event);
515 } else if (event->type == GDK_BUTTON_PRESS) {
518 case FadeInHandleItem:
519 start_fade_in_grab (item, event);
522 case FadeOutHandleItem:
523 start_fade_out_grab (item, event);
527 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
528 start_region_copy_grab (item, event);
529 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
530 start_region_brush_grab (item, event);
532 start_region_grab (item, event);
536 case RegionViewNameHighlight:
537 start_trim (item, event);
542 /* rename happens on edit clicks */
543 start_trim (clicked_regionview->get_name_highlight(), event);
547 case GainAutomationControlPointItem:
548 case PanAutomationControlPointItem:
549 case RedirectAutomationControlPointItem:
550 start_control_point_grab (item, event);
554 case GainAutomationLineItem:
555 case PanAutomationLineItem:
556 case RedirectAutomationLineItem:
557 start_line_grab_from_line (item, event);
562 case AutomationTrackItem:
563 start_rubberband_select (item, event);
566 /* <CMT Additions> */
567 case ImageFrameHandleStartItem:
568 imageframe_start_handle_op(item, event) ;
571 case ImageFrameHandleEndItem:
572 imageframe_end_handle_op(item, event) ;
575 case MarkerViewHandleStartItem:
576 markerview_item_start_handle_op(item, event) ;
579 case MarkerViewHandleEndItem:
580 markerview_item_end_handle_op(item, event) ;
583 /* </CMT Additions> */
585 /* <CMT Additions> */
587 start_markerview_grab(item, event) ;
590 start_imageframe_grab(item, event) ;
592 /* </CMT Additions> */
608 // start_line_grab_from_regionview (item, event);
611 case GainControlPointItem:
612 start_control_point_grab (item, event);
616 start_line_grab_from_line (item, event);
619 case GainAutomationControlPointItem:
620 case PanAutomationControlPointItem:
621 case RedirectAutomationControlPointItem:
622 start_control_point_grab (item, event);
633 case GainAutomationControlPointItem:
634 case PanAutomationControlPointItem:
635 case RedirectAutomationControlPointItem:
636 start_control_point_grab (item, event);
639 case GainAutomationLineItem:
640 case PanAutomationLineItem:
641 case RedirectAutomationLineItem:
642 start_line_grab_from_line (item, event);
646 // XXX need automation mode to identify which
648 // start_line_grab_from_regionview (item, event);
658 if (event->type == GDK_BUTTON_PRESS) {
659 start_mouse_zoom (item, event);
666 if (item_type == RegionItem) {
667 start_time_fx (item, event);
672 /* handled in release */
681 switch (mouse_mode) {
683 if (event->type == GDK_BUTTON_PRESS) {
686 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
687 start_region_copy_grab (item, event);
689 start_region_grab (item, event);
693 case GainAutomationControlPointItem:
694 case PanAutomationControlPointItem:
695 case RedirectAutomationControlPointItem:
696 start_control_point_grab (item, event);
707 case RegionViewNameHighlight:
708 start_trim (item, event);
713 start_trim (clicked_regionview->get_name_highlight(), event);
724 if (event->type == GDK_BUTTON_PRESS) {
725 /* relax till release */
732 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
733 temporal_zoom_session();
735 temporal_zoom_to_frame (true, event_frame(event));
758 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
760 nframes_t where = event_frame (event, 0, 0);
762 /* no action if we're recording */
764 if (session && session->actively_recording()) {
768 /* first, see if we're finishing a drag ... */
770 if (drag_info.item) {
771 if (end_grab (item, event)) {
772 /* grab dragged, so do nothing else */
777 button_selection (item, event, item_type);
779 /* edit events get handled here */
781 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
787 case TempoMarkerItem:
788 edit_tempo_marker (item);
791 case MeterMarkerItem:
792 edit_meter_marker (item);
796 if (clicked_regionview->name_active()) {
797 return mouse_rename_region (item, event);
807 /* context menu events get handled here */
809 if (Keyboard::is_context_menu_event (&event->button)) {
811 if (drag_info.item == 0) {
813 /* no matter which button pops up the context menu, tell the menu
814 widget to use button 1 to drive menu selection.
819 case FadeInHandleItem:
821 case FadeOutHandleItem:
822 popup_fade_context_menu (1, event->button.time, item, item_type);
826 popup_track_context_menu (1, event->button.time, item_type, false, where);
830 case RegionViewNameHighlight:
832 popup_track_context_menu (1, event->button.time, item_type, false, where);
836 popup_track_context_menu (1, event->button.time, item_type, true, where);
839 case AutomationTrackItem:
840 popup_track_context_menu (1, event->button.time, item_type, false, where);
844 case RangeMarkerBarItem:
845 case TransportMarkerBarItem:
848 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
852 marker_context_menu (&event->button, item);
855 case TempoMarkerItem:
856 tm_marker_context_menu (&event->button, item);
859 case MeterMarkerItem:
860 tm_marker_context_menu (&event->button, item);
863 case CrossfadeViewItem:
864 popup_track_context_menu (1, event->button.time, item_type, false, where);
867 /* <CMT Additions> */
869 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
871 case ImageFrameTimeAxisItem:
872 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
875 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
877 case MarkerTimeAxisItem:
878 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
880 /* <CMT Additions> */
891 /* delete events get handled here */
893 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
896 case TempoMarkerItem:
897 remove_tempo_marker (item);
900 case MeterMarkerItem:
901 remove_meter_marker (item);
905 remove_marker (*item, event);
909 if (mouse_mode == MouseObject) {
910 remove_clicked_region ();
914 case GainControlPointItem:
915 if (mouse_mode == MouseGain) {
916 remove_gain_control_point (item, event);
920 case GainAutomationControlPointItem:
921 case PanAutomationControlPointItem:
922 case RedirectAutomationControlPointItem:
923 remove_control_point (item, event);
932 switch (event->button.button) {
936 /* see comments in button_press_handler */
938 case PlayheadCursorItem:
941 case GainAutomationLineItem:
942 case PanAutomationLineItem:
943 case RedirectAutomationLineItem:
944 case StartSelectionTrimItem:
945 case EndSelectionTrimItem:
949 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
950 snap_to (where, 0, true);
952 mouse_add_new_marker (where);
956 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
959 mouse_add_new_tempo_event (where);
963 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
971 switch (mouse_mode) {
974 case AutomationTrackItem:
975 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
989 // Gain only makes sense for audio regions
991 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
997 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1001 case AutomationTrackItem:
1002 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1003 add_automation_event (item, event, where, event->button.y);
1012 switch (item_type) {
1014 audition_selected_region ();
1031 switch (mouse_mode) {
1034 switch (item_type) {
1036 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1038 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1041 // Button2 click is unused
1054 // x_style_paste (where, 1.0);
1074 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1080 switch (item_type) {
1081 case GainControlPointItem:
1082 if (mouse_mode == MouseGain) {
1083 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1084 cp->set_visible (true);
1088 at_y = cp->get_y ();
1089 cp->item->i2w (at_x, at_y);
1093 fraction = 1.0 - (cp->get_y() / cp->line.height());
1095 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1096 show_verbose_canvas_cursor ();
1098 if (is_drawable()) {
1099 track_canvas.get_window()->set_cursor (*fader_cursor);
1104 case GainAutomationControlPointItem:
1105 case PanAutomationControlPointItem:
1106 case RedirectAutomationControlPointItem:
1107 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1108 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1109 cp->set_visible (true);
1113 at_y = cp->get_y ();
1114 cp->item->i2w (at_x, at_y);
1118 fraction = 1.0 - (cp->get_y() / cp->line.height());
1120 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1121 show_verbose_canvas_cursor ();
1123 if (is_drawable()) {
1124 track_canvas.get_window()->set_cursor (*fader_cursor);
1130 if (mouse_mode == MouseGain) {
1131 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1133 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1134 if (is_drawable()) {
1135 track_canvas.get_window()->set_cursor (*fader_cursor);
1140 case GainAutomationLineItem:
1141 case RedirectAutomationLineItem:
1142 case PanAutomationLineItem:
1143 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1145 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1147 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1149 if (is_drawable()) {
1150 track_canvas.get_window()->set_cursor (*fader_cursor);
1155 case RegionViewNameHighlight:
1156 if (is_drawable() && mouse_mode == MouseObject) {
1157 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1161 case StartSelectionTrimItem:
1162 case EndSelectionTrimItem:
1163 /* <CMT Additions> */
1164 case ImageFrameHandleStartItem:
1165 case ImageFrameHandleEndItem:
1166 case MarkerViewHandleStartItem:
1167 case MarkerViewHandleEndItem:
1168 /* </CMT Additions> */
1170 if (is_drawable()) {
1171 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1175 case EditCursorItem:
1176 case PlayheadCursorItem:
1177 if (is_drawable()) {
1178 track_canvas.get_window()->set_cursor (*grabber_cursor);
1182 case RegionViewName:
1184 /* when the name is not an active item, the entire name highlight is for trimming */
1186 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1187 if (mouse_mode == MouseObject && is_drawable()) {
1188 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1194 case AutomationTrackItem:
1195 if (is_drawable()) {
1196 Gdk::Cursor *cursor;
1197 switch (mouse_mode) {
1199 cursor = selector_cursor;
1202 cursor = zoom_cursor;
1205 cursor = cross_hair_cursor;
1209 track_canvas.get_window()->set_cursor (*cursor);
1211 AutomationTimeAxisView* atv;
1212 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1213 clear_entered_track = false;
1214 set_entered_track (atv);
1220 case RangeMarkerBarItem:
1221 case TransportMarkerBarItem:
1224 if (is_drawable()) {
1225 time_canvas.get_window()->set_cursor (*timebar_cursor);
1230 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1233 marker->set_color_rgba (color_map[cEnteredMarker]);
1235 case MeterMarkerItem:
1236 case TempoMarkerItem:
1237 if (is_drawable()) {
1238 time_canvas.get_window()->set_cursor (*timebar_cursor);
1241 case FadeInHandleItem:
1242 case FadeOutHandleItem:
1243 if (mouse_mode == MouseObject) {
1244 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1246 rect->property_fill_color_rgba() = 0;
1247 rect->property_outline_pixels() = 1;
1256 /* second pass to handle entered track status in a comprehensible way.
1259 switch (item_type) {
1261 case GainAutomationLineItem:
1262 case RedirectAutomationLineItem:
1263 case PanAutomationLineItem:
1264 case GainControlPointItem:
1265 case GainAutomationControlPointItem:
1266 case PanAutomationControlPointItem:
1267 case RedirectAutomationControlPointItem:
1268 /* these do not affect the current entered track state */
1269 clear_entered_track = false;
1272 case AutomationTrackItem:
1273 /* handled above already */
1277 set_entered_track (0);
1285 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1294 switch (item_type) {
1295 case GainControlPointItem:
1296 case GainAutomationControlPointItem:
1297 case PanAutomationControlPointItem:
1298 case RedirectAutomationControlPointItem:
1299 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1300 if (cp->line.npoints() > 1) {
1301 if (!cp->selected) {
1302 cp->set_visible (false);
1306 if (is_drawable()) {
1307 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1310 hide_verbose_canvas_cursor ();
1313 case RegionViewNameHighlight:
1314 case StartSelectionTrimItem:
1315 case EndSelectionTrimItem:
1316 case EditCursorItem:
1317 case PlayheadCursorItem:
1318 /* <CMT Additions> */
1319 case ImageFrameHandleStartItem:
1320 case ImageFrameHandleEndItem:
1321 case MarkerViewHandleStartItem:
1322 case MarkerViewHandleEndItem:
1323 /* </CMT Additions> */
1324 if (is_drawable()) {
1325 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1330 case GainAutomationLineItem:
1331 case RedirectAutomationLineItem:
1332 case PanAutomationLineItem:
1333 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1335 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1337 line->property_fill_color_rgba() = al->get_line_color();
1339 if (is_drawable()) {
1340 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1344 case RegionViewName:
1345 /* see enter_handler() for notes */
1346 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1347 if (is_drawable() && mouse_mode == MouseObject) {
1348 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1353 case RangeMarkerBarItem:
1354 case TransportMarkerBarItem:
1358 if (is_drawable()) {
1359 time_canvas.get_window()->set_cursor (*timebar_cursor);
1364 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1367 loc = find_location_from_marker (marker, is_start);
1368 if (loc) location_flags_changed (loc, this);
1370 case MeterMarkerItem:
1371 case TempoMarkerItem:
1373 if (is_drawable()) {
1374 time_canvas.get_window()->set_cursor (*timebar_cursor);
1379 case FadeInHandleItem:
1380 case FadeOutHandleItem:
1381 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1383 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1385 rect->property_fill_color_rgba() = rv->get_fill_color();
1386 rect->property_outline_pixels() = 0;
1391 case AutomationTrackItem:
1392 if (is_drawable()) {
1393 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1394 clear_entered_track = true;
1395 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1407 Editor::left_automation_track ()
1409 if (clear_entered_track) {
1410 set_entered_track (0);
1411 clear_entered_track = false;
1417 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1421 /* We call this so that MOTION_NOTIFY events continue to be
1422 delivered to the canvas. We need to do this because we set
1423 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1424 the density of the events, at the expense of a round-trip
1425 to the server. Given that this will mostly occur on cases
1426 where DISPLAY = :0.0, and given the cost of what the motion
1427 event might do, its a good tradeoff.
1430 track_canvas.get_pointer (x, y);
1432 if (current_stepping_trackview) {
1433 /* don't keep the persistent stepped trackview if the mouse moves */
1434 current_stepping_trackview = 0;
1435 step_timeout.disconnect ();
1438 if (session && session->actively_recording()) {
1439 /* Sorry. no dragging stuff around while we record */
1443 drag_info.item_type = item_type;
1444 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1445 &drag_info.current_pointer_y);
1447 if (!from_autoscroll && drag_info.item) {
1448 /* item != 0 is the best test i can think of for dragging.
1450 if (!drag_info.move_threshold_passed) {
1452 bool x_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1453 bool y_threshold_passed = (abs ((int) (drag_info.current_pointer_y - drag_info.grab_y)) > 4);
1455 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1457 // and change the initial grab loc/frame if this drag info wants us to
1459 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1460 drag_info.grab_frame = drag_info.current_pointer_frame;
1461 drag_info.grab_x = drag_info.current_pointer_x;
1462 drag_info.grab_y = drag_info.current_pointer_y;
1463 drag_info.last_pointer_frame = drag_info.grab_frame;
1464 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1469 switch (item_type) {
1470 case PlayheadCursorItem:
1471 case EditCursorItem:
1473 case GainControlPointItem:
1474 case RedirectAutomationControlPointItem:
1475 case GainAutomationControlPointItem:
1476 case PanAutomationControlPointItem:
1477 case TempoMarkerItem:
1478 case MeterMarkerItem:
1479 case RegionViewNameHighlight:
1480 case StartSelectionTrimItem:
1481 case EndSelectionTrimItem:
1484 case RedirectAutomationLineItem:
1485 case GainAutomationLineItem:
1486 case PanAutomationLineItem:
1487 case FadeInHandleItem:
1488 case FadeOutHandleItem:
1489 /* <CMT Additions> */
1490 case ImageFrameHandleStartItem:
1491 case ImageFrameHandleEndItem:
1492 case MarkerViewHandleStartItem:
1493 case MarkerViewHandleEndItem:
1494 /* </CMT Additions> */
1495 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1496 (event->motion.state & Gdk::BUTTON2_MASK))) {
1497 if (!from_autoscroll) {
1498 maybe_autoscroll (event);
1500 (this->*(drag_info.motion_callback)) (item, event);
1509 switch (mouse_mode) {
1514 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1515 (event->motion.state & GDK_BUTTON2_MASK))) {
1516 if (!from_autoscroll) {
1517 maybe_autoscroll (event);
1519 (this->*(drag_info.motion_callback)) (item, event);
1530 track_canvas_motion (event);
1531 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1539 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1541 if (drag_info.item == 0) {
1542 fatal << _("programming error: start_grab called without drag item") << endmsg;
1548 cursor = grabber_cursor;
1551 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1553 if (event->button.button == 2) {
1554 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1555 drag_info.y_constrained = true;
1556 drag_info.x_constrained = false;
1558 drag_info.y_constrained = false;
1559 drag_info.x_constrained = true;
1562 drag_info.x_constrained = false;
1563 drag_info.y_constrained = false;
1566 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1567 drag_info.last_pointer_frame = drag_info.grab_frame;
1568 drag_info.current_pointer_frame = drag_info.grab_frame;
1569 drag_info.current_pointer_x = drag_info.grab_x;
1570 drag_info.current_pointer_y = drag_info.grab_y;
1571 drag_info.cumulative_x_drag = 0;
1572 drag_info.cumulative_y_drag = 0;
1573 drag_info.first_move = true;
1574 drag_info.move_threshold_passed = false;
1575 drag_info.want_move_threshold = false;
1576 drag_info.pointer_frame_offset = 0;
1577 drag_info.brushing = false;
1578 drag_info.copied_location = 0;
1580 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1582 event->button.time);
1584 if (session && session->transport_rolling()) {
1585 drag_info.was_rolling = true;
1587 drag_info.was_rolling = false;
1590 switch (snap_type) {
1591 case SnapToRegionStart:
1592 case SnapToRegionEnd:
1593 case SnapToRegionSync:
1594 case SnapToRegionBoundary:
1595 build_region_boundary_cache ();
1603 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1605 drag_info.item->ungrab (0);
1606 drag_info.item = new_item;
1609 cursor = grabber_cursor;
1612 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1616 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1618 bool did_drag = false;
1620 stop_canvas_autoscroll ();
1622 if (drag_info.item == 0) {
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 ((int32_t)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 ((int32_t)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 ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1818 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1824 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1828 if (pos > (arv->region()->last_frame() - 64)) {
1829 fade_length = 64; // this should really be a minimum fade defined somewhere
1831 else if (pos < arv->region()->position()) {
1832 fade_length = arv->region()->length();
1835 fade_length = arv->region()->last_frame() - pos;
1838 /* mapover the region selection */
1840 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1842 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1848 tmp->reset_fade_out_shape_width (fade_length);
1851 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1853 drag_info.first_move = false;
1857 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1859 if (drag_info.first_move) return;
1861 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1863 nframes_t fade_length;
1865 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1866 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1872 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1876 if (pos > (arv->region()->last_frame() - 64)) {
1877 fade_length = 64; // this should really be a minimum fade defined somewhere
1879 else if (pos < arv->region()->position()) {
1880 fade_length = arv->region()->length();
1883 fade_length = arv->region()->last_frame() - pos;
1886 begin_reversible_command (_("change fade out length"));
1888 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1890 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1896 AutomationList& alist = tmp->audio_region()->fade_out();
1897 XMLNode &before = alist.get_state();
1899 tmp->audio_region()->set_fade_out_length (fade_length);
1901 XMLNode &after = alist.get_state();
1902 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1905 commit_reversible_command ();
1909 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1911 drag_info.item = item;
1912 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1913 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1917 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1918 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1922 Cursor* cursor = (Cursor *) drag_info.data;
1924 if (cursor == playhead_cursor) {
1925 _dragging_playhead = true;
1927 if (session && drag_info.was_rolling) {
1928 session->request_stop ();
1931 if (session && session->is_auditioning()) {
1932 session->cancel_audition ();
1936 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1938 show_verbose_time_cursor (cursor->current_frame, 10);
1942 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1944 Cursor* cursor = (Cursor *) drag_info.data;
1945 nframes_t adjusted_frame;
1947 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1948 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1954 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1955 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1956 snap_to (adjusted_frame);
1960 if (adjusted_frame == drag_info.last_pointer_frame) return;
1962 cursor->set_position (adjusted_frame);
1964 if (cursor == edit_cursor) {
1965 edit_cursor_clock.set (cursor->current_frame);
1967 UpdateAllTransportClocks (cursor->current_frame);
1970 show_verbose_time_cursor (cursor->current_frame, 10);
1972 drag_info.last_pointer_frame = adjusted_frame;
1973 drag_info.first_move = false;
1977 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1979 if (drag_info.first_move) return;
1981 cursor_drag_motion_callback (item, event);
1983 _dragging_playhead = false;
1985 if (item == &playhead_cursor->canvas_item) {
1987 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1989 } else if (item == &edit_cursor->canvas_item) {
1990 edit_cursor->set_position (edit_cursor->current_frame);
1991 edit_cursor_clock.set (edit_cursor->current_frame);
1996 Editor::update_marker_drag_item (Location *location)
1998 double x1 = frame_to_pixel (location->start());
1999 double x2 = frame_to_pixel (location->end());
2001 if (location->is_mark()) {
2002 marker_drag_line_points.front().set_x(x1);
2003 marker_drag_line_points.back().set_x(x1);
2004 marker_drag_line->property_points() = marker_drag_line_points;
2007 range_marker_drag_rect->property_x1() = x1;
2008 range_marker_drag_rect->property_x2() = x2;
2013 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2017 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2018 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2024 Location *location = find_location_from_marker (marker, is_start);
2026 drag_info.item = item;
2027 drag_info.data = marker;
2028 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2029 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2033 drag_info.copied_location = new Location (*location);
2034 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2036 update_marker_drag_item (location);
2038 if (location->is_mark()) {
2039 marker_drag_line->show();
2040 marker_drag_line->raise_to_top();
2043 range_marker_drag_rect->show();
2044 range_marker_drag_rect->raise_to_top();
2047 if (is_start) show_verbose_time_cursor (location->start(), 10);
2048 else show_verbose_time_cursor (location->end(), 10);
2052 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2055 Marker* marker = (Marker *) drag_info.data;
2056 Location *real_location;
2057 Location *copy_location;
2059 bool move_both = false;
2063 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2064 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2070 nframes_t next = newframe;
2072 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2073 snap_to (newframe, 0, true);
2076 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2080 /* call this to find out if its the start or end */
2082 real_location = find_location_from_marker (marker, is_start);
2084 /* use the copy that we're "dragging" around */
2086 copy_location = drag_info.copied_location;
2088 f_delta = copy_location->end() - copy_location->start();
2090 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2094 if (copy_location->is_mark()) {
2097 copy_location->set_start (newframe);
2101 if (is_start) { // start-of-range marker
2104 copy_location->set_start (newframe);
2105 copy_location->set_end (newframe + f_delta);
2106 } else if (newframe < copy_location->end()) {
2107 copy_location->set_start (newframe);
2109 snap_to (next, 1, true);
2110 copy_location->set_end (next);
2111 copy_location->set_start (newframe);
2114 } else { // end marker
2117 copy_location->set_end (newframe);
2118 copy_location->set_start (newframe - f_delta);
2119 } else if (newframe > copy_location->start()) {
2120 copy_location->set_end (newframe);
2122 } else if (newframe > 0) {
2123 snap_to (next, -1, true);
2124 copy_location->set_start (next);
2125 copy_location->set_end (newframe);
2130 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2131 drag_info.first_move = false;
2133 update_marker_drag_item (copy_location);
2135 LocationMarkers* lm = find_location_markers (real_location);
2136 lm->set_position (copy_location->start(), copy_location->end());
2138 show_verbose_time_cursor (newframe, 10);
2142 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2144 if (drag_info.first_move) {
2145 marker_drag_motion_callback (item, event);
2149 Marker* marker = (Marker *) drag_info.data;
2153 begin_reversible_command ( _("move marker") );
2154 XMLNode &before = session->locations()->get_state();
2156 Location * location = find_location_from_marker (marker, is_start);
2159 if (location->is_mark()) {
2160 location->set_start (drag_info.copied_location->start());
2162 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2166 XMLNode &after = session->locations()->get_state();
2167 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2168 commit_reversible_command ();
2170 marker_drag_line->hide();
2171 range_marker_drag_rect->hide();
2175 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2178 MeterMarker* meter_marker;
2180 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2181 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2185 meter_marker = dynamic_cast<MeterMarker*> (marker);
2187 MetricSection& section (meter_marker->meter());
2189 if (!section.movable()) {
2193 drag_info.item = item;
2194 drag_info.data = marker;
2195 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2196 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2200 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2202 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2206 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2209 MeterMarker* meter_marker;
2211 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2212 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2216 meter_marker = dynamic_cast<MeterMarker*> (marker);
2218 // create a dummy marker for visual representation of moving the copy.
2219 // The actual copying is not done before we reach the finish callback.
2221 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2222 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2223 *new MeterSection(meter_marker->meter()));
2225 drag_info.item = &new_marker->the_item();
2226 drag_info.copy = true;
2227 drag_info.data = new_marker;
2228 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2229 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2233 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2235 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2239 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2241 MeterMarker* marker = (MeterMarker *) drag_info.data;
2242 nframes_t adjusted_frame;
2244 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2245 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2251 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2252 snap_to (adjusted_frame);
2255 if (adjusted_frame == drag_info.last_pointer_frame) return;
2257 marker->set_position (adjusted_frame);
2260 drag_info.last_pointer_frame = adjusted_frame;
2261 drag_info.first_move = false;
2263 show_verbose_time_cursor (adjusted_frame, 10);
2267 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2269 if (drag_info.first_move) return;
2271 meter_marker_drag_motion_callback (drag_info.item, event);
2273 MeterMarker* marker = (MeterMarker *) drag_info.data;
2276 TempoMap& map (session->tempo_map());
2277 map.bbt_time (drag_info.last_pointer_frame, when);
2279 if (drag_info.copy == true) {
2280 begin_reversible_command (_("copy meter mark"));
2281 XMLNode &before = map.get_state();
2282 map.add_meter (marker->meter(), when);
2283 XMLNode &after = map.get_state();
2284 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2285 commit_reversible_command ();
2287 // delete the dummy marker we used for visual representation of copying.
2288 // a new visual marker will show up automatically.
2291 begin_reversible_command (_("move meter mark"));
2292 XMLNode &before = map.get_state();
2293 map.move_meter (marker->meter(), when);
2294 XMLNode &after = map.get_state();
2295 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2296 commit_reversible_command ();
2301 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2304 TempoMarker* tempo_marker;
2306 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2307 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2311 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2312 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2316 MetricSection& section (tempo_marker->tempo());
2318 if (!section.movable()) {
2322 drag_info.item = item;
2323 drag_info.data = marker;
2324 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2325 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2329 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2330 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2334 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2337 TempoMarker* tempo_marker;
2339 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2340 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2344 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2345 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2349 // create a dummy marker for visual representation of moving the copy.
2350 // The actual copying is not done before we reach the finish callback.
2352 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2353 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2354 *new TempoSection(tempo_marker->tempo()));
2356 drag_info.item = &new_marker->the_item();
2357 drag_info.copy = true;
2358 drag_info.data = new_marker;
2359 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2360 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2364 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2366 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2370 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2372 TempoMarker* marker = (TempoMarker *) drag_info.data;
2373 nframes_t adjusted_frame;
2375 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2376 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2382 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2383 snap_to (adjusted_frame);
2386 if (adjusted_frame == drag_info.last_pointer_frame) return;
2388 /* OK, we've moved far enough to make it worth actually move the thing. */
2390 marker->set_position (adjusted_frame);
2392 show_verbose_time_cursor (adjusted_frame, 10);
2394 drag_info.last_pointer_frame = adjusted_frame;
2395 drag_info.first_move = false;
2399 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2401 if (drag_info.first_move) return;
2403 tempo_marker_drag_motion_callback (drag_info.item, event);
2405 TempoMarker* marker = (TempoMarker *) drag_info.data;
2408 TempoMap& map (session->tempo_map());
2409 map.bbt_time (drag_info.last_pointer_frame, when);
2411 if (drag_info.copy == true) {
2412 begin_reversible_command (_("copy tempo mark"));
2413 XMLNode &before = map.get_state();
2414 map.add_tempo (marker->tempo(), when);
2415 XMLNode &after = map.get_state();
2416 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2417 commit_reversible_command ();
2419 // delete the dummy marker we used for visual representation of copying.
2420 // a new visual marker will show up automatically.
2423 begin_reversible_command (_("move tempo mark"));
2424 XMLNode &before = map.get_state();
2425 map.move_tempo (marker->tempo(), when);
2426 XMLNode &after = map.get_state();
2427 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2428 commit_reversible_command ();
2433 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2435 ControlPoint* control_point;
2437 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2438 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2442 // We shouldn't remove the first or last gain point
2443 if (control_point->line.is_last_point(*control_point) ||
2444 control_point->line.is_first_point(*control_point)) {
2448 control_point->line.remove_point (*control_point);
2452 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2454 ControlPoint* control_point;
2456 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2457 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2461 control_point->line.remove_point (*control_point);
2465 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2467 ControlPoint* control_point;
2469 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2470 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2474 drag_info.item = item;
2475 drag_info.data = control_point;
2476 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2477 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2479 start_grab (event, fader_cursor);
2481 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2483 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2484 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2485 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2487 show_verbose_canvas_cursor ();
2491 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2493 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2495 double cx = drag_info.current_pointer_x;
2496 double cy = drag_info.current_pointer_y;
2498 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2499 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2501 if (drag_info.x_constrained) {
2502 cx = drag_info.grab_x;
2504 if (drag_info.y_constrained) {
2505 cy = drag_info.grab_y;
2508 cp->line.parent_group().w2i (cx, cy);
2512 cy = min ((double) cp->line.height(), cy);
2514 //translate cx to frames
2515 nframes_t cx_frames = unit_to_frame (cx);
2517 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2518 snap_to (cx_frames);
2521 float fraction = 1.0 - (cy / cp->line.height());
2525 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2531 cp->line.point_drag (*cp, cx_frames , fraction, push);
2533 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2535 drag_info.first_move = false;
2539 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2541 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2543 if (drag_info.first_move) {
2547 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2548 reset_point_selection ();
2552 control_point_drag_motion_callback (item, event);
2554 cp->line.end_drag (cp);
2558 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2560 switch (mouse_mode) {
2562 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2563 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2571 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2575 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2576 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2580 start_line_grab (al, event);
2584 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2588 nframes_t frame_within_region;
2590 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2594 cx = event->button.x;
2595 cy = event->button.y;
2596 line->parent_group().w2i (cx, cy);
2597 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2599 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2600 current_line_drag_info.after)) {
2601 /* no adjacent points */
2605 drag_info.item = &line->grab_item();
2606 drag_info.data = line;
2607 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2608 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2610 start_grab (event, fader_cursor);
2612 double fraction = 1.0 - (cy / line->height());
2614 line->start_drag (0, drag_info.grab_frame, fraction);
2616 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2617 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2618 show_verbose_canvas_cursor ();
2622 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2624 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2625 double cx = drag_info.current_pointer_x;
2626 double cy = drag_info.current_pointer_y;
2628 line->parent_group().w2i (cx, cy);
2631 fraction = 1.0 - (cy / line->height());
2635 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2641 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2643 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2647 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2649 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2650 line_drag_motion_callback (item, event);
2655 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2657 if (selection->regions.empty() || clicked_regionview == 0) {
2661 drag_info.copy = false;
2662 drag_info.item = item;
2663 drag_info.data = clicked_regionview;
2664 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2665 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2670 TimeAxisView* tvp = clicked_trackview;
2671 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2673 if (tv && tv->is_audio_track()) {
2674 speed = tv->get_diskstream()->speed();
2677 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2678 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2679 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2680 // we want a move threshold
2681 drag_info.want_move_threshold = true;
2683 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2685 begin_reversible_command (_("move region(s)"));
2689 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2691 if (selection->regions.empty() || clicked_regionview == 0) {
2695 drag_info.copy = true;
2696 drag_info.item = item;
2697 drag_info.data = clicked_regionview;
2701 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2702 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2705 if (atv && atv->is_audio_track()) {
2706 speed = atv->get_diskstream()->speed();
2709 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2710 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2711 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2712 // we want a move threshold
2713 drag_info.want_move_threshold = true;
2714 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2715 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2716 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2720 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2722 if (selection->regions.empty() || clicked_regionview == 0) {
2726 drag_info.copy = false;
2727 drag_info.item = item;
2728 drag_info.data = clicked_regionview;
2729 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2730 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2735 TimeAxisView* tvp = clicked_trackview;
2736 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2738 if (tv && tv->is_audio_track()) {
2739 speed = tv->get_diskstream()->speed();
2742 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2743 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2744 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2745 // we want a move threshold
2746 drag_info.want_move_threshold = true;
2747 drag_info.brushing = true;
2749 begin_reversible_command (_("Drag region brush"));
2753 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2757 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2758 nframes_t pending_region_position = 0;
2759 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2760 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2761 bool clamp_y_axis = false;
2762 vector<int32_t> height_list(512) ;
2763 vector<int32_t>::iterator j;
2765 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2767 drag_info.want_move_threshold = false; // don't copy again
2769 /* duplicate the region(s) */
2771 vector<RegionView*> new_regionviews;
2773 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2776 AudioRegionView* arv;
2781 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
2782 /* XXX handle MIDI here */
2786 nrv = new AudioRegionView (*arv);
2787 nrv->get_canvas_group()->show ();
2789 new_regionviews.push_back (nrv);
2792 if (new_regionviews.empty()) {
2796 /* reset selection to new regionviews */
2798 selection->set (new_regionviews);
2800 /* reset drag_info data to reflect the fact that we are dragging the copies */
2802 drag_info.data = new_regionviews.front();
2804 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2807 /* Which trackview is this ? */
2809 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2810 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2812 /* The region motion is only processed if the pointer is over
2816 if (!tv || !tv->is_audio_track()) {
2817 /* To make sure we hide the verbose canvas cursor when the mouse is
2818 not held over and audiotrack.
2820 hide_verbose_canvas_cursor ();
2824 original_pointer_order = drag_info.last_trackview->order;
2826 /************************************************************
2828 ************************************************************/
2830 if (drag_info.brushing) {
2831 clamp_y_axis = true;
2836 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2838 int32_t children = 0, numtracks = 0;
2839 // XXX hard coding track limit, oh my, so very very bad
2840 bitset <1024> tracks (0x00);
2841 /* get a bitmask representing the visible tracks */
2843 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2844 TimeAxisView *tracklist_timeview;
2845 tracklist_timeview = (*i);
2846 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2847 list<TimeAxisView*> children_list;
2849 /* zeroes are audio tracks. ones are other types. */
2851 if (!atv2->hidden()) {
2853 if (visible_y_high < atv2->order) {
2854 visible_y_high = atv2->order;
2856 if (visible_y_low > atv2->order) {
2857 visible_y_low = atv2->order;
2860 if (!atv2->is_audio_track()) {
2861 tracks = tracks |= (0x01 << atv2->order);
2864 height_list[atv2->order] = (*i)->height;
2866 if ((children_list = atv2->get_child_list()).size() > 0) {
2867 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2868 tracks = tracks |= (0x01 << (atv2->order + children));
2869 height_list[atv2->order + children] = (*j)->height;
2877 /* find the actual span according to the canvas */
2879 canvas_pointer_y_span = pointer_y_span;
2880 if (drag_info.last_trackview->order >= tv->order) {
2882 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2883 if (height_list[y] == 0 ) {
2884 canvas_pointer_y_span--;
2889 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2890 if ( height_list[y] == 0 ) {
2891 canvas_pointer_y_span++;
2896 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2897 RegionView* rv2 = (*i);
2898 double ix1, ix2, iy1, iy2;
2901 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2902 rv2->get_canvas_group()->i2w (ix1, iy1);
2903 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2904 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2906 if (atv2->order != original_pointer_order) {
2907 /* this isn't the pointer track */
2909 if (canvas_pointer_y_span > 0) {
2911 /* moving up the canvas */
2912 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2914 int32_t visible_tracks = 0;
2915 while (visible_tracks < canvas_pointer_y_span ) {
2918 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2919 /* we're passing through a hidden track */
2924 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2925 clamp_y_axis = true;
2929 clamp_y_axis = true;
2932 } else if (canvas_pointer_y_span < 0) {
2934 /*moving down the canvas*/
2936 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2939 int32_t visible_tracks = 0;
2941 while (visible_tracks > canvas_pointer_y_span ) {
2944 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2948 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2949 clamp_y_axis = true;
2954 clamp_y_axis = true;
2960 /* this is the pointer's track */
2961 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2962 clamp_y_axis = true;
2963 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2964 clamp_y_axis = true;
2972 } else if (drag_info.last_trackview == tv) {
2973 clamp_y_axis = true;
2977 if (!clamp_y_axis) {
2978 drag_info.last_trackview = tv;
2981 /************************************************************
2983 ************************************************************/
2985 /* compute the amount of pointer motion in frames, and where
2986 the region would be if we moved it by that much.
2989 if (drag_info.move_threshold_passed) {
2991 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2993 nframes_t sync_frame;
2994 nframes_t sync_offset;
2997 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2999 sync_offset = rv->region()->sync_offset (sync_dir);
3000 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3002 /* we snap if the snap modifier is not enabled.
3005 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3006 snap_to (sync_frame);
3009 if (sync_frame - sync_offset <= sync_frame) {
3010 pending_region_position = sync_frame - (sync_dir*sync_offset);
3012 pending_region_position = 0;
3016 pending_region_position = 0;
3019 if (pending_region_position > max_frames - rv->region()->length()) {
3020 pending_region_position = drag_info.last_frame_position;
3023 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3025 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3027 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3028 to make it appear at the new location.
3031 if (pending_region_position > drag_info.last_frame_position) {
3032 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3034 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3037 drag_info.last_frame_position = pending_region_position;
3044 /* threshold not passed */
3049 /*************************************************************
3051 ************************************************************/
3053 if (x_delta == 0 && (pointer_y_span == 0)) {
3054 /* haven't reached next snap point, and we're not switching
3055 trackviews. nothing to do.
3061 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3063 RegionView* rv2 = (*i);
3065 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3067 double ix1, ix2, iy1, iy2;
3068 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3069 rv2->get_canvas_group()->i2w (ix1, iy1);
3078 /*************************************************************
3080 ************************************************************/
3084 if (drag_info.first_move) {
3085 if (drag_info.move_threshold_passed) {
3096 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3097 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3099 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3101 RegionView* rv = (*i);
3102 double ix1, ix2, iy1, iy2;
3103 int32_t temp_pointer_y_span = pointer_y_span;
3105 /* get item BBox, which will be relative to parent. so we have
3106 to query on a child, then convert to world coordinates using
3110 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3111 rv->get_canvas_group()->i2w (ix1, iy1);
3112 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3113 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3114 AudioTimeAxisView* temp_atv;
3116 if ((pointer_y_span != 0) && !clamp_y_axis) {
3119 for (j = height_list.begin(); j!= height_list.end(); j++) {
3120 if (x == canvas_atv->order) {
3121 /* we found the track the region is on */
3122 if (x != original_pointer_order) {
3123 /*this isn't from the same track we're dragging from */
3124 temp_pointer_y_span = canvas_pointer_y_span;
3126 while (temp_pointer_y_span > 0) {
3127 /* we're moving up canvas-wise,
3128 so we need to find the next track height
3130 if (j != height_list.begin()) {
3133 if (x != original_pointer_order) {
3134 /* we're not from the dragged track, so ignore hidden tracks. */
3136 temp_pointer_y_span++;
3140 temp_pointer_y_span--;
3142 while (temp_pointer_y_span < 0) {
3144 if (x != original_pointer_order) {
3146 temp_pointer_y_span--;
3150 if (j != height_list.end()) {
3153 temp_pointer_y_span++;
3155 /* find out where we'll be when we move and set height accordingly */
3157 tvp2 = trackview_by_y_position (iy1 + y_delta);
3158 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3159 rv->set_height (temp_atv->height);
3161 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3162 personally, i think this can confuse things, but never mind.
3165 //const GdkColor& col (temp_atv->view->get_region_color());
3166 //rv->set_color (const_cast<GdkColor&>(col));
3173 /* prevent the regionview from being moved to before
3174 the zero position on the canvas.
3179 if (-x_delta > ix1) {
3182 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3183 x_delta = max_frames - rv->region()->last_frame();
3186 if (drag_info.first_move) {
3188 /* hide any dependent views */
3190 rv->get_time_axis_view().hide_dependent_views (*rv);
3192 /* this is subtle. raising the regionview itself won't help,
3193 because raise_to_top() just puts the item on the top of
3194 its parent's stack. so, we need to put the trackview canvas_display group
3195 on the top, since its parent is the whole canvas.
3198 rv->get_canvas_group()->raise_to_top();
3199 rv->get_time_axis_view().canvas_display->raise_to_top();
3200 cursor_group->raise_to_top();
3202 rv->fake_set_opaque (true);
3205 if (drag_info.brushing) {
3206 mouse_brush_insert_region (rv, pending_region_position);
3208 rv->move (x_delta, y_delta);
3211 } /* foreach region */
3215 if (drag_info.first_move && drag_info.move_threshold_passed) {
3216 cursor_group->raise_to_top();
3217 drag_info.first_move = false;
3220 if (x_delta != 0 && !drag_info.brushing) {
3221 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3226 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3229 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3230 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3231 bool nocommit = true;
3233 RouteTimeAxisView* atv;
3234 bool regionview_y_movement;
3235 bool regionview_x_movement;
3236 vector<RegionView*> copies;
3238 /* first_move is set to false if the regionview has been moved in the
3242 if (drag_info.first_move) {
3245 if (drag_info.copy) {
3246 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3247 copies.push_back (*i);
3255 /* The regionview has been moved at some stage during the grab so we need
3256 to account for any mouse movement between this event and the last one.
3259 region_drag_motion_callback (item, event);
3261 if (drag_info.brushing) {
3262 /* all changes were made during motion event handlers */
3264 if (drag_info.copy) {
3265 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3266 copies.push_back (*i);
3273 /* adjust for track speed */
3276 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3277 if (atv && atv->get_diskstream()) {
3278 speed = atv->get_diskstream()->speed();
3281 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3282 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3284 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3285 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3289 if (drag_info.copy) {
3290 if (drag_info.x_constrained) {
3291 op_string = _("fixed time region copy");
3293 op_string = _("region copy");
3296 if (drag_info.x_constrained) {
3297 op_string = _("fixed time region drag");
3299 op_string = _("region drag");
3303 begin_reversible_command (op_string);
3305 if (regionview_y_movement) {
3307 /* moved to a different audio track. */
3309 vector<RegionView*> new_selection;
3311 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3313 RegionView* rv = (*i);
3315 double ix1, ix2, iy1, iy2;
3317 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3318 rv->get_canvas_group()->i2w (ix1, iy1);
3319 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3320 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3322 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3323 boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
3325 where = (nframes_t) (unit_to_frame (ix1) * speed);
3326 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3328 /* undo the previous hide_dependent_views so that xfades don't
3329 disappear on copying regions
3332 rv->get_time_axis_view().reveal_dependent_views (*rv);
3334 if (!drag_info.copy) {
3336 /* the region that used to be in the old playlist is not
3337 moved to the new one - we make a copy of it. as a result,
3338 any existing editor for the region should no longer be
3342 rv->hide_region_editor();
3343 rv->fake_set_opaque (false);
3345 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3346 from_playlist->remove_region ((rv->region()));
3347 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3351 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3353 copies.push_back (rv);
3356 latest_regionview = 0;
3358 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3359 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3360 to_playlist->add_region (new_region, where);
3361 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3364 if (latest_regionview) {
3365 new_selection.push_back (latest_regionview);
3368 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3369 was selected in all of them, then removing it from the playlist will have removed all
3370 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3371 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3372 corresponding regionview, and the selection is now empty).
3374 this could have invalidated any and all iterators into the region selection.
3376 the heuristic we use here is: if the region selection is empty, break out of the loop
3377 here. if the region selection is not empty, then restart the loop because we know that
3378 we must have removed at least the region(view) we've just been working on as well as any
3379 that we processed on previous iterations.
3381 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3382 we can just iterate.
3385 if (drag_info.copy) {
3388 if (selection->regions.empty()) {
3391 i = selection->regions.by_layer().begin();
3396 selection->set (new_selection);
3400 /* motion within a single track */
3402 list<RegionView*> regions = selection->regions.by_layer();
3404 if (drag_info.copy) {
3405 selection->clear_regions();
3408 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3412 if (rv->region()->locked()) {
3417 if (regionview_x_movement) {
3418 double ownspeed = 1.0;
3419 atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3421 if (atv && atv->get_diskstream()) {
3422 ownspeed = atv->get_diskstream()->speed();
3425 /* base the new region position on the current position of the regionview.*/
3427 double ix1, ix2, iy1, iy2;
3429 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3430 rv->get_canvas_group()->i2w (ix1, iy1);
3431 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3435 where = rv->region()->position();
3438 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3440 assert (to_playlist);
3444 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3446 if (drag_info.copy) {
3448 boost::shared_ptr<Region> newregion;
3449 boost::shared_ptr<Region> ar;
3451 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3452 newregion = RegionFactory::create (ar);
3454 /* XXX MIDI HERE drobilla */
3460 latest_regionview = 0;
3461 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3462 to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
3465 if (latest_regionview) {
3466 atv->reveal_dependent_views (*latest_regionview);
3467 selection->add (latest_regionview);
3470 /* if the original region was locked, we don't care for the new one */
3472 newregion->set_locked (false);
3476 /* just change the model */
3478 rv->region()->set_position (where, (void*) this);
3484 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3486 if (drag_info.copy) {
3487 copies.push_back (rv);
3495 commit_reversible_command ();
3498 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3504 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3506 /* Either add to or set the set the region selection, unless
3507 this is an alignment click (control used)
3510 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3511 TimeAxisView* tv = &rv.get_time_axis_view();
3512 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3514 if (atv && atv->is_audio_track()) {
3515 speed = atv->get_diskstream()->speed();
3518 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3520 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3522 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3524 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3528 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3534 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3540 nframes_t frame_rate;
3547 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3548 case AudioClock::BBT:
3549 session->bbt_time (frame, bbt);
3550 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3553 case AudioClock::SMPTE:
3554 session->smpte_time (frame, smpte);
3555 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3558 case AudioClock::MinSec:
3559 /* XXX this is copied from show_verbose_duration_cursor() */
3560 frame_rate = session->frame_rate();
3561 hours = frame / (frame_rate * 3600);
3562 frame = frame % (frame_rate * 3600);
3563 mins = frame / (frame_rate * 60);
3564 frame = frame % (frame_rate * 60);
3565 secs = (float) frame / (float) frame_rate;
3566 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3570 snprintf (buf, sizeof(buf), "%u", frame);
3574 if (xpos >= 0 && ypos >=0) {
3575 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3578 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3580 show_verbose_canvas_cursor ();
3584 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3591 nframes_t distance, frame_rate;
3593 Meter meter_at_start(session->tempo_map().meter_at(start));
3599 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3600 case AudioClock::BBT:
3601 session->bbt_time (start, sbbt);
3602 session->bbt_time (end, ebbt);
3605 /* XXX this computation won't work well if the
3606 user makes a selection that spans any meter changes.
3609 ebbt.bars -= sbbt.bars;
3610 if (ebbt.beats >= sbbt.beats) {
3611 ebbt.beats -= sbbt.beats;
3614 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3616 if (ebbt.ticks >= sbbt.ticks) {
3617 ebbt.ticks -= sbbt.ticks;
3620 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3623 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3626 case AudioClock::SMPTE:
3627 session->smpte_duration (end - start, smpte);
3628 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3631 case AudioClock::MinSec:
3632 /* XXX this stuff should be elsewhere.. */
3633 distance = end - start;
3634 frame_rate = session->frame_rate();
3635 hours = distance / (frame_rate * 3600);
3636 distance = distance % (frame_rate * 3600);
3637 mins = distance / (frame_rate * 60);
3638 distance = distance % (frame_rate * 60);
3639 secs = (float) distance / (float) frame_rate;
3640 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3644 snprintf (buf, sizeof(buf), "%u", end - start);
3648 if (xpos >= 0 && ypos >=0) {
3649 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3652 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3654 show_verbose_canvas_cursor ();
3658 Editor::collect_new_region_view (RegionView* rv)
3660 latest_regionview = rv;
3664 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3666 if (clicked_regionview == 0) {
3670 /* lets try to create new Region for the selection */
3672 vector<boost::shared_ptr<AudioRegion> > new_regions;
3673 create_region_from_selection (new_regions);
3675 if (new_regions.empty()) {
3679 /* XXX fix me one day to use all new regions */
3681 boost::shared_ptr<Region> region (new_regions.front());
3683 /* add it to the current stream/playlist.
3685 tricky: the streamview for the track will add a new regionview. we will
3686 catch the signal it sends when it creates the regionview to
3687 set the regionview we want to then drag.
3690 latest_regionview = 0;
3691 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3693 /* A selection grab currently creates two undo/redo operations, one for
3694 creating the new region and another for moving it.
3697 begin_reversible_command (_("selection grab"));
3699 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3701 XMLNode *before = &(playlist->get_state());
3702 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3703 XMLNode *after = &(playlist->get_state());
3704 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3706 commit_reversible_command ();
3710 if (latest_regionview == 0) {
3711 /* something went wrong */
3715 /* we need to deselect all other regionviews, and select this one
3716 i'm ignoring undo stuff, because the region creation will take care of it */
3717 selection->set (latest_regionview);
3719 drag_info.item = latest_regionview->get_canvas_group();
3720 drag_info.data = latest_regionview;
3721 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3722 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3726 drag_info.last_trackview = clicked_trackview;
3727 drag_info.last_frame_position = latest_regionview->region()->position();
3728 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3730 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3734 Editor::cancel_selection ()
3736 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3737 (*i)->hide_selection ();
3739 begin_reversible_command (_("cancel selection"));
3740 selection->clear ();
3741 clicked_selection = 0;
3742 commit_reversible_command ();
3746 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3748 nframes_t start = 0;
3755 drag_info.item = item;
3756 drag_info.motion_callback = &Editor::drag_selection;
3757 drag_info.finished_callback = &Editor::end_selection_op;
3762 case CreateSelection:
3763 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3764 drag_info.copy = true;
3766 drag_info.copy = false;
3768 start_grab (event, selector_cursor);
3771 case SelectionStartTrim:
3772 if (clicked_trackview) {
3773 clicked_trackview->order_selection_trims (item, true);
3775 start_grab (event, trimmer_cursor);
3776 start = selection->time[clicked_selection].start;
3777 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3780 case SelectionEndTrim:
3781 if (clicked_trackview) {
3782 clicked_trackview->order_selection_trims (item, false);
3784 start_grab (event, trimmer_cursor);
3785 end = selection->time[clicked_selection].end;
3786 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3790 start = selection->time[clicked_selection].start;
3792 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3796 if (selection_op == SelectionMove) {
3797 show_verbose_time_cursor(start, 10);
3799 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3804 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3806 nframes_t start = 0;
3809 nframes_t pending_position;
3811 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3812 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3815 pending_position = 0;
3818 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3819 snap_to (pending_position);
3822 /* only alter selection if the current frame is
3823 different from the last frame position (adjusted)
3826 if (pending_position == drag_info.last_pointer_frame) return;
3828 switch (selection_op) {
3829 case CreateSelection:
3831 if (drag_info.first_move) {
3832 snap_to (drag_info.grab_frame);
3835 if (pending_position < drag_info.grab_frame) {
3836 start = pending_position;
3837 end = drag_info.grab_frame;
3839 end = pending_position;
3840 start = drag_info.grab_frame;
3843 /* first drag: Either add to the selection
3844 or create a new selection->
3847 if (drag_info.first_move) {
3849 begin_reversible_command (_("range selection"));
3851 if (drag_info.copy) {
3852 /* adding to the selection */
3853 clicked_selection = selection->add (start, end);
3854 drag_info.copy = false;
3856 /* new selection-> */
3857 clicked_selection = selection->set (clicked_trackview, start, end);
3862 case SelectionStartTrim:
3864 if (drag_info.first_move) {
3865 begin_reversible_command (_("trim selection start"));
3868 start = selection->time[clicked_selection].start;
3869 end = selection->time[clicked_selection].end;
3871 if (pending_position > end) {
3874 start = pending_position;
3878 case SelectionEndTrim:
3880 if (drag_info.first_move) {
3881 begin_reversible_command (_("trim selection end"));
3884 start = selection->time[clicked_selection].start;
3885 end = selection->time[clicked_selection].end;
3887 if (pending_position < start) {
3890 end = pending_position;
3897 if (drag_info.first_move) {
3898 begin_reversible_command (_("move selection"));
3901 start = selection->time[clicked_selection].start;
3902 end = selection->time[clicked_selection].end;
3904 length = end - start;
3906 start = pending_position;
3909 end = start + length;
3914 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3915 start_canvas_autoscroll (1);
3919 selection->replace (clicked_selection, start, end);
3922 drag_info.last_pointer_frame = pending_position;
3923 drag_info.first_move = false;
3925 if (selection_op == SelectionMove) {
3926 show_verbose_time_cursor(start, 10);
3928 show_verbose_time_cursor(pending_position, 10);
3933 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3935 if (!drag_info.first_move) {
3936 drag_selection (item, event);
3937 /* XXX this is not object-oriented programming at all. ick */
3938 if (selection->time.consolidate()) {
3939 selection->TimeChanged ();
3941 commit_reversible_command ();
3943 /* just a click, no pointer movement.*/
3945 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3947 selection->clear_time();
3952 /* XXX what happens if its a music selection? */
3953 session->set_audio_range (selection->time);
3954 stop_canvas_autoscroll ();
3958 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3961 TimeAxisView* tvp = clicked_trackview;
3962 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3964 if (tv && tv->is_audio_track()) {
3965 speed = tv->get_diskstream()->speed();
3968 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3969 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3970 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3972 //drag_info.item = clicked_regionview->get_name_highlight();
3973 drag_info.item = item;
3974 drag_info.motion_callback = &Editor::trim_motion_callback;
3975 drag_info.finished_callback = &Editor::trim_finished_callback;
3977 start_grab (event, trimmer_cursor);
3979 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3980 trim_op = ContentsTrim;
3982 /* These will get overridden for a point trim.*/
3983 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3984 /* closer to start */
3985 trim_op = StartTrim;
3986 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3994 show_verbose_time_cursor(region_start, 10);
3997 show_verbose_time_cursor(region_end, 10);
4000 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4006 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4008 RegionView* rv = clicked_regionview;
4009 nframes_t frame_delta = 0;
4010 bool left_direction;
4011 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4013 /* snap modifier works differently here..
4014 its' current state has to be passed to the
4015 various trim functions in order to work properly
4019 TimeAxisView* tvp = clicked_trackview;
4020 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4021 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4023 if (tv && tv->is_audio_track()) {
4024 speed = tv->get_diskstream()->speed();
4027 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4028 left_direction = true;
4030 left_direction = false;
4034 snap_to (drag_info.current_pointer_frame);
4037 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4041 if (drag_info.first_move) {
4047 trim_type = "Region start trim";
4050 trim_type = "Region end trim";
4053 trim_type = "Region content trim";
4057 begin_reversible_command (trim_type);
4059 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4060 (*i)->fake_set_opaque(false);
4061 (*i)->region()->freeze ();
4063 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4065 arv->temporarily_hide_envelope ();
4067 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4068 insert_result = motion_frozen_playlists.insert (pl);
4069 if (insert_result.second) {
4070 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4075 if (left_direction) {
4076 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4078 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4083 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4086 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4087 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4093 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4096 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4097 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4104 bool swap_direction = false;
4106 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4107 swap_direction = true;
4110 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4111 i != selection->regions.by_layer().end(); ++i)
4113 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4121 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4124 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4127 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4131 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4132 drag_info.first_move = false;
4136 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4138 boost::shared_ptr<Region> region (rv.region());
4140 if (region->locked()) {
4144 nframes_t new_bound;
4147 TimeAxisView* tvp = clicked_trackview;
4148 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4150 if (tv && tv->is_audio_track()) {
4151 speed = tv->get_diskstream()->speed();
4154 if (left_direction) {
4155 if (swap_direction) {
4156 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4158 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4161 if (swap_direction) {
4162 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4164 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4169 snap_to (new_bound);
4171 region->trim_start ((nframes_t) (new_bound * speed), this);
4172 rv.region_changed (StartChanged);
4176 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4178 boost::shared_ptr<Region> region (rv.region());
4180 if (region->locked()) {
4184 nframes_t new_bound;
4187 TimeAxisView* tvp = clicked_trackview;
4188 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4190 if (tv && tv->is_audio_track()) {
4191 speed = tv->get_diskstream()->speed();
4194 if (left_direction) {
4195 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4197 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4201 snap_to (new_bound, (left_direction ? 0 : 1));
4204 region->trim_front ((nframes_t) (new_bound * speed), this);
4206 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4210 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4212 boost::shared_ptr<Region> region (rv.region());
4214 if (region->locked()) {
4218 nframes_t new_bound;
4221 TimeAxisView* tvp = clicked_trackview;
4222 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4224 if (tv && tv->is_audio_track()) {
4225 speed = tv->get_diskstream()->speed();
4228 if (left_direction) {
4229 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4231 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4235 snap_to (new_bound);
4237 region->trim_end ((nframes_t) (new_bound * speed), this);
4238 rv.region_changed (LengthChanged);
4242 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4244 if (!drag_info.first_move) {
4245 trim_motion_callback (item, event);
4247 if (!clicked_regionview->get_selected()) {
4248 thaw_region_after_trim (*clicked_regionview);
4251 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4252 i != selection->regions.by_layer().end(); ++i)
4254 thaw_region_after_trim (**i);
4255 (*i)->fake_set_opaque (true);
4259 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4261 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4264 motion_frozen_playlists.clear ();
4266 commit_reversible_command();
4268 /* no mouse movement */
4274 Editor::point_trim (GdkEvent* event)
4276 RegionView* rv = clicked_regionview;
4277 nframes_t new_bound = drag_info.current_pointer_frame;
4279 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4280 snap_to (new_bound);
4283 /* Choose action dependant on which button was pressed */
4284 switch (event->button.button) {
4286 trim_op = StartTrim;
4287 begin_reversible_command (_("Start point trim"));
4289 if (rv->get_selected()) {
4291 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4292 i != selection->regions.by_layer().end(); ++i)
4294 if (!(*i)->region()->locked()) {
4295 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4296 XMLNode &before = pl->get_state();
4297 (*i)->region()->trim_front (new_bound, this);
4298 XMLNode &after = pl->get_state();
4299 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4305 if (!rv->region()->locked()) {
4306 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4307 XMLNode &before = pl->get_state();
4308 rv->region()->trim_front (new_bound, this);
4309 XMLNode &after = pl->get_state();
4310 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4314 commit_reversible_command();
4319 begin_reversible_command (_("End point trim"));
4321 if (rv->get_selected()) {
4323 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4325 if (!(*i)->region()->locked()) {
4326 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4327 XMLNode &before = pl->get_state();
4328 (*i)->region()->trim_end (new_bound, this);
4329 XMLNode &after = pl->get_state();
4330 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4336 if (!rv->region()->locked()) {
4337 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4338 XMLNode &before = pl->get_state();
4339 rv->region()->trim_end (new_bound, this);
4340 XMLNode &after = pl->get_state();
4341 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4345 commit_reversible_command();
4354 Editor::thaw_region_after_trim (RegionView& rv)
4356 boost::shared_ptr<Region> region (rv.region());
4358 if (region->locked()) {
4362 region->thaw (_("trimmed region"));
4363 XMLNode &after = region->playlist()->get_state();
4364 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4366 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4368 arv->unhide_envelope ();
4372 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4377 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4378 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4382 Location* location = find_location_from_marker (marker, is_start);
4383 location->set_hidden (true, this);
4388 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4394 drag_info.item = item;
4395 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4396 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4398 range_marker_op = op;
4400 if (!temp_location) {
4401 temp_location = new Location;
4405 case CreateRangeMarker:
4406 case CreateTransportMarker:
4408 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4409 drag_info.copy = true;
4411 drag_info.copy = false;
4413 start_grab (event, selector_cursor);
4417 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4422 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4424 nframes_t start = 0;
4426 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4428 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4429 snap_to (drag_info.current_pointer_frame);
4432 /* only alter selection if the current frame is
4433 different from the last frame position.
4436 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4438 switch (range_marker_op) {
4439 case CreateRangeMarker:
4440 case CreateTransportMarker:
4441 if (drag_info.first_move) {
4442 snap_to (drag_info.grab_frame);
4445 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4446 start = drag_info.current_pointer_frame;
4447 end = drag_info.grab_frame;
4449 end = drag_info.current_pointer_frame;
4450 start = drag_info.grab_frame;
4453 /* first drag: Either add to the selection
4454 or create a new selection.
4457 if (drag_info.first_move) {
4459 temp_location->set (start, end);
4463 update_marker_drag_item (temp_location);
4464 range_marker_drag_rect->show();
4465 range_marker_drag_rect->raise_to_top();
4471 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4472 start_canvas_autoscroll (1);
4476 temp_location->set (start, end);
4478 double x1 = frame_to_pixel (start);
4479 double x2 = frame_to_pixel (end);
4480 crect->property_x1() = x1;
4481 crect->property_x2() = x2;
4483 update_marker_drag_item (temp_location);
4486 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4487 drag_info.first_move = false;
4489 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4494 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4496 Location * newloc = 0;
4499 if (!drag_info.first_move) {
4500 drag_range_markerbar_op (item, event);
4502 switch (range_marker_op) {
4503 case CreateRangeMarker:
4505 begin_reversible_command (_("new range marker"));
4506 XMLNode &before = session->locations()->get_state();
4507 session->locations()->next_available_name(rangename,"unnamed");
4508 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4509 session->locations()->add (newloc, true);
4510 XMLNode &after = session->locations()->get_state();
4511 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4512 commit_reversible_command ();
4514 range_bar_drag_rect->hide();
4515 range_marker_drag_rect->hide();
4519 case CreateTransportMarker:
4520 // popup menu to pick loop or punch
4521 new_transport_marker_context_menu (&event->button, item);
4526 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4528 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4533 start = session->locations()->first_mark_before (drag_info.grab_frame);
4534 end = session->locations()->first_mark_after (drag_info.grab_frame);
4536 if (end == max_frames) {
4537 end = session->current_end_frame ();
4541 start = session->current_start_frame ();
4544 switch (mouse_mode) {
4546 /* find the two markers on either side and then make the selection from it */
4547 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4551 /* find the two markers on either side of the click and make the range out of it */
4552 selection->set (0, start, end);
4561 stop_canvas_autoscroll ();
4567 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4569 drag_info.item = item;
4570 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4571 drag_info.finished_callback = &Editor::end_mouse_zoom;
4573 start_grab (event, zoom_cursor);
4575 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4579 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4584 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4585 snap_to (drag_info.current_pointer_frame);
4587 if (drag_info.first_move) {
4588 snap_to (drag_info.grab_frame);
4592 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4594 /* base start and end on initial click position */
4595 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4596 start = drag_info.current_pointer_frame;
4597 end = drag_info.grab_frame;
4599 end = drag_info.current_pointer_frame;
4600 start = drag_info.grab_frame;
4605 if (drag_info.first_move) {
4607 zoom_rect->raise_to_top();
4610 reposition_zoom_rect(start, end);
4612 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4613 drag_info.first_move = false;
4615 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4620 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4622 if (!drag_info.first_move) {
4623 drag_mouse_zoom (item, event);
4625 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4626 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4628 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4631 temporal_zoom_to_frame (false, drag_info.grab_frame);
4633 temporal_zoom_step (false);
4634 center_screen (drag_info.grab_frame);
4642 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4644 double x1 = frame_to_pixel (start);
4645 double x2 = frame_to_pixel (end);
4646 double y2 = full_canvas_height - 1.0;
4648 zoom_rect->property_x1() = x1;
4649 zoom_rect->property_y1() = 1.0;
4650 zoom_rect->property_x2() = x2;
4651 zoom_rect->property_y2() = y2;
4655 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4657 drag_info.item = item;
4658 drag_info.motion_callback = &Editor::drag_rubberband_select;
4659 drag_info.finished_callback = &Editor::end_rubberband_select;
4661 start_grab (event, cross_hair_cursor);
4663 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4667 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4674 /* use a bigger drag threshold than the default */
4676 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4680 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4681 if (drag_info.first_move) {
4682 snap_to (drag_info.grab_frame);
4684 snap_to (drag_info.current_pointer_frame);
4687 /* base start and end on initial click position */
4689 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4690 start = drag_info.current_pointer_frame;
4691 end = drag_info.grab_frame;
4693 end = drag_info.current_pointer_frame;
4694 start = drag_info.grab_frame;
4697 if (drag_info.current_pointer_y < drag_info.grab_y) {
4698 y1 = drag_info.current_pointer_y;
4699 y2 = drag_info.grab_y;
4701 y2 = drag_info.current_pointer_y;
4702 y1 = drag_info.grab_y;
4706 if (start != end || y1 != y2) {
4708 double x1 = frame_to_pixel (start);
4709 double x2 = frame_to_pixel (end);
4711 rubberband_rect->property_x1() = x1;
4712 rubberband_rect->property_y1() = y1;
4713 rubberband_rect->property_x2() = x2;
4714 rubberband_rect->property_y2() = y2;
4716 rubberband_rect->show();
4717 rubberband_rect->raise_to_top();
4719 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4720 drag_info.first_move = false;
4722 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4727 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4729 if (!drag_info.first_move) {
4731 drag_rubberband_select (item, event);
4734 if (drag_info.current_pointer_y < drag_info.grab_y) {
4735 y1 = drag_info.current_pointer_y;
4736 y2 = drag_info.grab_y;
4739 y2 = drag_info.current_pointer_y;
4740 y1 = drag_info.grab_y;
4744 Selection::Operation op = Keyboard::selection_type (event->button.state);
4747 begin_reversible_command (_("rubberband selection"));
4749 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4750 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4752 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4756 commit_reversible_command ();
4760 selection->clear_tracks();
4761 selection->clear_regions();
4762 selection->clear_points ();
4763 selection->clear_lines ();
4766 rubberband_rect->hide();
4771 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4773 using namespace Gtkmm2ext;
4775 ArdourPrompter prompter (false);
4777 prompter.set_prompt (_("Name for region:"));
4778 prompter.set_initial_text (clicked_regionview->region()->name());
4779 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4780 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4781 prompter.show_all ();
4782 switch (prompter.run ()) {
4783 case Gtk::RESPONSE_ACCEPT:
4785 prompter.get_result(str);
4787 clicked_regionview->region()->set_name (str);
4795 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4797 drag_info.item = item;
4798 drag_info.motion_callback = &Editor::time_fx_motion;
4799 drag_info.finished_callback = &Editor::end_time_fx;
4803 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4807 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4809 RegionView* rv = clicked_regionview;
4811 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4812 snap_to (drag_info.current_pointer_frame);
4815 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4819 if (drag_info.current_pointer_frame > rv->region()->position()) {
4820 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4823 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4824 drag_info.first_move = false;
4826 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4830 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4832 clicked_regionview->get_time_axis_view().hide_timestretch ();
4834 if (drag_info.first_move) {
4838 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4839 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4841 begin_reversible_command (_("timestretch"));
4843 if (run_timestretch (selection->regions, percentage) == 0) {
4844 session->commit_reversible_command ();
4849 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4851 /* no brushing without a useful snap setting */
4854 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4857 switch (snap_mode) {
4859 return; /* can't work because it allows region to be placed anywhere */
4864 switch (snap_type) {
4867 case SnapToEditCursor:
4874 /* don't brush a copy over the original */
4876 if (pos == rv->region()->position()) {
4880 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4882 if (atv == 0 || !atv->is_audio_track()) {
4886 boost::shared_ptr<Playlist> playlist = atv->playlist();
4887 double speed = atv->get_diskstream()->speed();
4889 XMLNode &before = playlist->get_state();
4890 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4891 XMLNode &after = playlist->get_state();
4892 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4894 // playlist is frozen, so we have to update manually
4896 playlist->Modified(); /* EMIT SIGNAL */
4900 Editor::track_height_step_timeout ()
4903 struct timeval delta;
4905 gettimeofday (&now, 0);
4906 timersub (&now, &last_track_height_step_timestamp, &delta);
4908 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4909 current_stepping_trackview = 0;