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"
37 #include "midi_region_view.h"
39 #include "streamview.h"
40 #include "region_gain_line.h"
41 #include "automation_time_axis.h"
44 #include "selection.h"
47 #include "rgb_macros.h"
49 #include <ardour/types.h>
50 #include <ardour/profile.h>
51 #include <ardour/route.h>
52 #include <ardour/audio_track.h>
53 #include <ardour/audio_diskstream.h>
54 #include <ardour/playlist.h>
55 #include <ardour/audioplaylist.h>
56 #include <ardour/audioregion.h>
57 #include <ardour/midi_region.h>
58 #include <ardour/dB.h>
59 #include <ardour/utils.h>
60 #include <ardour/region_factory.h>
67 using namespace ARDOUR;
71 using namespace Editing;
74 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
88 switch (event->type) {
89 case GDK_BUTTON_RELEASE:
90 case GDK_BUTTON_PRESS:
91 case GDK_2BUTTON_PRESS:
92 case GDK_3BUTTON_PRESS:
93 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
95 case GDK_MOTION_NOTIFY:
96 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
98 case GDK_ENTER_NOTIFY:
99 case GDK_LEAVE_NOTIFY:
100 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
103 case GDK_KEY_RELEASE:
104 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
107 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
111 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
112 position is negative (as can be the case with motion events in particular),
113 the frame location is always positive.
116 return pixel_to_frame (*pcx);
120 Editor::mouse_mode_toggled (MouseMode m)
122 if (ignore_mouse_mode_toggle) {
128 if (mouse_select_button.get_active()) {
134 if (mouse_move_button.get_active()) {
140 if (mouse_gain_button.get_active()) {
146 if (mouse_zoom_button.get_active()) {
152 if (mouse_timefx_button.get_active()) {
158 if (mouse_audition_button.get_active()) {
169 Editor::set_mouse_mode (MouseMode m, bool force)
171 if (drag_info.item) {
175 if (!force && m == mouse_mode) {
183 if (mouse_mode != MouseRange) {
185 /* in all modes except range, hide the range selection,
186 show the object (region) selection.
189 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
190 (*i)->set_should_show_selection (true);
192 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
193 (*i)->hide_selection ();
199 in range mode,show the range selection.
202 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
203 if ((*i)->get_selected()) {
204 (*i)->show_selection (selection->time);
209 /* XXX the hack of unsetting all other buttongs should go
210 away once GTK2 allows us to use regular radio buttons drawn like
211 normal buttons, rather than my silly GroupedButton hack.
214 ignore_mouse_mode_toggle = true;
216 switch (mouse_mode) {
218 mouse_select_button.set_active (true);
219 current_canvas_cursor = selector_cursor;
223 mouse_move_button.set_active (true);
224 current_canvas_cursor = grabber_cursor;
228 mouse_gain_button.set_active (true);
229 current_canvas_cursor = cross_hair_cursor;
233 mouse_zoom_button.set_active (true);
234 current_canvas_cursor = zoom_cursor;
238 mouse_timefx_button.set_active (true);
239 current_canvas_cursor = time_fx_cursor; // just use playhead
243 mouse_audition_button.set_active (true);
244 current_canvas_cursor = speaker_cursor;
248 ignore_mouse_mode_toggle = false;
251 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
256 Editor::step_mouse_mode (bool next)
258 switch (current_mouse_mode()) {
260 if (next) set_mouse_mode (MouseRange);
261 else set_mouse_mode (MouseTimeFX);
265 if (next) set_mouse_mode (MouseZoom);
266 else set_mouse_mode (MouseObject);
270 if (next) set_mouse_mode (MouseGain);
271 else set_mouse_mode (MouseRange);
275 if (next) set_mouse_mode (MouseTimeFX);
276 else set_mouse_mode (MouseZoom);
280 if (next) set_mouse_mode (MouseAudition);
281 else set_mouse_mode (MouseGain);
285 if (next) set_mouse_mode (MouseObject);
286 else set_mouse_mode (MouseTimeFX);
292 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
296 /* in object/audition/timefx mode, any button press sets
297 the selection if the object can be selected. this is a
298 bit of hack, because we want to avoid this if the
299 mouse operation is a region alignment.
301 note: not dbl-click or triple-click
304 if (((mouse_mode != MouseObject) &&
305 (mouse_mode != MouseAudition || item_type != RegionItem) &&
306 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
307 (mouse_mode != MouseRange)) ||
309 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
314 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
316 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
318 /* almost no selection action on modified button-2 or button-3 events */
320 if (item_type != RegionItem && event->button.button != 2) {
326 Selection::Operation op = Keyboard::selection_type (event->button.state);
327 bool press = (event->type == GDK_BUTTON_PRESS);
329 // begin_reversible_command (_("select on click"));
333 if (mouse_mode != MouseRange) {
334 commit = set_selected_regionview_from_click (press, op, true);
335 } else if (event->type == GDK_BUTTON_PRESS) {
336 commit = set_selected_track_from_click (press, op, false);
340 case RegionViewNameHighlight:
342 if (mouse_mode != MouseRange) {
343 commit = set_selected_regionview_from_click (press, op, true);
344 } else if (event->type == GDK_BUTTON_PRESS) {
345 commit = set_selected_track_from_click (press, op, false);
349 case FadeInHandleItem:
351 case FadeOutHandleItem:
353 if (mouse_mode != MouseRange) {
354 commit = set_selected_regionview_from_click (press, op, true);
355 } else if (event->type == GDK_BUTTON_PRESS) {
356 commit = set_selected_track_from_click (press, op, false);
360 case GainAutomationControlPointItem:
361 case PanAutomationControlPointItem:
362 case RedirectAutomationControlPointItem:
363 commit = set_selected_track_from_click (press, op, true);
364 if (mouse_mode != MouseRange) {
365 commit |= set_selected_control_point_from_click (op, false);
370 /* for context click or range selection, select track */
371 if (event->button.button == 3) {
372 commit = set_selected_track_from_click (press, op, true);
373 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
374 commit = set_selected_track_from_click (press, op, false);
378 case AutomationTrackItem:
379 commit = set_selected_track_from_click (press, op, true);
387 // commit_reversible_command ();
392 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
394 track_canvas.grab_focus();
396 if (session && session->actively_recording()) {
400 button_selection (item, event, item_type);
402 if (drag_info.item == 0 &&
403 (Keyboard::is_delete_event (&event->button) ||
404 Keyboard::is_context_menu_event (&event->button) ||
405 Keyboard::is_edit_event (&event->button))) {
407 /* handled by button release */
411 switch (event->button.button) {
414 if (event->type == GDK_BUTTON_PRESS) {
416 if (drag_info.item) {
417 drag_info.item->ungrab (event->button.time);
420 /* single mouse clicks on any of these item types operate
421 independent of mouse mode, mostly because they are
422 not on the main track canvas or because we want
428 case PlayheadCursorItem:
429 start_cursor_grab (item, event);
433 if (Keyboard::modifier_state_equals (event->button.state,
434 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
435 hide_marker (item, event);
437 start_marker_grab (item, event);
441 case TempoMarkerItem:
442 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
443 start_tempo_marker_copy_grab (item, event);
445 start_tempo_marker_grab (item, event);
449 case MeterMarkerItem:
450 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
451 start_meter_marker_copy_grab (item, event);
453 start_meter_marker_grab (item, event);
463 case RangeMarkerBarItem:
464 start_range_markerbar_op (item, event, CreateRangeMarker);
468 case TransportMarkerBarItem:
469 start_range_markerbar_op (item, event, CreateTransportMarker);
478 switch (mouse_mode) {
481 case StartSelectionTrimItem:
482 start_selection_op (item, event, SelectionStartTrim);
485 case EndSelectionTrimItem:
486 start_selection_op (item, event, SelectionEndTrim);
490 if (Keyboard::modifier_state_contains
491 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
492 // contains and not equals because I can't use alt as a modifier alone.
493 start_selection_grab (item, event);
494 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
495 /* grab selection for moving */
496 start_selection_op (item, event, SelectionMove);
499 /* this was debated, but decided the more common action was to
500 make a new selection */
501 start_selection_op (item, event, CreateSelection);
506 start_selection_op (item, event, CreateSelection);
512 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
513 event->type == GDK_BUTTON_PRESS) {
515 start_rubberband_select (item, event);
517 } else if (event->type == GDK_BUTTON_PRESS) {
520 case FadeInHandleItem:
521 start_fade_in_grab (item, event);
524 case FadeOutHandleItem:
525 start_fade_out_grab (item, event);
529 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
530 start_region_copy_grab (item, event);
531 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
532 start_region_brush_grab (item, event);
534 start_region_grab (item, event);
538 case RegionViewNameHighlight:
539 start_trim (item, event);
544 /* rename happens on edit clicks */
545 start_trim (clicked_regionview->get_name_highlight(), event);
549 case GainAutomationControlPointItem:
550 case PanAutomationControlPointItem:
551 case RedirectAutomationControlPointItem:
552 start_control_point_grab (item, event);
556 case GainAutomationLineItem:
557 case PanAutomationLineItem:
558 case RedirectAutomationLineItem:
559 start_line_grab_from_line (item, event);
564 case AutomationTrackItem:
565 start_rubberband_select (item, event);
568 /* <CMT Additions> */
569 case ImageFrameHandleStartItem:
570 imageframe_start_handle_op(item, event) ;
573 case ImageFrameHandleEndItem:
574 imageframe_end_handle_op(item, event) ;
577 case MarkerViewHandleStartItem:
578 markerview_item_start_handle_op(item, event) ;
581 case MarkerViewHandleEndItem:
582 markerview_item_end_handle_op(item, event) ;
585 /* </CMT Additions> */
587 /* <CMT Additions> */
589 start_markerview_grab(item, event) ;
592 start_imageframe_grab(item, event) ;
594 /* </CMT Additions> */
610 // start_line_grab_from_regionview (item, event);
613 case GainControlPointItem:
614 start_control_point_grab (item, event);
618 start_line_grab_from_line (item, event);
621 case GainAutomationControlPointItem:
622 case PanAutomationControlPointItem:
623 case RedirectAutomationControlPointItem:
624 start_control_point_grab (item, event);
635 case GainAutomationControlPointItem:
636 case PanAutomationControlPointItem:
637 case RedirectAutomationControlPointItem:
638 start_control_point_grab (item, event);
641 case GainAutomationLineItem:
642 case PanAutomationLineItem:
643 case RedirectAutomationLineItem:
644 start_line_grab_from_line (item, event);
648 // XXX need automation mode to identify which
650 // start_line_grab_from_regionview (item, event);
660 if (event->type == GDK_BUTTON_PRESS) {
661 start_mouse_zoom (item, event);
668 if (item_type == RegionItem) {
669 start_time_fx (item, event);
674 /* handled in release */
683 switch (mouse_mode) {
685 if (event->type == GDK_BUTTON_PRESS) {
688 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
689 start_region_copy_grab (item, event);
691 start_region_grab (item, event);
695 case GainAutomationControlPointItem:
696 case PanAutomationControlPointItem:
697 case RedirectAutomationControlPointItem:
698 start_control_point_grab (item, event);
709 case RegionViewNameHighlight:
710 start_trim (item, event);
715 start_trim (clicked_regionview->get_name_highlight(), event);
726 if (event->type == GDK_BUTTON_PRESS) {
727 /* relax till release */
734 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
735 temporal_zoom_session();
737 temporal_zoom_to_frame (true, event_frame(event));
760 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
762 nframes_t where = event_frame (event, 0, 0);
764 /* no action if we're recording */
766 if (session && session->actively_recording()) {
770 /* first, see if we're finishing a drag ... */
772 if (drag_info.item) {
773 if (end_grab (item, event)) {
774 /* grab dragged, so do nothing else */
779 button_selection (item, event, item_type);
781 /* edit events get handled here */
783 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
789 case TempoMarkerItem:
790 edit_tempo_marker (item);
793 case MeterMarkerItem:
794 edit_meter_marker (item);
798 if (clicked_regionview->name_active()) {
799 return mouse_rename_region (item, event);
809 /* context menu events get handled here */
811 if (Keyboard::is_context_menu_event (&event->button)) {
813 if (drag_info.item == 0) {
815 /* no matter which button pops up the context menu, tell the menu
816 widget to use button 1 to drive menu selection.
821 case FadeInHandleItem:
823 case FadeOutHandleItem:
824 popup_fade_context_menu (1, event->button.time, item, item_type);
828 popup_track_context_menu (1, event->button.time, item_type, false, where);
832 case RegionViewNameHighlight:
834 popup_track_context_menu (1, event->button.time, item_type, false, where);
838 popup_track_context_menu (1, event->button.time, item_type, true, where);
841 case AutomationTrackItem:
842 popup_track_context_menu (1, event->button.time, item_type, false, where);
846 case RangeMarkerBarItem:
847 case TransportMarkerBarItem:
850 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
854 marker_context_menu (&event->button, item);
857 case TempoMarkerItem:
858 tm_marker_context_menu (&event->button, item);
861 case MeterMarkerItem:
862 tm_marker_context_menu (&event->button, item);
865 case CrossfadeViewItem:
866 popup_track_context_menu (1, event->button.time, item_type, false, where);
869 /* <CMT Additions> */
871 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
873 case ImageFrameTimeAxisItem:
874 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
877 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
879 case MarkerTimeAxisItem:
880 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
882 /* <CMT Additions> */
893 /* delete events get handled here */
895 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
898 case TempoMarkerItem:
899 remove_tempo_marker (item);
902 case MeterMarkerItem:
903 remove_meter_marker (item);
907 remove_marker (*item, event);
911 if (mouse_mode == MouseObject) {
912 remove_clicked_region ();
916 case GainControlPointItem:
917 if (mouse_mode == MouseGain) {
918 remove_gain_control_point (item, event);
922 case GainAutomationControlPointItem:
923 case PanAutomationControlPointItem:
924 case RedirectAutomationControlPointItem:
925 remove_control_point (item, event);
934 switch (event->button.button) {
938 /* see comments in button_press_handler */
940 case PlayheadCursorItem:
943 case GainAutomationLineItem:
944 case PanAutomationLineItem:
945 case RedirectAutomationLineItem:
946 case StartSelectionTrimItem:
947 case EndSelectionTrimItem:
951 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
952 snap_to (where, 0, true);
954 mouse_add_new_marker (where);
958 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
961 mouse_add_new_tempo_event (where);
965 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
973 switch (mouse_mode) {
976 case AutomationTrackItem:
977 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->add_automation_event
991 // Gain only makes sense for audio regions
993 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
999 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1003 case AutomationTrackItem:
1004 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1005 add_automation_event (item, event, where, event->button.y);
1014 switch (item_type) {
1016 audition_selected_region ();
1033 switch (mouse_mode) {
1036 switch (item_type) {
1038 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1040 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1043 // Button2 click is unused
1056 // x_style_paste (where, 1.0);
1076 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1082 switch (item_type) {
1083 case GainControlPointItem:
1084 if (mouse_mode == MouseGain) {
1085 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1086 cp->set_visible (true);
1090 at_y = cp->get_y ();
1091 cp->item->i2w (at_x, at_y);
1095 fraction = 1.0 - (cp->get_y() / cp->line.height());
1097 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1098 show_verbose_canvas_cursor ();
1100 if (is_drawable()) {
1101 track_canvas.get_window()->set_cursor (*fader_cursor);
1106 case GainAutomationControlPointItem:
1107 case PanAutomationControlPointItem:
1108 case RedirectAutomationControlPointItem:
1109 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1110 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1111 cp->set_visible (true);
1115 at_y = cp->get_y ();
1116 cp->item->i2w (at_x, at_y);
1120 fraction = 1.0 - (cp->get_y() / cp->line.height());
1122 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1123 show_verbose_canvas_cursor ();
1125 if (is_drawable()) {
1126 track_canvas.get_window()->set_cursor (*fader_cursor);
1132 if (mouse_mode == MouseGain) {
1133 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1135 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1136 if (is_drawable()) {
1137 track_canvas.get_window()->set_cursor (*fader_cursor);
1142 case GainAutomationLineItem:
1143 case RedirectAutomationLineItem:
1144 case PanAutomationLineItem:
1145 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1147 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1149 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1151 if (is_drawable()) {
1152 track_canvas.get_window()->set_cursor (*fader_cursor);
1157 case RegionViewNameHighlight:
1158 if (is_drawable() && mouse_mode == MouseObject) {
1159 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1163 case StartSelectionTrimItem:
1164 case EndSelectionTrimItem:
1165 /* <CMT Additions> */
1166 case ImageFrameHandleStartItem:
1167 case ImageFrameHandleEndItem:
1168 case MarkerViewHandleStartItem:
1169 case MarkerViewHandleEndItem:
1170 /* </CMT Additions> */
1172 if (is_drawable()) {
1173 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1177 case EditCursorItem:
1178 case PlayheadCursorItem:
1179 if (is_drawable()) {
1180 track_canvas.get_window()->set_cursor (*grabber_cursor);
1184 case RegionViewName:
1186 /* when the name is not an active item, the entire name highlight is for trimming */
1188 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1189 if (mouse_mode == MouseObject && is_drawable()) {
1190 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1196 case AutomationTrackItem:
1197 if (is_drawable()) {
1198 Gdk::Cursor *cursor;
1199 switch (mouse_mode) {
1201 cursor = selector_cursor;
1204 cursor = zoom_cursor;
1207 cursor = cross_hair_cursor;
1211 track_canvas.get_window()->set_cursor (*cursor);
1213 AutomationTimeAxisView* atv;
1214 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1215 clear_entered_track = false;
1216 set_entered_track (atv);
1222 case RangeMarkerBarItem:
1223 case TransportMarkerBarItem:
1226 if (is_drawable()) {
1227 time_canvas.get_window()->set_cursor (*timebar_cursor);
1232 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1235 marker->set_color_rgba (color_map[cEnteredMarker]);
1237 case MeterMarkerItem:
1238 case TempoMarkerItem:
1239 if (is_drawable()) {
1240 time_canvas.get_window()->set_cursor (*timebar_cursor);
1243 case FadeInHandleItem:
1244 case FadeOutHandleItem:
1245 if (mouse_mode == MouseObject) {
1246 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1248 rect->property_fill_color_rgba() = 0;
1249 rect->property_outline_pixels() = 1;
1258 /* second pass to handle entered track status in a comprehensible way.
1261 switch (item_type) {
1263 case GainAutomationLineItem:
1264 case RedirectAutomationLineItem:
1265 case PanAutomationLineItem:
1266 case GainControlPointItem:
1267 case GainAutomationControlPointItem:
1268 case PanAutomationControlPointItem:
1269 case RedirectAutomationControlPointItem:
1270 /* these do not affect the current entered track state */
1271 clear_entered_track = false;
1274 case AutomationTrackItem:
1275 /* handled above already */
1279 set_entered_track (0);
1287 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1296 switch (item_type) {
1297 case GainControlPointItem:
1298 case GainAutomationControlPointItem:
1299 case PanAutomationControlPointItem:
1300 case RedirectAutomationControlPointItem:
1301 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1302 if (cp->line.npoints() > 1) {
1303 if (!cp->selected) {
1304 cp->set_visible (false);
1308 if (is_drawable()) {
1309 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1312 hide_verbose_canvas_cursor ();
1315 case RegionViewNameHighlight:
1316 case StartSelectionTrimItem:
1317 case EndSelectionTrimItem:
1318 case EditCursorItem:
1319 case PlayheadCursorItem:
1320 /* <CMT Additions> */
1321 case ImageFrameHandleStartItem:
1322 case ImageFrameHandleEndItem:
1323 case MarkerViewHandleStartItem:
1324 case MarkerViewHandleEndItem:
1325 /* </CMT Additions> */
1326 if (is_drawable()) {
1327 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1332 case GainAutomationLineItem:
1333 case RedirectAutomationLineItem:
1334 case PanAutomationLineItem:
1335 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1337 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1339 line->property_fill_color_rgba() = al->get_line_color();
1341 if (is_drawable()) {
1342 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1346 case RegionViewName:
1347 /* see enter_handler() for notes */
1348 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1349 if (is_drawable() && mouse_mode == MouseObject) {
1350 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1355 case RangeMarkerBarItem:
1356 case TransportMarkerBarItem:
1360 if (is_drawable()) {
1361 time_canvas.get_window()->set_cursor (*timebar_cursor);
1366 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1369 loc = find_location_from_marker (marker, is_start);
1370 if (loc) location_flags_changed (loc, this);
1372 case MeterMarkerItem:
1373 case TempoMarkerItem:
1375 if (is_drawable()) {
1376 time_canvas.get_window()->set_cursor (*timebar_cursor);
1381 case FadeInHandleItem:
1382 case FadeOutHandleItem:
1383 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1385 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1387 rect->property_fill_color_rgba() = rv->get_fill_color();
1388 rect->property_outline_pixels() = 0;
1393 case AutomationTrackItem:
1394 if (is_drawable()) {
1395 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1396 clear_entered_track = true;
1397 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1409 Editor::left_automation_track ()
1411 if (clear_entered_track) {
1412 set_entered_track (0);
1413 clear_entered_track = false;
1419 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1423 /* We call this so that MOTION_NOTIFY events continue to be
1424 delivered to the canvas. We need to do this because we set
1425 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1426 the density of the events, at the expense of a round-trip
1427 to the server. Given that this will mostly occur on cases
1428 where DISPLAY = :0.0, and given the cost of what the motion
1429 event might do, its a good tradeoff.
1432 track_canvas.get_pointer (x, y);
1434 if (current_stepping_trackview) {
1435 /* don't keep the persistent stepped trackview if the mouse moves */
1436 current_stepping_trackview = 0;
1437 step_timeout.disconnect ();
1440 if (session && session->actively_recording()) {
1441 /* Sorry. no dragging stuff around while we record */
1445 drag_info.item_type = item_type;
1446 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1447 &drag_info.current_pointer_y);
1449 if (!from_autoscroll && drag_info.item) {
1450 /* item != 0 is the best test i can think of for dragging.
1452 if (!drag_info.move_threshold_passed) {
1454 bool x_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1455 bool y_threshold_passed = (abs ((int) (drag_info.current_pointer_y - drag_info.grab_y)) > 4);
1457 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1459 // and change the initial grab loc/frame if this drag info wants us to
1461 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1462 drag_info.grab_frame = drag_info.current_pointer_frame;
1463 drag_info.grab_x = drag_info.current_pointer_x;
1464 drag_info.grab_y = drag_info.current_pointer_y;
1465 drag_info.last_pointer_frame = drag_info.grab_frame;
1466 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1471 switch (item_type) {
1472 case PlayheadCursorItem:
1473 case EditCursorItem:
1475 case GainControlPointItem:
1476 case RedirectAutomationControlPointItem:
1477 case GainAutomationControlPointItem:
1478 case PanAutomationControlPointItem:
1479 case TempoMarkerItem:
1480 case MeterMarkerItem:
1481 case RegionViewNameHighlight:
1482 case StartSelectionTrimItem:
1483 case EndSelectionTrimItem:
1486 case RedirectAutomationLineItem:
1487 case GainAutomationLineItem:
1488 case PanAutomationLineItem:
1489 case FadeInHandleItem:
1490 case FadeOutHandleItem:
1491 /* <CMT Additions> */
1492 case ImageFrameHandleStartItem:
1493 case ImageFrameHandleEndItem:
1494 case MarkerViewHandleStartItem:
1495 case MarkerViewHandleEndItem:
1496 /* </CMT Additions> */
1497 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1498 (event->motion.state & Gdk::BUTTON2_MASK))) {
1499 if (!from_autoscroll) {
1500 maybe_autoscroll (event);
1502 (this->*(drag_info.motion_callback)) (item, event);
1511 switch (mouse_mode) {
1516 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1517 (event->motion.state & GDK_BUTTON2_MASK))) {
1518 if (!from_autoscroll) {
1519 maybe_autoscroll (event);
1521 (this->*(drag_info.motion_callback)) (item, event);
1532 track_canvas_motion (event);
1533 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1541 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1543 if (drag_info.item == 0) {
1544 fatal << _("programming error: start_grab called without drag item") << endmsg;
1550 cursor = grabber_cursor;
1553 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1555 if (event->button.button == 2) {
1556 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1557 drag_info.y_constrained = true;
1558 drag_info.x_constrained = false;
1560 drag_info.y_constrained = false;
1561 drag_info.x_constrained = true;
1564 drag_info.x_constrained = false;
1565 drag_info.y_constrained = false;
1568 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1569 drag_info.last_pointer_frame = drag_info.grab_frame;
1570 drag_info.current_pointer_frame = drag_info.grab_frame;
1571 drag_info.current_pointer_x = drag_info.grab_x;
1572 drag_info.current_pointer_y = drag_info.grab_y;
1573 drag_info.cumulative_x_drag = 0;
1574 drag_info.cumulative_y_drag = 0;
1575 drag_info.first_move = true;
1576 drag_info.move_threshold_passed = false;
1577 drag_info.want_move_threshold = false;
1578 drag_info.pointer_frame_offset = 0;
1579 drag_info.brushing = false;
1580 drag_info.copied_location = 0;
1582 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1584 event->button.time);
1586 if (session && session->transport_rolling()) {
1587 drag_info.was_rolling = true;
1589 drag_info.was_rolling = false;
1592 switch (snap_type) {
1593 case SnapToRegionStart:
1594 case SnapToRegionEnd:
1595 case SnapToRegionSync:
1596 case SnapToRegionBoundary:
1597 build_region_boundary_cache ();
1605 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1607 drag_info.item->ungrab (0);
1608 drag_info.item = new_item;
1611 cursor = grabber_cursor;
1614 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1618 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1620 bool did_drag = false;
1622 stop_canvas_autoscroll ();
1624 if (drag_info.item == 0) {
1628 drag_info.item->ungrab (event->button.time);
1630 if (drag_info.finished_callback) {
1631 (this->*(drag_info.finished_callback)) (item, event);
1634 did_drag = !drag_info.first_move;
1636 hide_verbose_canvas_cursor();
1639 drag_info.copy = false;
1640 drag_info.motion_callback = 0;
1641 drag_info.finished_callback = 0;
1642 drag_info.last_trackview = 0;
1643 drag_info.last_frame_position = 0;
1644 drag_info.grab_frame = 0;
1645 drag_info.last_pointer_frame = 0;
1646 drag_info.current_pointer_frame = 0;
1647 drag_info.brushing = false;
1649 if (drag_info.copied_location) {
1650 delete drag_info.copied_location;
1651 drag_info.copied_location = 0;
1658 Editor::set_edit_cursor (GdkEvent* event)
1660 nframes_t pointer_frame = event_frame (event);
1662 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1663 if (snap_type != SnapToEditCursor) {
1664 snap_to (pointer_frame);
1668 edit_cursor->set_position (pointer_frame);
1669 edit_cursor_clock.set (pointer_frame);
1673 Editor::set_playhead_cursor (GdkEvent* event)
1675 nframes_t pointer_frame = event_frame (event);
1677 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1678 snap_to (pointer_frame);
1682 session->request_locate (pointer_frame, session->transport_rolling());
1687 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1689 drag_info.item = item;
1690 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1691 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1695 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1696 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1700 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1702 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1706 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1708 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1710 nframes_t fade_length;
1712 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1713 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1719 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1723 if (pos < (arv->region()->position() + 64)) {
1724 fade_length = 64; // this should be a minimum defined somewhere
1725 } else if (pos > arv->region()->last_frame()) {
1726 fade_length = arv->region()->length();
1728 fade_length = pos - arv->region()->position();
1730 /* mapover the region selection */
1732 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1734 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1740 tmp->reset_fade_in_shape_width (fade_length);
1743 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1745 drag_info.first_move = false;
1749 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1751 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1753 nframes_t fade_length;
1755 if (drag_info.first_move) return;
1757 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1758 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1763 if (pos < (arv->region()->position() + 64)) {
1764 fade_length = 64; // this should be a minimum defined somewhere
1765 } else if (pos > arv->region()->last_frame()) {
1766 fade_length = arv->region()->length();
1768 fade_length = pos - arv->region()->position();
1771 begin_reversible_command (_("change fade in length"));
1773 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1775 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1781 AutomationList& alist = tmp->audio_region()->fade_in();
1782 XMLNode &before = alist.get_state();
1784 tmp->audio_region()->set_fade_in_length (fade_length);
1786 XMLNode &after = alist.get_state();
1787 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1790 commit_reversible_command ();
1794 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1796 drag_info.item = item;
1797 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1798 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1802 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1803 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1807 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1809 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1813 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1815 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1817 nframes_t fade_length;
1819 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1820 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1826 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1830 if (pos > (arv->region()->last_frame() - 64)) {
1831 fade_length = 64; // this should really be a minimum fade defined somewhere
1833 else if (pos < arv->region()->position()) {
1834 fade_length = arv->region()->length();
1837 fade_length = arv->region()->last_frame() - pos;
1840 /* mapover the region selection */
1842 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1844 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1850 tmp->reset_fade_out_shape_width (fade_length);
1853 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1855 drag_info.first_move = false;
1859 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1861 if (drag_info.first_move) return;
1863 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1865 nframes_t fade_length;
1867 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1868 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1874 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1878 if (pos > (arv->region()->last_frame() - 64)) {
1879 fade_length = 64; // this should really be a minimum fade defined somewhere
1881 else if (pos < arv->region()->position()) {
1882 fade_length = arv->region()->length();
1885 fade_length = arv->region()->last_frame() - pos;
1888 begin_reversible_command (_("change fade out length"));
1890 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1892 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1898 AutomationList& alist = tmp->audio_region()->fade_out();
1899 XMLNode &before = alist.get_state();
1901 tmp->audio_region()->set_fade_out_length (fade_length);
1903 XMLNode &after = alist.get_state();
1904 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1907 commit_reversible_command ();
1911 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1913 drag_info.item = item;
1914 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1915 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1919 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1920 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1924 Cursor* cursor = (Cursor *) drag_info.data;
1926 if (cursor == playhead_cursor) {
1927 _dragging_playhead = true;
1929 if (session && drag_info.was_rolling) {
1930 session->request_stop ();
1933 if (session && session->is_auditioning()) {
1934 session->cancel_audition ();
1938 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1940 show_verbose_time_cursor (cursor->current_frame, 10);
1944 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1946 Cursor* cursor = (Cursor *) drag_info.data;
1947 nframes_t adjusted_frame;
1949 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1950 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1956 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1957 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1958 snap_to (adjusted_frame);
1962 if (adjusted_frame == drag_info.last_pointer_frame) return;
1964 cursor->set_position (adjusted_frame);
1966 if (cursor == edit_cursor) {
1967 edit_cursor_clock.set (cursor->current_frame);
1969 UpdateAllTransportClocks (cursor->current_frame);
1972 show_verbose_time_cursor (cursor->current_frame, 10);
1974 drag_info.last_pointer_frame = adjusted_frame;
1975 drag_info.first_move = false;
1979 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1981 if (drag_info.first_move) return;
1983 cursor_drag_motion_callback (item, event);
1985 _dragging_playhead = false;
1987 if (item == &playhead_cursor->canvas_item) {
1989 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1991 } else if (item == &edit_cursor->canvas_item) {
1992 edit_cursor->set_position (edit_cursor->current_frame);
1993 edit_cursor_clock.set (edit_cursor->current_frame);
1998 Editor::update_marker_drag_item (Location *location)
2000 double x1 = frame_to_pixel (location->start());
2001 double x2 = frame_to_pixel (location->end());
2003 if (location->is_mark()) {
2004 marker_drag_line_points.front().set_x(x1);
2005 marker_drag_line_points.back().set_x(x1);
2006 marker_drag_line->property_points() = marker_drag_line_points;
2009 range_marker_drag_rect->property_x1() = x1;
2010 range_marker_drag_rect->property_x2() = x2;
2015 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2019 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2020 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2026 Location *location = find_location_from_marker (marker, is_start);
2028 drag_info.item = item;
2029 drag_info.data = marker;
2030 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2031 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2035 drag_info.copied_location = new Location (*location);
2036 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2038 update_marker_drag_item (location);
2040 if (location->is_mark()) {
2041 marker_drag_line->show();
2042 marker_drag_line->raise_to_top();
2045 range_marker_drag_rect->show();
2046 range_marker_drag_rect->raise_to_top();
2049 if (is_start) show_verbose_time_cursor (location->start(), 10);
2050 else show_verbose_time_cursor (location->end(), 10);
2054 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2057 Marker* marker = (Marker *) drag_info.data;
2058 Location *real_location;
2059 Location *copy_location;
2061 bool move_both = false;
2065 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2066 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2072 nframes_t next = newframe;
2074 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2075 snap_to (newframe, 0, true);
2078 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2082 /* call this to find out if its the start or end */
2084 real_location = find_location_from_marker (marker, is_start);
2086 /* use the copy that we're "dragging" around */
2088 copy_location = drag_info.copied_location;
2090 f_delta = copy_location->end() - copy_location->start();
2092 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2096 if (copy_location->is_mark()) {
2099 copy_location->set_start (newframe);
2103 if (is_start) { // start-of-range marker
2106 copy_location->set_start (newframe);
2107 copy_location->set_end (newframe + f_delta);
2108 } else if (newframe < copy_location->end()) {
2109 copy_location->set_start (newframe);
2111 snap_to (next, 1, true);
2112 copy_location->set_end (next);
2113 copy_location->set_start (newframe);
2116 } else { // end marker
2119 copy_location->set_end (newframe);
2120 copy_location->set_start (newframe - f_delta);
2121 } else if (newframe > copy_location->start()) {
2122 copy_location->set_end (newframe);
2124 } else if (newframe > 0) {
2125 snap_to (next, -1, true);
2126 copy_location->set_start (next);
2127 copy_location->set_end (newframe);
2132 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2133 drag_info.first_move = false;
2135 update_marker_drag_item (copy_location);
2137 LocationMarkers* lm = find_location_markers (real_location);
2138 lm->set_position (copy_location->start(), copy_location->end());
2140 show_verbose_time_cursor (newframe, 10);
2144 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2146 if (drag_info.first_move) {
2147 marker_drag_motion_callback (item, event);
2151 Marker* marker = (Marker *) drag_info.data;
2155 begin_reversible_command ( _("move marker") );
2156 XMLNode &before = session->locations()->get_state();
2158 Location * location = find_location_from_marker (marker, is_start);
2161 if (location->is_mark()) {
2162 location->set_start (drag_info.copied_location->start());
2164 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2168 XMLNode &after = session->locations()->get_state();
2169 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2170 commit_reversible_command ();
2172 marker_drag_line->hide();
2173 range_marker_drag_rect->hide();
2177 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2180 MeterMarker* meter_marker;
2182 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2183 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2187 meter_marker = dynamic_cast<MeterMarker*> (marker);
2189 MetricSection& section (meter_marker->meter());
2191 if (!section.movable()) {
2195 drag_info.item = item;
2196 drag_info.data = marker;
2197 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2198 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2202 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2204 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2208 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2211 MeterMarker* meter_marker;
2213 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2214 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2218 meter_marker = dynamic_cast<MeterMarker*> (marker);
2220 // create a dummy marker for visual representation of moving the copy.
2221 // The actual copying is not done before we reach the finish callback.
2223 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2224 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2225 *new MeterSection(meter_marker->meter()));
2227 drag_info.item = &new_marker->the_item();
2228 drag_info.copy = true;
2229 drag_info.data = new_marker;
2230 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2231 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2235 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2237 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2241 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2243 MeterMarker* marker = (MeterMarker *) drag_info.data;
2244 nframes_t adjusted_frame;
2246 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2247 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2253 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2254 snap_to (adjusted_frame);
2257 if (adjusted_frame == drag_info.last_pointer_frame) return;
2259 marker->set_position (adjusted_frame);
2262 drag_info.last_pointer_frame = adjusted_frame;
2263 drag_info.first_move = false;
2265 show_verbose_time_cursor (adjusted_frame, 10);
2269 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2271 if (drag_info.first_move) return;
2273 meter_marker_drag_motion_callback (drag_info.item, event);
2275 MeterMarker* marker = (MeterMarker *) drag_info.data;
2278 TempoMap& map (session->tempo_map());
2279 map.bbt_time (drag_info.last_pointer_frame, when);
2281 if (drag_info.copy == true) {
2282 begin_reversible_command (_("copy meter mark"));
2283 XMLNode &before = map.get_state();
2284 map.add_meter (marker->meter(), when);
2285 XMLNode &after = map.get_state();
2286 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2287 commit_reversible_command ();
2289 // delete the dummy marker we used for visual representation of copying.
2290 // a new visual marker will show up automatically.
2293 begin_reversible_command (_("move meter mark"));
2294 XMLNode &before = map.get_state();
2295 map.move_meter (marker->meter(), when);
2296 XMLNode &after = map.get_state();
2297 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2298 commit_reversible_command ();
2303 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2306 TempoMarker* tempo_marker;
2308 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2309 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2313 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2314 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2318 MetricSection& section (tempo_marker->tempo());
2320 if (!section.movable()) {
2324 drag_info.item = item;
2325 drag_info.data = marker;
2326 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2327 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2331 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2332 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2336 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2339 TempoMarker* tempo_marker;
2341 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2342 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2346 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2347 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2351 // create a dummy marker for visual representation of moving the copy.
2352 // The actual copying is not done before we reach the finish callback.
2354 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2355 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2356 *new TempoSection(tempo_marker->tempo()));
2358 drag_info.item = &new_marker->the_item();
2359 drag_info.copy = true;
2360 drag_info.data = new_marker;
2361 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2362 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2366 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2368 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2372 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2374 TempoMarker* marker = (TempoMarker *) drag_info.data;
2375 nframes_t adjusted_frame;
2377 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2378 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2384 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2385 snap_to (adjusted_frame);
2388 if (adjusted_frame == drag_info.last_pointer_frame) return;
2390 /* OK, we've moved far enough to make it worth actually move the thing. */
2392 marker->set_position (adjusted_frame);
2394 show_verbose_time_cursor (adjusted_frame, 10);
2396 drag_info.last_pointer_frame = adjusted_frame;
2397 drag_info.first_move = false;
2401 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2403 if (drag_info.first_move) return;
2405 tempo_marker_drag_motion_callback (drag_info.item, event);
2407 TempoMarker* marker = (TempoMarker *) drag_info.data;
2410 TempoMap& map (session->tempo_map());
2411 map.bbt_time (drag_info.last_pointer_frame, when);
2413 if (drag_info.copy == true) {
2414 begin_reversible_command (_("copy tempo mark"));
2415 XMLNode &before = map.get_state();
2416 map.add_tempo (marker->tempo(), when);
2417 XMLNode &after = map.get_state();
2418 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2419 commit_reversible_command ();
2421 // delete the dummy marker we used for visual representation of copying.
2422 // a new visual marker will show up automatically.
2425 begin_reversible_command (_("move tempo mark"));
2426 XMLNode &before = map.get_state();
2427 map.move_tempo (marker->tempo(), when);
2428 XMLNode &after = map.get_state();
2429 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2430 commit_reversible_command ();
2435 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2437 ControlPoint* control_point;
2439 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2440 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2444 // We shouldn't remove the first or last gain point
2445 if (control_point->line.is_last_point(*control_point) ||
2446 control_point->line.is_first_point(*control_point)) {
2450 control_point->line.remove_point (*control_point);
2454 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2456 ControlPoint* control_point;
2458 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2459 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2463 control_point->line.remove_point (*control_point);
2467 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2469 ControlPoint* control_point;
2471 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2472 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2476 drag_info.item = item;
2477 drag_info.data = control_point;
2478 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2479 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2481 start_grab (event, fader_cursor);
2483 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2485 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2486 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2487 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2489 show_verbose_canvas_cursor ();
2493 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2495 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2497 double cx = drag_info.current_pointer_x;
2498 double cy = drag_info.current_pointer_y;
2500 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2501 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2503 if (drag_info.x_constrained) {
2504 cx = drag_info.grab_x;
2506 if (drag_info.y_constrained) {
2507 cy = drag_info.grab_y;
2510 cp->line.parent_group().w2i (cx, cy);
2514 cy = min ((double) cp->line.height(), cy);
2516 //translate cx to frames
2517 nframes_t cx_frames = unit_to_frame (cx);
2519 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2520 snap_to (cx_frames);
2523 float fraction = 1.0 - (cy / cp->line.height());
2527 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2533 cp->line.point_drag (*cp, cx_frames , fraction, push);
2535 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2537 drag_info.first_move = false;
2541 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2543 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2545 if (drag_info.first_move) {
2549 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2550 reset_point_selection ();
2554 control_point_drag_motion_callback (item, event);
2556 cp->line.end_drag (cp);
2560 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2562 switch (mouse_mode) {
2564 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2565 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2573 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2577 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2578 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2582 start_line_grab (al, event);
2586 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2590 nframes_t frame_within_region;
2592 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2596 cx = event->button.x;
2597 cy = event->button.y;
2598 line->parent_group().w2i (cx, cy);
2599 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2601 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2602 current_line_drag_info.after)) {
2603 /* no adjacent points */
2607 drag_info.item = &line->grab_item();
2608 drag_info.data = line;
2609 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2610 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2612 start_grab (event, fader_cursor);
2614 double fraction = 1.0 - (cy / line->height());
2616 line->start_drag (0, drag_info.grab_frame, fraction);
2618 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2619 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2620 show_verbose_canvas_cursor ();
2624 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2626 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2627 double cx = drag_info.current_pointer_x;
2628 double cy = drag_info.current_pointer_y;
2630 line->parent_group().w2i (cx, cy);
2633 fraction = 1.0 - (cy / line->height());
2637 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2643 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2645 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2649 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2651 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2652 line_drag_motion_callback (item, event);
2657 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2659 if (selection->regions.empty() || clicked_regionview == 0) {
2663 drag_info.copy = false;
2664 drag_info.item = item;
2665 drag_info.data = clicked_regionview;
2666 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2667 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2672 TimeAxisView* tvp = clicked_axisview;
2673 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2675 if (tv && tv->is_track()) {
2676 speed = tv->get_diskstream()->speed();
2679 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2680 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2681 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2682 // we want a move threshold
2683 drag_info.want_move_threshold = true;
2685 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2687 begin_reversible_command (_("move region(s)"));
2691 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2693 if (selection->regions.empty() || clicked_regionview == 0) {
2697 drag_info.copy = true;
2698 drag_info.item = item;
2699 drag_info.data = clicked_regionview;
2703 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2704 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2707 if (rtv && rtv->is_track()) {
2708 speed = rtv->get_diskstream()->speed();
2711 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2712 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2713 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2714 // we want a move threshold
2715 drag_info.want_move_threshold = true;
2716 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2717 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2718 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2722 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2724 if (selection->regions.empty() || clicked_regionview == 0) {
2728 drag_info.copy = false;
2729 drag_info.item = item;
2730 drag_info.data = clicked_regionview;
2731 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2732 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2737 TimeAxisView* tvp = clicked_axisview;
2738 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2740 if (tv && tv->is_track()) {
2741 speed = tv->get_diskstream()->speed();
2744 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2745 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2746 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2747 // we want a move threshold
2748 drag_info.want_move_threshold = true;
2749 drag_info.brushing = true;
2751 begin_reversible_command (_("Drag region brush"));
2755 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2759 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2760 nframes_t pending_region_position = 0;
2761 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2762 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2763 bool clamp_y_axis = false;
2764 vector<int32_t> height_list(512) ;
2765 vector<int32_t>::iterator j;
2767 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2769 drag_info.want_move_threshold = false; // don't copy again
2771 /* duplicate the region(s) */
2773 vector<RegionView*> new_regionviews;
2775 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2781 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2782 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2785 nrv = new AudioRegionView (*arv);
2787 nrv = new MidiRegionView (*mrv);
2792 nrv->get_canvas_group()->show ();
2794 new_regionviews.push_back (nrv);
2797 if (new_regionviews.empty()) {
2801 /* reset selection to new regionviews */
2803 selection->set (new_regionviews);
2805 /* reset drag_info data to reflect the fact that we are dragging the copies */
2807 drag_info.data = new_regionviews.front();
2809 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2812 /* Which trackview is this ? */
2814 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2815 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2817 /* The region motion is only processed if the pointer is over
2821 if (!tv || !tv->is_track()) {
2822 /* To make sure we hide the verbose canvas cursor when the mouse is
2823 not held over a track.
2825 hide_verbose_canvas_cursor ();
2829 original_pointer_order = drag_info.last_trackview->order;
2831 /************************************************************
2833 ************************************************************/
2835 if (drag_info.brushing) {
2836 clamp_y_axis = true;
2841 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2843 int32_t children = 0, numtracks = 0;
2844 // XXX hard coding track limit, oh my, so very very bad
2845 bitset <1024> tracks (0x00);
2846 /* get a bitmask representing the visible tracks */
2848 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2849 TimeAxisView *tracklist_timeview;
2850 tracklist_timeview = (*i);
2851 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2852 list<TimeAxisView*> children_list;
2854 /* zeroes are audio tracks. ones are other types. */
2856 if (!rtv2->hidden()) {
2858 if (visible_y_high < rtv2->order) {
2859 visible_y_high = rtv2->order;
2861 if (visible_y_low > rtv2->order) {
2862 visible_y_low = rtv2->order;
2865 if (!rtv2->is_track()) {
2866 tracks = tracks |= (0x01 << rtv2->order);
2869 height_list[rtv2->order] = (*i)->height;
2871 if ((children_list = rtv2->get_child_list()).size() > 0) {
2872 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2873 tracks = tracks |= (0x01 << (rtv2->order + children));
2874 height_list[rtv2->order + children] = (*j)->height;
2882 /* find the actual span according to the canvas */
2884 canvas_pointer_y_span = pointer_y_span;
2885 if (drag_info.last_trackview->order >= tv->order) {
2887 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2888 if (height_list[y] == 0 ) {
2889 canvas_pointer_y_span--;
2894 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2895 if ( height_list[y] == 0 ) {
2896 canvas_pointer_y_span++;
2901 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2902 RegionView* rv2 = (*i);
2903 double ix1, ix2, iy1, iy2;
2906 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2907 rv2->get_canvas_group()->i2w (ix1, iy1);
2908 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2909 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2911 if (rtv2->order != original_pointer_order) {
2912 /* this isn't the pointer track */
2914 if (canvas_pointer_y_span > 0) {
2916 /* moving up the canvas */
2917 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
2919 int32_t visible_tracks = 0;
2920 while (visible_tracks < canvas_pointer_y_span ) {
2923 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2924 /* we're passing through a hidden track */
2929 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2930 clamp_y_axis = true;
2934 clamp_y_axis = true;
2937 } else if (canvas_pointer_y_span < 0) {
2939 /*moving down the canvas*/
2941 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2944 int32_t visible_tracks = 0;
2946 while (visible_tracks > canvas_pointer_y_span ) {
2949 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2953 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2954 clamp_y_axis = true;
2959 clamp_y_axis = true;
2965 /* this is the pointer's track */
2966 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2967 clamp_y_axis = true;
2968 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2969 clamp_y_axis = true;
2977 } else if (drag_info.last_trackview == tv) {
2978 clamp_y_axis = true;
2982 if (!clamp_y_axis) {
2983 drag_info.last_trackview = tv;
2986 /************************************************************
2988 ************************************************************/
2990 /* compute the amount of pointer motion in frames, and where
2991 the region would be if we moved it by that much.
2994 if (drag_info.move_threshold_passed) {
2996 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2998 nframes_t sync_frame;
2999 nframes_t sync_offset;
3002 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3004 sync_offset = rv->region()->sync_offset (sync_dir);
3005 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3007 /* we snap if the snap modifier is not enabled.
3010 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3011 snap_to (sync_frame);
3014 if (sync_frame - sync_offset <= sync_frame) {
3015 pending_region_position = sync_frame - (sync_dir*sync_offset);
3017 pending_region_position = 0;
3021 pending_region_position = 0;
3024 if (pending_region_position > max_frames - rv->region()->length()) {
3025 pending_region_position = drag_info.last_frame_position;
3028 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3030 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3032 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3033 to make it appear at the new location.
3036 if (pending_region_position > drag_info.last_frame_position) {
3037 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3039 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3042 drag_info.last_frame_position = pending_region_position;
3049 /* threshold not passed */
3054 /*************************************************************
3056 ************************************************************/
3058 if (x_delta == 0 && (pointer_y_span == 0)) {
3059 /* haven't reached next snap point, and we're not switching
3060 trackviews. nothing to do.
3066 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3068 RegionView* rv2 = (*i);
3070 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3072 double ix1, ix2, iy1, iy2;
3073 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3074 rv2->get_canvas_group()->i2w (ix1, iy1);
3083 /*************************************************************
3085 ************************************************************/
3089 if (drag_info.first_move) {
3090 if (drag_info.move_threshold_passed) {
3101 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3102 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3104 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3106 RegionView* rv = (*i);
3107 double ix1, ix2, iy1, iy2;
3108 int32_t temp_pointer_y_span = pointer_y_span;
3110 /* get item BBox, which will be relative to parent. so we have
3111 to query on a child, then convert to world coordinates using
3115 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3116 rv->get_canvas_group()->i2w (ix1, iy1);
3117 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3118 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3119 RouteTimeAxisView* temp_rtv;
3121 if ((pointer_y_span != 0) && !clamp_y_axis) {
3124 for (j = height_list.begin(); j!= height_list.end(); j++) {
3125 if (x == canvas_rtv->order) {
3126 /* we found the track the region is on */
3127 if (x != original_pointer_order) {
3128 /*this isn't from the same track we're dragging from */
3129 temp_pointer_y_span = canvas_pointer_y_span;
3131 while (temp_pointer_y_span > 0) {
3132 /* we're moving up canvas-wise,
3133 so we need to find the next track height
3135 if (j != height_list.begin()) {
3138 if (x != original_pointer_order) {
3139 /* we're not from the dragged track, so ignore hidden tracks. */
3141 temp_pointer_y_span++;
3145 temp_pointer_y_span--;
3147 while (temp_pointer_y_span < 0) {
3149 if (x != original_pointer_order) {
3151 temp_pointer_y_span--;
3155 if (j != height_list.end()) {
3158 temp_pointer_y_span++;
3160 /* find out where we'll be when we move and set height accordingly */
3162 tvp2 = trackview_by_y_position (iy1 + y_delta);
3163 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3164 rv->set_height (temp_rtv->height);
3166 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3167 personally, i think this can confuse things, but never mind.
3170 //const GdkColor& col (temp_rtv->view->get_region_color());
3171 //rv->set_color (const_cast<GdkColor&>(col));
3178 /* prevent the regionview from being moved to before
3179 the zero position on the canvas.
3184 if (-x_delta > ix1) {
3187 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3188 x_delta = max_frames - rv->region()->last_frame();
3191 if (drag_info.first_move) {
3193 /* hide any dependent views */
3195 rv->get_time_axis_view().hide_dependent_views (*rv);
3197 /* this is subtle. raising the regionview itself won't help,
3198 because raise_to_top() just puts the item on the top of
3199 its parent's stack. so, we need to put the trackview canvas_display group
3200 on the top, since its parent is the whole canvas.
3203 rv->get_canvas_group()->raise_to_top();
3204 rv->get_time_axis_view().canvas_display->raise_to_top();
3205 cursor_group->raise_to_top();
3207 rv->fake_set_opaque (true);
3210 if (drag_info.brushing) {
3211 mouse_brush_insert_region (rv, pending_region_position);
3213 rv->move (x_delta, y_delta);
3216 } /* foreach region */
3220 if (drag_info.first_move && drag_info.move_threshold_passed) {
3221 cursor_group->raise_to_top();
3222 drag_info.first_move = false;
3225 if (x_delta != 0 && !drag_info.brushing) {
3226 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3231 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3234 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3235 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3236 bool nocommit = true;
3238 RouteTimeAxisView* rtv;
3239 bool regionview_y_movement;
3240 bool regionview_x_movement;
3241 vector<RegionView*> copies;
3243 /* first_move is set to false if the regionview has been moved in the
3247 if (drag_info.first_move && !(drag_info.copy && drag_info.x_constrained)) {
3254 /* The regionview has been moved at some stage during the grab so we need
3255 to account for any mouse movement between this event and the last one.
3258 region_drag_motion_callback (item, event);
3260 if (drag_info.brushing) {
3261 /* all changes were made during motion event handlers */
3263 if (drag_info.copy) {
3264 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3265 copies.push_back (*i);
3272 /* adjust for track speed */
3275 rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3276 if (rtv && rtv->get_diskstream()) {
3277 speed = rtv->get_diskstream()->speed();
3280 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3281 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3283 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3284 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3288 if (drag_info.copy) {
3289 if (drag_info.x_constrained) {
3290 op_string = _("fixed time region copy");
3292 op_string = _("region copy");
3295 if (drag_info.x_constrained) {
3296 op_string = _("fixed time region drag");
3298 op_string = _("region drag");
3302 begin_reversible_command (op_string);
3304 if (regionview_y_movement) {
3306 /* moved to a different audio track. */
3308 vector<RegionView*> new_selection;
3310 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3312 RegionView* rv = (*i);
3314 double ix1, ix2, iy1, iy2;
3316 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3317 rv->get_canvas_group()->i2w (ix1, iy1);
3318 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3319 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3321 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3322 boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3324 where = (nframes_t) (unit_to_frame (ix1) * speed);
3325 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3327 /* undo the previous hide_dependent_views so that xfades don't
3328 disappear on copying regions
3331 rv->get_time_axis_view().reveal_dependent_views (*rv);
3333 if (!drag_info.copy) {
3335 /* the region that used to be in the old playlist is not
3336 moved to the new one - we make a copy of it. as a result,
3337 any existing editor for the region should no longer be
3341 rv->hide_region_editor();
3342 rv->fake_set_opaque (false);
3344 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3345 from_playlist->remove_region ((rv->region()));
3346 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3350 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3352 copies.push_back (rv);
3355 latest_regionview = 0;
3357 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3358 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3359 to_playlist->add_region (new_region, where);
3360 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3363 if (latest_regionview) {
3364 new_selection.push_back (latest_regionview);
3367 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3368 was selected in all of them, then removing it from the playlist will have removed all
3369 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3370 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3371 corresponding regionview, and the selection is now empty).
3373 this could have invalidated any and all iterators into the region selection.
3375 the heuristic we use here is: if the region selection is empty, break out of the loop
3376 here. if the region selection is not empty, then restart the loop because we know that
3377 we must have removed at least the region(view) we've just been working on as well as any
3378 that we processed on previous iterations.
3380 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3381 we can just iterate.
3384 if (drag_info.copy) {
3387 if (selection->regions.empty()) {
3390 i = selection->regions.by_layer().begin();
3395 selection->set (new_selection);
3399 /* motion within a single track */
3401 list<RegionView*> regions = selection->regions.by_layer();
3403 if (drag_info.copy) {
3404 selection->clear_regions();
3407 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3411 if (rv->region()->locked()) {
3416 if (regionview_x_movement) {
3417 double ownspeed = 1.0;
3418 rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3420 if (rtv && rtv->get_diskstream()) {
3421 ownspeed = rtv->get_diskstream()->speed();
3424 /* base the new region position on the current position of the regionview.*/
3426 double ix1, ix2, iy1, iy2;
3428 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3429 rv->get_canvas_group()->i2w (ix1, iy1);
3430 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3434 where = rv->region()->position();
3437 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3439 assert (to_playlist);
3443 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3445 if (drag_info.copy) {
3447 boost::shared_ptr<Region> newregion;
3448 boost::shared_ptr<Region> ar;
3450 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3451 newregion = RegionFactory::create (ar);
3453 /* XXX MIDI HERE drobilla */
3459 latest_regionview = 0;
3460 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3461 to_playlist->add_region (newregion, (nframes_t) (where * rtv->get_diskstream()->speed()));
3464 if (latest_regionview) {
3465 rtv->reveal_dependent_views (*latest_regionview);
3466 selection->add (latest_regionview);
3469 /* if the original region was locked, we don't care for the new one */
3471 newregion->set_locked (false);
3475 /* just change the model */
3477 rv->region()->set_position (where, (void*) this);
3483 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3485 if (drag_info.copy) {
3486 copies.push_back (rv);
3494 commit_reversible_command ();
3497 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3503 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3505 /* Either add to or set the set the region selection, unless
3506 this is an alignment click (control used)
3509 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3510 TimeAxisView* tv = &rv.get_time_axis_view();
3511 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3513 if (rtv && rtv->is_track()) {
3514 speed = rtv->get_diskstream()->speed();
3517 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3519 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3521 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3523 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3527 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3533 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3539 nframes_t frame_rate;
3546 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3547 case AudioClock::BBT:
3548 session->bbt_time (frame, bbt);
3549 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3552 case AudioClock::SMPTE:
3553 session->smpte_time (frame, smpte);
3554 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3557 case AudioClock::MinSec:
3558 /* XXX this is copied from show_verbose_duration_cursor() */
3559 frame_rate = session->frame_rate();
3560 hours = frame / (frame_rate * 3600);
3561 frame = frame % (frame_rate * 3600);
3562 mins = frame / (frame_rate * 60);
3563 frame = frame % (frame_rate * 60);
3564 secs = (float) frame / (float) frame_rate;
3565 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3569 snprintf (buf, sizeof(buf), "%u", frame);
3573 if (xpos >= 0 && ypos >=0) {
3574 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3577 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3579 show_verbose_canvas_cursor ();
3583 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3590 nframes_t distance, frame_rate;
3592 Meter meter_at_start(session->tempo_map().meter_at(start));
3598 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3599 case AudioClock::BBT:
3600 session->bbt_time (start, sbbt);
3601 session->bbt_time (end, ebbt);
3604 /* XXX this computation won't work well if the
3605 user makes a selection that spans any meter changes.
3608 ebbt.bars -= sbbt.bars;
3609 if (ebbt.beats >= sbbt.beats) {
3610 ebbt.beats -= sbbt.beats;
3613 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3615 if (ebbt.ticks >= sbbt.ticks) {
3616 ebbt.ticks -= sbbt.ticks;
3619 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3622 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3625 case AudioClock::SMPTE:
3626 session->smpte_duration (end - start, smpte);
3627 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3630 case AudioClock::MinSec:
3631 /* XXX this stuff should be elsewhere.. */
3632 distance = end - start;
3633 frame_rate = session->frame_rate();
3634 hours = distance / (frame_rate * 3600);
3635 distance = distance % (frame_rate * 3600);
3636 mins = distance / (frame_rate * 60);
3637 distance = distance % (frame_rate * 60);
3638 secs = (float) distance / (float) frame_rate;
3639 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3643 snprintf (buf, sizeof(buf), "%u", end - start);
3647 if (xpos >= 0 && ypos >=0) {
3648 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3651 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3653 show_verbose_canvas_cursor ();
3657 Editor::collect_new_region_view (RegionView* rv)
3659 latest_regionview = rv;
3663 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3665 if (clicked_regionview == 0) {
3669 /* lets try to create new Region for the selection */
3671 vector<boost::shared_ptr<AudioRegion> > new_regions;
3672 create_region_from_selection (new_regions);
3674 if (new_regions.empty()) {
3678 /* XXX fix me one day to use all new regions */
3680 boost::shared_ptr<Region> region (new_regions.front());
3682 /* add it to the current stream/playlist.
3684 tricky: the streamview for the track will add a new regionview. we will
3685 catch the signal it sends when it creates the regionview to
3686 set the regionview we want to then drag.
3689 latest_regionview = 0;
3690 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3692 /* A selection grab currently creates two undo/redo operations, one for
3693 creating the new region and another for moving it.
3696 begin_reversible_command (_("selection grab"));
3698 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3700 XMLNode *before = &(playlist->get_state());
3701 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3702 XMLNode *after = &(playlist->get_state());
3703 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3705 commit_reversible_command ();
3709 if (latest_regionview == 0) {
3710 /* something went wrong */
3714 /* we need to deselect all other regionviews, and select this one
3715 i'm ignoring undo stuff, because the region creation will take care of it */
3716 selection->set (latest_regionview);
3718 drag_info.item = latest_regionview->get_canvas_group();
3719 drag_info.data = latest_regionview;
3720 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3721 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3725 drag_info.last_trackview = clicked_axisview;
3726 drag_info.last_frame_position = latest_regionview->region()->position();
3727 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3729 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3733 Editor::cancel_selection ()
3735 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3736 (*i)->hide_selection ();
3738 begin_reversible_command (_("cancel selection"));
3739 selection->clear ();
3740 clicked_selection = 0;
3741 commit_reversible_command ();
3745 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3747 nframes_t start = 0;
3754 drag_info.item = item;
3755 drag_info.motion_callback = &Editor::drag_selection;
3756 drag_info.finished_callback = &Editor::end_selection_op;
3761 case CreateSelection:
3762 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3763 drag_info.copy = true;
3765 drag_info.copy = false;
3767 start_grab (event, selector_cursor);
3770 case SelectionStartTrim:
3771 if (clicked_axisview) {
3772 clicked_axisview->order_selection_trims (item, true);
3774 start_grab (event, trimmer_cursor);
3775 start = selection->time[clicked_selection].start;
3776 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3779 case SelectionEndTrim:
3780 if (clicked_axisview) {
3781 clicked_axisview->order_selection_trims (item, false);
3783 start_grab (event, trimmer_cursor);
3784 end = selection->time[clicked_selection].end;
3785 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3789 start = selection->time[clicked_selection].start;
3791 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3795 if (selection_op == SelectionMove) {
3796 show_verbose_time_cursor(start, 10);
3798 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3803 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3805 nframes_t start = 0;
3808 nframes_t pending_position;
3810 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3811 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3814 pending_position = 0;
3817 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3818 snap_to (pending_position);
3821 /* only alter selection if the current frame is
3822 different from the last frame position (adjusted)
3825 if (pending_position == drag_info.last_pointer_frame) return;
3827 switch (selection_op) {
3828 case CreateSelection:
3830 if (drag_info.first_move) {
3831 snap_to (drag_info.grab_frame);
3834 if (pending_position < drag_info.grab_frame) {
3835 start = pending_position;
3836 end = drag_info.grab_frame;
3838 end = pending_position;
3839 start = drag_info.grab_frame;
3842 /* first drag: Either add to the selection
3843 or create a new selection->
3846 if (drag_info.first_move) {
3848 begin_reversible_command (_("range selection"));
3850 if (drag_info.copy) {
3851 /* adding to the selection */
3852 clicked_selection = selection->add (start, end);
3853 drag_info.copy = false;
3855 /* new selection-> */
3856 clicked_selection = selection->set (clicked_axisview, start, end);
3861 case SelectionStartTrim:
3863 if (drag_info.first_move) {
3864 begin_reversible_command (_("trim selection start"));
3867 start = selection->time[clicked_selection].start;
3868 end = selection->time[clicked_selection].end;
3870 if (pending_position > end) {
3873 start = pending_position;
3877 case SelectionEndTrim:
3879 if (drag_info.first_move) {
3880 begin_reversible_command (_("trim selection end"));
3883 start = selection->time[clicked_selection].start;
3884 end = selection->time[clicked_selection].end;
3886 if (pending_position < start) {
3889 end = pending_position;
3896 if (drag_info.first_move) {
3897 begin_reversible_command (_("move selection"));
3900 start = selection->time[clicked_selection].start;
3901 end = selection->time[clicked_selection].end;
3903 length = end - start;
3905 start = pending_position;
3908 end = start + length;
3913 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3914 start_canvas_autoscroll (1);
3918 selection->replace (clicked_selection, start, end);
3921 drag_info.last_pointer_frame = pending_position;
3922 drag_info.first_move = false;
3924 if (selection_op == SelectionMove) {
3925 show_verbose_time_cursor(start, 10);
3927 show_verbose_time_cursor(pending_position, 10);
3932 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3934 if (!drag_info.first_move) {
3935 drag_selection (item, event);
3936 /* XXX this is not object-oriented programming at all. ick */
3937 if (selection->time.consolidate()) {
3938 selection->TimeChanged ();
3940 commit_reversible_command ();
3942 /* just a click, no pointer movement.*/
3944 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3946 selection->clear_time();
3951 /* XXX what happens if its a music selection? */
3952 session->set_audio_range (selection->time);
3953 stop_canvas_autoscroll ();
3957 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3960 TimeAxisView* tvp = clicked_axisview;
3961 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3963 if (tv && tv->is_track()) {
3964 speed = tv->get_diskstream()->speed();
3967 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3968 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3969 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3971 //drag_info.item = clicked_regionview->get_name_highlight();
3972 drag_info.item = item;
3973 drag_info.motion_callback = &Editor::trim_motion_callback;
3974 drag_info.finished_callback = &Editor::trim_finished_callback;
3976 start_grab (event, trimmer_cursor);
3978 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3979 trim_op = ContentsTrim;
3981 /* These will get overridden for a point trim.*/
3982 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3983 /* closer to start */
3984 trim_op = StartTrim;
3985 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3993 show_verbose_time_cursor(region_start, 10);
3996 show_verbose_time_cursor(region_end, 10);
3999 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4005 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4007 RegionView* rv = clicked_regionview;
4008 nframes_t frame_delta = 0;
4009 bool left_direction;
4010 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4012 /* snap modifier works differently here..
4013 its' current state has to be passed to the
4014 various trim functions in order to work properly
4018 TimeAxisView* tvp = clicked_axisview;
4019 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4020 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4022 if (tv && tv->is_track()) {
4023 speed = tv->get_diskstream()->speed();
4026 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4027 left_direction = true;
4029 left_direction = false;
4033 snap_to (drag_info.current_pointer_frame);
4036 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4040 if (drag_info.first_move) {
4046 trim_type = "Region start trim";
4049 trim_type = "Region end trim";
4052 trim_type = "Region content trim";
4056 begin_reversible_command (trim_type);
4058 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4059 (*i)->fake_set_opaque(false);
4060 (*i)->region()->freeze ();
4062 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4064 arv->temporarily_hide_envelope ();
4066 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4067 insert_result = motion_frozen_playlists.insert (pl);
4068 if (insert_result.second) {
4069 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4074 if (left_direction) {
4075 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4077 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4082 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4085 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4086 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4092 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4095 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4096 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4103 bool swap_direction = false;
4105 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4106 swap_direction = true;
4109 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4110 i != selection->regions.by_layer().end(); ++i)
4112 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4120 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4123 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4126 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4130 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4131 drag_info.first_move = false;
4135 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4137 boost::shared_ptr<Region> region (rv.region());
4139 if (region->locked()) {
4143 nframes_t new_bound;
4146 TimeAxisView* tvp = clicked_axisview;
4147 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4149 if (tv && tv->is_track()) {
4150 speed = tv->get_diskstream()->speed();
4153 if (left_direction) {
4154 if (swap_direction) {
4155 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4157 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4160 if (swap_direction) {
4161 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4163 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4168 snap_to (new_bound);
4170 region->trim_start ((nframes_t) (new_bound * speed), this);
4171 rv.region_changed (StartChanged);
4175 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4177 boost::shared_ptr<Region> region (rv.region());
4179 if (region->locked()) {
4183 nframes_t new_bound;
4186 TimeAxisView* tvp = clicked_axisview;
4187 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4189 if (tv && tv->is_track()) {
4190 speed = tv->get_diskstream()->speed();
4193 if (left_direction) {
4194 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4196 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4200 snap_to (new_bound, (left_direction ? 0 : 1));
4203 region->trim_front ((nframes_t) (new_bound * speed), this);
4205 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4209 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4211 boost::shared_ptr<Region> region (rv.region());
4213 if (region->locked()) {
4217 nframes_t new_bound;
4220 TimeAxisView* tvp = clicked_axisview;
4221 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4223 if (tv && tv->is_track()) {
4224 speed = tv->get_diskstream()->speed();
4227 if (left_direction) {
4228 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4230 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4234 snap_to (new_bound);
4236 region->trim_end ((nframes_t) (new_bound * speed), this);
4237 rv.region_changed (LengthChanged);
4241 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4243 if (!drag_info.first_move) {
4244 trim_motion_callback (item, event);
4246 if (!clicked_regionview->get_selected()) {
4247 thaw_region_after_trim (*clicked_regionview);
4250 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4251 i != selection->regions.by_layer().end(); ++i)
4253 thaw_region_after_trim (**i);
4254 (*i)->fake_set_opaque (true);
4258 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4260 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4263 motion_frozen_playlists.clear ();
4265 commit_reversible_command();
4267 /* no mouse movement */
4273 Editor::point_trim (GdkEvent* event)
4275 RegionView* rv = clicked_regionview;
4276 nframes_t new_bound = drag_info.current_pointer_frame;
4278 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4279 snap_to (new_bound);
4282 /* Choose action dependant on which button was pressed */
4283 switch (event->button.button) {
4285 trim_op = StartTrim;
4286 begin_reversible_command (_("Start point trim"));
4288 if (rv->get_selected()) {
4290 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4291 i != selection->regions.by_layer().end(); ++i)
4293 if (!(*i)->region()->locked()) {
4294 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4295 XMLNode &before = pl->get_state();
4296 (*i)->region()->trim_front (new_bound, this);
4297 XMLNode &after = pl->get_state();
4298 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4304 if (!rv->region()->locked()) {
4305 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4306 XMLNode &before = pl->get_state();
4307 rv->region()->trim_front (new_bound, this);
4308 XMLNode &after = pl->get_state();
4309 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4313 commit_reversible_command();
4318 begin_reversible_command (_("End point trim"));
4320 if (rv->get_selected()) {
4322 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4324 if (!(*i)->region()->locked()) {
4325 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4326 XMLNode &before = pl->get_state();
4327 (*i)->region()->trim_end (new_bound, this);
4328 XMLNode &after = pl->get_state();
4329 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4335 if (!rv->region()->locked()) {
4336 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4337 XMLNode &before = pl->get_state();
4338 rv->region()->trim_end (new_bound, this);
4339 XMLNode &after = pl->get_state();
4340 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4344 commit_reversible_command();
4353 Editor::thaw_region_after_trim (RegionView& rv)
4355 boost::shared_ptr<Region> region (rv.region());
4357 if (region->locked()) {
4361 region->thaw (_("trimmed region"));
4362 XMLNode &after = region->playlist()->get_state();
4363 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4365 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4367 arv->unhide_envelope ();
4371 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4376 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4377 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4381 Location* location = find_location_from_marker (marker, is_start);
4382 location->set_hidden (true, this);
4387 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4393 drag_info.item = item;
4394 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4395 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4397 range_marker_op = op;
4399 if (!temp_location) {
4400 temp_location = new Location;
4404 case CreateRangeMarker:
4405 case CreateTransportMarker:
4407 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4408 drag_info.copy = true;
4410 drag_info.copy = false;
4412 start_grab (event, selector_cursor);
4416 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4421 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4423 nframes_t start = 0;
4425 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4427 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4428 snap_to (drag_info.current_pointer_frame);
4431 /* only alter selection if the current frame is
4432 different from the last frame position.
4435 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4437 switch (range_marker_op) {
4438 case CreateRangeMarker:
4439 case CreateTransportMarker:
4440 if (drag_info.first_move) {
4441 snap_to (drag_info.grab_frame);
4444 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4445 start = drag_info.current_pointer_frame;
4446 end = drag_info.grab_frame;
4448 end = drag_info.current_pointer_frame;
4449 start = drag_info.grab_frame;
4452 /* first drag: Either add to the selection
4453 or create a new selection.
4456 if (drag_info.first_move) {
4458 temp_location->set (start, end);
4462 update_marker_drag_item (temp_location);
4463 range_marker_drag_rect->show();
4464 range_marker_drag_rect->raise_to_top();
4470 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4471 start_canvas_autoscroll (1);
4475 temp_location->set (start, end);
4477 double x1 = frame_to_pixel (start);
4478 double x2 = frame_to_pixel (end);
4479 crect->property_x1() = x1;
4480 crect->property_x2() = x2;
4482 update_marker_drag_item (temp_location);
4485 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4486 drag_info.first_move = false;
4488 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4493 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4495 Location * newloc = 0;
4498 if (!drag_info.first_move) {
4499 drag_range_markerbar_op (item, event);
4501 switch (range_marker_op) {
4502 case CreateRangeMarker:
4504 begin_reversible_command (_("new range marker"));
4505 XMLNode &before = session->locations()->get_state();
4506 session->locations()->next_available_name(rangename,"unnamed");
4507 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4508 session->locations()->add (newloc, true);
4509 XMLNode &after = session->locations()->get_state();
4510 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4511 commit_reversible_command ();
4513 range_bar_drag_rect->hide();
4514 range_marker_drag_rect->hide();
4518 case CreateTransportMarker:
4519 // popup menu to pick loop or punch
4520 new_transport_marker_context_menu (&event->button, item);
4525 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4527 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4532 start = session->locations()->first_mark_before (drag_info.grab_frame);
4533 end = session->locations()->first_mark_after (drag_info.grab_frame);
4535 if (end == max_frames) {
4536 end = session->current_end_frame ();
4540 start = session->current_start_frame ();
4543 switch (mouse_mode) {
4545 /* find the two markers on either side and then make the selection from it */
4546 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4550 /* find the two markers on either side of the click and make the range out of it */
4551 selection->set (0, start, end);
4560 stop_canvas_autoscroll ();
4566 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4568 drag_info.item = item;
4569 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4570 drag_info.finished_callback = &Editor::end_mouse_zoom;
4572 start_grab (event, zoom_cursor);
4574 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4578 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4583 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4584 snap_to (drag_info.current_pointer_frame);
4586 if (drag_info.first_move) {
4587 snap_to (drag_info.grab_frame);
4591 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4593 /* base start and end on initial click position */
4594 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4595 start = drag_info.current_pointer_frame;
4596 end = drag_info.grab_frame;
4598 end = drag_info.current_pointer_frame;
4599 start = drag_info.grab_frame;
4604 if (drag_info.first_move) {
4606 zoom_rect->raise_to_top();
4609 reposition_zoom_rect(start, end);
4611 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4612 drag_info.first_move = false;
4614 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4619 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4621 if (!drag_info.first_move) {
4622 drag_mouse_zoom (item, event);
4624 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4625 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4627 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4630 temporal_zoom_to_frame (false, drag_info.grab_frame);
4632 temporal_zoom_step (false);
4633 center_screen (drag_info.grab_frame);
4641 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4643 double x1 = frame_to_pixel (start);
4644 double x2 = frame_to_pixel (end);
4645 double y2 = full_canvas_height - 1.0;
4647 zoom_rect->property_x1() = x1;
4648 zoom_rect->property_y1() = 1.0;
4649 zoom_rect->property_x2() = x2;
4650 zoom_rect->property_y2() = y2;
4654 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4656 drag_info.item = item;
4657 drag_info.motion_callback = &Editor::drag_rubberband_select;
4658 drag_info.finished_callback = &Editor::end_rubberband_select;
4660 start_grab (event, cross_hair_cursor);
4662 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4666 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4673 /* use a bigger drag threshold than the default */
4675 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4679 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4680 if (drag_info.first_move) {
4681 snap_to (drag_info.grab_frame);
4683 snap_to (drag_info.current_pointer_frame);
4686 /* base start and end on initial click position */
4688 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4689 start = drag_info.current_pointer_frame;
4690 end = drag_info.grab_frame;
4692 end = drag_info.current_pointer_frame;
4693 start = drag_info.grab_frame;
4696 if (drag_info.current_pointer_y < drag_info.grab_y) {
4697 y1 = drag_info.current_pointer_y;
4698 y2 = drag_info.grab_y;
4700 y2 = drag_info.current_pointer_y;
4701 y1 = drag_info.grab_y;
4705 if (start != end || y1 != y2) {
4707 double x1 = frame_to_pixel (start);
4708 double x2 = frame_to_pixel (end);
4710 rubberband_rect->property_x1() = x1;
4711 rubberband_rect->property_y1() = y1;
4712 rubberband_rect->property_x2() = x2;
4713 rubberband_rect->property_y2() = y2;
4715 rubberband_rect->show();
4716 rubberband_rect->raise_to_top();
4718 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4719 drag_info.first_move = false;
4721 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4726 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4728 if (!drag_info.first_move) {
4730 drag_rubberband_select (item, event);
4733 if (drag_info.current_pointer_y < drag_info.grab_y) {
4734 y1 = drag_info.current_pointer_y;
4735 y2 = drag_info.grab_y;
4738 y2 = drag_info.current_pointer_y;
4739 y1 = drag_info.grab_y;
4743 Selection::Operation op = Keyboard::selection_type (event->button.state);
4746 begin_reversible_command (_("rubberband selection"));
4748 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4749 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4751 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4755 commit_reversible_command ();
4759 selection->clear_tracks();
4760 selection->clear_regions();
4761 selection->clear_points ();
4762 selection->clear_lines ();
4765 rubberband_rect->hide();
4770 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4772 using namespace Gtkmm2ext;
4774 ArdourPrompter prompter (false);
4776 prompter.set_prompt (_("Name for region:"));
4777 prompter.set_initial_text (clicked_regionview->region()->name());
4778 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4779 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4780 prompter.show_all ();
4781 switch (prompter.run ()) {
4782 case Gtk::RESPONSE_ACCEPT:
4784 prompter.get_result(str);
4786 clicked_regionview->region()->set_name (str);
4794 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4796 drag_info.item = item;
4797 drag_info.motion_callback = &Editor::time_fx_motion;
4798 drag_info.finished_callback = &Editor::end_time_fx;
4802 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4806 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4808 RegionView* rv = clicked_regionview;
4810 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4811 snap_to (drag_info.current_pointer_frame);
4814 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4818 if (drag_info.current_pointer_frame > rv->region()->position()) {
4819 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4822 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4823 drag_info.first_move = false;
4825 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4829 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4831 clicked_regionview->get_time_axis_view().hide_timestretch ();
4833 if (drag_info.first_move) {
4837 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4838 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4840 begin_reversible_command (_("timestretch"));
4842 if (run_timestretch (selection->regions, percentage) == 0) {
4843 session->commit_reversible_command ();
4848 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4850 /* no brushing without a useful snap setting */
4853 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4856 switch (snap_mode) {
4858 return; /* can't work because it allows region to be placed anywhere */
4863 switch (snap_type) {
4866 case SnapToEditCursor:
4873 /* don't brush a copy over the original */
4875 if (pos == rv->region()->position()) {
4879 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4881 if (rtv == 0 || !rtv->is_track()) {
4885 boost::shared_ptr<Playlist> playlist = rtv->playlist();
4886 double speed = rtv->get_diskstream()->speed();
4888 XMLNode &before = playlist->get_state();
4889 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4890 XMLNode &after = playlist->get_state();
4891 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4893 // playlist is frozen, so we have to update manually
4895 playlist->Modified(); /* EMIT SIGNAL */
4899 Editor::track_height_step_timeout ()
4902 struct timeval delta;
4904 gettimeofday (&now, 0);
4905 timersub (&now, &last_track_height_step_timestamp, &delta);
4907 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4908 current_stepping_trackview = 0;