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 case RegionViewNameHighlight:
335 case FadeInHandleItem:
337 case FadeOutHandleItem:
339 if (mouse_mode != MouseRange) {
340 commit = set_selected_regionview_from_click (press, op, true);
341 } else if (event->type == GDK_BUTTON_PRESS) {
342 commit = set_selected_track_from_click (press, op, false);
346 case CrossfadeViewItem:
347 commit = set_selected_track_from_click (press, op, false);
350 case GainAutomationControlPointItem:
351 case PanAutomationControlPointItem:
352 case RedirectAutomationControlPointItem:
353 case MidiCCAutomationControlPointItem:
354 commit = set_selected_track_from_click (press, op, true);
355 if (mouse_mode != MouseRange) {
356 commit |= set_selected_control_point_from_click (op, false);
361 /* for context click or range selection, select track */
362 if (event->button.button == 3) {
363 commit = set_selected_track_from_click (press, op, true);
364 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
365 commit = set_selected_track_from_click (press, op, false);
369 case AutomationTrackItem:
370 commit = set_selected_track_from_click (press, op, true);
378 // commit_reversible_command ();
383 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
385 track_canvas.grab_focus();
387 if (session && session->actively_recording()) {
391 button_selection (item, event, item_type);
393 if (drag_info.item == 0 &&
394 (Keyboard::is_delete_event (&event->button) ||
395 Keyboard::is_context_menu_event (&event->button) ||
396 Keyboard::is_edit_event (&event->button))) {
398 /* handled by button release */
402 switch (event->button.button) {
405 if (event->type == GDK_BUTTON_PRESS) {
407 if (drag_info.item) {
408 drag_info.item->ungrab (event->button.time);
411 /* single mouse clicks on any of these item types operate
412 independent of mouse mode, mostly because they are
413 not on the main track canvas or because we want
419 case PlayheadCursorItem:
420 start_cursor_grab (item, event);
424 if (Keyboard::modifier_state_equals (event->button.state,
425 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
426 hide_marker (item, event);
428 start_marker_grab (item, event);
432 case TempoMarkerItem:
433 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
434 start_tempo_marker_copy_grab (item, event);
436 start_tempo_marker_grab (item, event);
440 case MeterMarkerItem:
441 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
442 start_meter_marker_copy_grab (item, event);
444 start_meter_marker_grab (item, event);
454 case RangeMarkerBarItem:
455 start_range_markerbar_op (item, event, CreateRangeMarker);
459 case TransportMarkerBarItem:
460 start_range_markerbar_op (item, event, CreateTransportMarker);
469 switch (mouse_mode) {
472 case StartSelectionTrimItem:
473 start_selection_op (item, event, SelectionStartTrim);
476 case EndSelectionTrimItem:
477 start_selection_op (item, event, SelectionEndTrim);
481 if (Keyboard::modifier_state_contains
482 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
483 // contains and not equals because I can't use alt as a modifier alone.
484 start_selection_grab (item, event);
485 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
486 /* grab selection for moving */
487 start_selection_op (item, event, SelectionMove);
490 /* this was debated, but decided the more common action was to
491 make a new selection */
492 start_selection_op (item, event, CreateSelection);
497 start_selection_op (item, event, CreateSelection);
503 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
504 event->type == GDK_BUTTON_PRESS) {
506 start_rubberband_select (item, event);
508 } else if (event->type == GDK_BUTTON_PRESS) {
511 case FadeInHandleItem:
512 start_fade_in_grab (item, event);
515 case FadeOutHandleItem:
516 start_fade_out_grab (item, event);
520 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
521 start_region_copy_grab (item, event);
522 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
523 start_region_brush_grab (item, event);
525 start_region_grab (item, event);
529 case RegionViewNameHighlight:
530 start_trim (item, event);
535 /* rename happens on edit clicks */
536 start_trim (clicked_regionview->get_name_highlight(), event);
540 case GainAutomationControlPointItem:
541 case PanAutomationControlPointItem:
542 case RedirectAutomationControlPointItem:
543 case MidiCCAutomationControlPointItem:
544 start_control_point_grab (item, event);
548 case GainAutomationLineItem:
549 case PanAutomationLineItem:
550 case ProcessorAutomationLineItem:
551 case MidiCCAutomationLineItem:
552 start_line_grab_from_line (item, event);
557 case AutomationTrackItem:
558 start_rubberband_select (item, event);
562 case ImageFrameHandleStartItem:
563 imageframe_start_handle_op(item, event) ;
566 case ImageFrameHandleEndItem:
567 imageframe_end_handle_op(item, event) ;
570 case MarkerViewHandleStartItem:
571 markerview_item_start_handle_op(item, event) ;
574 case MarkerViewHandleEndItem:
575 markerview_item_end_handle_op(item, event) ;
579 start_markerview_grab(item, event) ;
582 start_imageframe_grab(item, event) ;
600 // start_line_grab_from_regionview (item, event);
603 case GainControlPointItem:
604 start_control_point_grab (item, event);
608 start_line_grab_from_line (item, event);
611 case GainAutomationControlPointItem:
612 case PanAutomationControlPointItem:
613 case RedirectAutomationControlPointItem:
614 case MidiCCAutomationControlPointItem:
615 start_control_point_grab (item, event);
626 case GainAutomationControlPointItem:
627 case PanAutomationControlPointItem:
628 case RedirectAutomationControlPointItem:
629 case MidiCCAutomationControlPointItem:
630 start_control_point_grab (item, event);
633 case GainAutomationLineItem:
634 case PanAutomationLineItem:
635 case ProcessorAutomationLineItem:
636 case MidiCCAutomationLineItem:
637 start_line_grab_from_line (item, event);
641 // XXX need automation mode to identify which
643 // start_line_grab_from_regionview (item, event);
653 if (event->type == GDK_BUTTON_PRESS) {
654 start_mouse_zoom (item, event);
661 if (item_type == RegionItem) {
662 start_time_fx (item, event);
667 /* handled in release */
676 switch (mouse_mode) {
678 if (event->type == GDK_BUTTON_PRESS) {
681 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
682 start_region_copy_grab (item, event);
684 start_region_grab (item, event);
688 case GainAutomationControlPointItem:
689 case PanAutomationControlPointItem:
690 case RedirectAutomationControlPointItem:
691 case MidiCCAutomationControlPointItem:
692 start_control_point_grab (item, event);
703 case RegionViewNameHighlight:
704 start_trim (item, event);
709 start_trim (clicked_regionview->get_name_highlight(), event);
720 if (event->type == GDK_BUTTON_PRESS) {
721 /* relax till release */
728 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
729 temporal_zoom_session();
731 temporal_zoom_to_frame (true, event_frame(event));
754 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
756 nframes_t where = event_frame (event, 0, 0);
758 /* no action if we're recording */
760 if (session && session->actively_recording()) {
764 /* first, see if we're finishing a drag ... */
766 if (drag_info.item) {
767 if (end_grab (item, event)) {
768 /* grab dragged, so do nothing else */
773 button_selection (item, event, item_type);
775 /* edit events get handled here */
777 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
783 case TempoMarkerItem:
784 edit_tempo_marker (item);
787 case MeterMarkerItem:
788 edit_meter_marker (item);
792 if (clicked_regionview->name_active()) {
793 return mouse_rename_region (item, event);
803 /* context menu events get handled here */
805 if (Keyboard::is_context_menu_event (&event->button)) {
807 if (drag_info.item == 0) {
809 /* no matter which button pops up the context menu, tell the menu
810 widget to use button 1 to drive menu selection.
815 case FadeInHandleItem:
817 case FadeOutHandleItem:
818 popup_fade_context_menu (1, event->button.time, item, item_type);
823 case RegionViewNameHighlight:
826 case AutomationTrackItem:
827 case CrossfadeViewItem:
828 popup_track_context_menu (1, event->button.time, where);
832 case RangeMarkerBarItem:
833 case TransportMarkerBarItem:
836 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
840 marker_context_menu (&event->button, item);
843 case TempoMarkerItem:
844 tm_marker_context_menu (&event->button, item);
847 case MeterMarkerItem:
848 tm_marker_context_menu (&event->button, item);
853 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
855 case ImageFrameTimeAxisItem:
856 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
859 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
861 case MarkerTimeAxisItem:
862 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
874 /* delete events get handled here */
876 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
879 case TempoMarkerItem:
880 remove_tempo_marker (item);
883 case MeterMarkerItem:
884 remove_meter_marker (item);
888 remove_marker (*item, event);
892 if (mouse_mode == MouseObject) {
893 remove_clicked_region ();
897 case GainControlPointItem:
898 if (mouse_mode == MouseGain) {
899 remove_gain_control_point (item, event);
903 case GainAutomationControlPointItem:
904 case PanAutomationControlPointItem:
905 case RedirectAutomationControlPointItem:
906 case MidiCCAutomationControlPointItem:
907 remove_control_point (item, event);
916 switch (event->button.button) {
920 /* see comments in button_press_handler */
922 case PlayheadCursorItem:
925 case GainAutomationLineItem:
926 case PanAutomationLineItem:
927 case ProcessorAutomationLineItem:
928 case MidiCCAutomationLineItem:
929 case StartSelectionTrimItem:
930 case EndSelectionTrimItem:
934 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
935 snap_to (where, 0, true);
937 mouse_add_new_marker (where);
941 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
944 mouse_add_new_tempo_event (where);
948 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
956 switch (mouse_mode) {
959 case AutomationTrackItem:
960 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->add_automation_event
974 // Gain only makes sense for audio regions
976 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
982 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
986 case AutomationTrackItem:
987 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
988 add_automation_event (item, event, where, event->button.y);
999 audition_selected_region ();
1016 switch (mouse_mode) {
1020 // x_style_paste (where, 1.0);
1040 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1046 switch (item_type) {
1047 case GainControlPointItem:
1048 if (mouse_mode == MouseGain) {
1049 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1050 cp->set_visible (true);
1054 at_y = cp->get_y ();
1055 cp->item->i2w (at_x, at_y);
1059 fraction = 1.0 - ((cp->get_y() - cp->line.y_position()) / cp->line.height());
1061 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1062 show_verbose_canvas_cursor ();
1064 if (is_drawable()) {
1065 track_canvas.get_window()->set_cursor (*fader_cursor);
1070 case GainAutomationControlPointItem:
1071 case PanAutomationControlPointItem:
1072 case RedirectAutomationControlPointItem:
1073 case MidiCCAutomationControlPointItem:
1074 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1075 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1076 cp->set_visible (true);
1080 at_y = cp->get_y ();
1081 cp->item->i2w (at_x, at_y);
1085 fraction = 1.0 - (cp->get_y() / cp->line.height());
1087 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1088 show_verbose_canvas_cursor ();
1090 if (is_drawable()) {
1091 track_canvas.get_window()->set_cursor (*fader_cursor);
1097 if (mouse_mode == MouseGain) {
1098 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1100 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1101 if (is_drawable()) {
1102 track_canvas.get_window()->set_cursor (*fader_cursor);
1107 case GainAutomationLineItem:
1108 case ProcessorAutomationLineItem:
1109 case MidiCCAutomationLineItem:
1110 case PanAutomationLineItem:
1111 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1113 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1115 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1117 if (is_drawable()) {
1118 track_canvas.get_window()->set_cursor (*fader_cursor);
1123 case RegionViewNameHighlight:
1124 if (is_drawable() && mouse_mode == MouseObject) {
1125 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1129 case StartSelectionTrimItem:
1130 case EndSelectionTrimItem:
1133 case ImageFrameHandleStartItem:
1134 case ImageFrameHandleEndItem:
1135 case MarkerViewHandleStartItem:
1136 case MarkerViewHandleEndItem:
1139 if (is_drawable()) {
1140 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1144 case EditCursorItem:
1145 case PlayheadCursorItem:
1146 if (is_drawable()) {
1147 track_canvas.get_window()->set_cursor (*grabber_cursor);
1151 case RegionViewName:
1153 /* when the name is not an active item, the entire name highlight is for trimming */
1155 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1156 if (mouse_mode == MouseObject && is_drawable()) {
1157 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1163 case AutomationTrackItem:
1164 if (is_drawable()) {
1165 Gdk::Cursor *cursor;
1166 switch (mouse_mode) {
1168 cursor = selector_cursor;
1171 cursor = zoom_cursor;
1174 cursor = cross_hair_cursor;
1178 track_canvas.get_window()->set_cursor (*cursor);
1180 AutomationTimeAxisView* atv;
1181 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1182 clear_entered_track = false;
1183 set_entered_track (atv);
1189 case RangeMarkerBarItem:
1190 case TransportMarkerBarItem:
1193 if (is_drawable()) {
1194 time_canvas.get_window()->set_cursor (*timebar_cursor);
1199 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1202 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1204 case MeterMarkerItem:
1205 case TempoMarkerItem:
1206 if (is_drawable()) {
1207 time_canvas.get_window()->set_cursor (*timebar_cursor);
1210 case FadeInHandleItem:
1211 case FadeOutHandleItem:
1212 if (mouse_mode == MouseObject) {
1213 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1215 rect->property_fill_color_rgba() = 0;
1216 rect->property_outline_pixels() = 1;
1225 /* second pass to handle entered track status in a comprehensible way.
1228 switch (item_type) {
1230 case GainAutomationLineItem:
1231 case ProcessorAutomationLineItem:
1232 case MidiCCAutomationLineItem:
1233 case PanAutomationLineItem:
1234 case GainControlPointItem:
1235 case GainAutomationControlPointItem:
1236 case PanAutomationControlPointItem:
1237 case RedirectAutomationControlPointItem:
1238 case MidiCCAutomationControlPointItem:
1239 /* these do not affect the current entered track state */
1240 clear_entered_track = false;
1243 case AutomationTrackItem:
1244 /* handled above already */
1248 set_entered_track (0);
1256 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1265 switch (item_type) {
1266 case GainControlPointItem:
1267 case GainAutomationControlPointItem:
1268 case PanAutomationControlPointItem:
1269 case RedirectAutomationControlPointItem:
1270 case MidiCCAutomationControlPointItem:
1271 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1272 if (cp->line.npoints() > 1) {
1273 if (!cp->selected) {
1274 cp->set_visible (false);
1278 if (is_drawable()) {
1279 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1282 hide_verbose_canvas_cursor ();
1285 case RegionViewNameHighlight:
1286 case StartSelectionTrimItem:
1287 case EndSelectionTrimItem:
1288 case EditCursorItem:
1289 case PlayheadCursorItem:
1292 case ImageFrameHandleStartItem:
1293 case ImageFrameHandleEndItem:
1294 case MarkerViewHandleStartItem:
1295 case MarkerViewHandleEndItem:
1298 if (is_drawable()) {
1299 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1304 case GainAutomationLineItem:
1305 case ProcessorAutomationLineItem:
1306 case MidiCCAutomationLineItem:
1307 case PanAutomationLineItem:
1308 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1310 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1312 line->property_fill_color_rgba() = al->get_line_color();
1314 if (is_drawable()) {
1315 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1319 case RegionViewName:
1320 /* see enter_handler() for notes */
1321 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1322 if (is_drawable() && mouse_mode == MouseObject) {
1323 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1328 case RangeMarkerBarItem:
1329 case TransportMarkerBarItem:
1333 if (is_drawable()) {
1334 time_canvas.get_window()->set_cursor (*timebar_cursor);
1339 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1342 loc = find_location_from_marker (marker, is_start);
1343 if (loc) location_flags_changed (loc, this);
1345 case MeterMarkerItem:
1346 case TempoMarkerItem:
1348 if (is_drawable()) {
1349 time_canvas.get_window()->set_cursor (*timebar_cursor);
1354 case FadeInHandleItem:
1355 case FadeOutHandleItem:
1356 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1358 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1360 rect->property_fill_color_rgba() = rv->get_fill_color();
1361 rect->property_outline_pixels() = 0;
1366 case AutomationTrackItem:
1367 if (is_drawable()) {
1368 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1369 clear_entered_track = true;
1370 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1382 Editor::left_automation_track ()
1384 if (clear_entered_track) {
1385 set_entered_track (0);
1386 clear_entered_track = false;
1392 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1396 /* We call this so that MOTION_NOTIFY events continue to be
1397 delivered to the canvas. We need to do this because we set
1398 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1399 the density of the events, at the expense of a round-trip
1400 to the server. Given that this will mostly occur on cases
1401 where DISPLAY = :0.0, and given the cost of what the motion
1402 event might do, its a good tradeoff.
1405 track_canvas.get_pointer (x, y);
1407 if (current_stepping_trackview) {
1408 /* don't keep the persistent stepped trackview if the mouse moves */
1409 current_stepping_trackview = 0;
1410 step_timeout.disconnect ();
1413 if (session && session->actively_recording()) {
1414 /* Sorry. no dragging stuff around while we record */
1418 drag_info.item_type = item_type;
1419 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1420 &drag_info.current_pointer_y);
1422 if (!from_autoscroll && drag_info.item) {
1423 /* item != 0 is the best test i can think of for dragging.
1425 if (!drag_info.move_threshold_passed) {
1427 bool x_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1428 bool y_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1430 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1432 // and change the initial grab loc/frame if this drag info wants us to
1434 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1435 drag_info.grab_frame = drag_info.current_pointer_frame;
1436 drag_info.grab_x = drag_info.current_pointer_x;
1437 drag_info.grab_y = drag_info.current_pointer_y;
1438 drag_info.last_pointer_frame = drag_info.grab_frame;
1439 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1444 switch (item_type) {
1445 case PlayheadCursorItem:
1446 case EditCursorItem:
1448 case GainControlPointItem:
1449 case RedirectAutomationControlPointItem:
1450 case MidiCCAutomationControlPointItem:
1451 case GainAutomationControlPointItem:
1452 case PanAutomationControlPointItem:
1453 case TempoMarkerItem:
1454 case MeterMarkerItem:
1455 case RegionViewNameHighlight:
1456 case StartSelectionTrimItem:
1457 case EndSelectionTrimItem:
1460 case ProcessorAutomationLineItem:
1461 case MidiCCAutomationLineItem:
1462 case GainAutomationLineItem:
1463 case PanAutomationLineItem:
1464 case FadeInHandleItem:
1465 case FadeOutHandleItem:
1468 case ImageFrameHandleStartItem:
1469 case ImageFrameHandleEndItem:
1470 case MarkerViewHandleStartItem:
1471 case MarkerViewHandleEndItem:
1474 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1475 (event->motion.state & Gdk::BUTTON2_MASK))) {
1476 if (!from_autoscroll) {
1477 maybe_autoscroll (event);
1479 (this->*(drag_info.motion_callback)) (item, event);
1488 switch (mouse_mode) {
1493 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1494 (event->motion.state & GDK_BUTTON2_MASK))) {
1495 if (!from_autoscroll) {
1496 maybe_autoscroll (event);
1498 (this->*(drag_info.motion_callback)) (item, event);
1509 track_canvas_motion (event);
1510 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1518 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1520 if (drag_info.item == 0) {
1521 fatal << _("programming error: start_grab called without drag item") << endmsg;
1527 cursor = grabber_cursor;
1530 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1532 if (event->button.button == 2) {
1533 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1534 drag_info.y_constrained = true;
1535 drag_info.x_constrained = false;
1537 drag_info.y_constrained = false;
1538 drag_info.x_constrained = true;
1541 drag_info.x_constrained = false;
1542 drag_info.y_constrained = false;
1545 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1546 drag_info.last_pointer_frame = drag_info.grab_frame;
1547 drag_info.current_pointer_frame = drag_info.grab_frame;
1548 drag_info.current_pointer_x = drag_info.grab_x;
1549 drag_info.current_pointer_y = drag_info.grab_y;
1550 drag_info.cumulative_x_drag = 0;
1551 drag_info.cumulative_y_drag = 0;
1552 drag_info.first_move = true;
1553 drag_info.move_threshold_passed = false;
1554 drag_info.want_move_threshold = false;
1555 drag_info.pointer_frame_offset = 0;
1556 drag_info.brushing = false;
1557 drag_info.copied_location = 0;
1559 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1561 event->button.time);
1563 if (session && session->transport_rolling()) {
1564 drag_info.was_rolling = true;
1566 drag_info.was_rolling = false;
1569 switch (snap_type) {
1570 case SnapToRegionStart:
1571 case SnapToRegionEnd:
1572 case SnapToRegionSync:
1573 case SnapToRegionBoundary:
1574 build_region_boundary_cache ();
1582 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1584 drag_info.item->ungrab (0);
1585 drag_info.item = new_item;
1588 cursor = grabber_cursor;
1591 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1595 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1597 bool did_drag = false;
1599 stop_canvas_autoscroll ();
1601 if (drag_info.item == 0) {
1605 drag_info.item->ungrab (event->button.time);
1607 if (drag_info.finished_callback) {
1608 (this->*(drag_info.finished_callback)) (item, event);
1611 did_drag = !drag_info.first_move;
1613 hide_verbose_canvas_cursor();
1616 drag_info.copy = false;
1617 drag_info.motion_callback = 0;
1618 drag_info.finished_callback = 0;
1619 drag_info.last_trackview = 0;
1620 drag_info.last_frame_position = 0;
1621 drag_info.grab_frame = 0;
1622 drag_info.last_pointer_frame = 0;
1623 drag_info.current_pointer_frame = 0;
1624 drag_info.brushing = false;
1626 if (drag_info.copied_location) {
1627 delete drag_info.copied_location;
1628 drag_info.copied_location = 0;
1635 Editor::set_edit_cursor (GdkEvent* event)
1637 nframes_t pointer_frame = event_frame (event);
1639 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1640 if (snap_type != SnapToEditCursor) {
1641 snap_to (pointer_frame);
1645 edit_cursor->set_position (pointer_frame);
1646 edit_cursor_clock.set (pointer_frame);
1650 Editor::set_playhead_cursor (GdkEvent* event)
1652 nframes_t pointer_frame = event_frame (event);
1654 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1655 snap_to (pointer_frame);
1659 session->request_locate (pointer_frame, session->transport_rolling());
1664 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1666 drag_info.item = item;
1667 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1668 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1672 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1673 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1677 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1679 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
1683 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1685 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1687 nframes_t fade_length;
1689 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1690 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1696 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1700 if (pos < (arv->region()->position() + 64)) {
1701 fade_length = 64; // this should be a minimum defined somewhere
1702 } else if (pos > arv->region()->last_frame()) {
1703 fade_length = arv->region()->length();
1705 fade_length = pos - arv->region()->position();
1707 /* mapover the region selection */
1709 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1711 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1717 tmp->reset_fade_in_shape_width (fade_length);
1720 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1722 drag_info.first_move = false;
1726 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1728 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1730 nframes_t fade_length;
1732 if (drag_info.first_move) return;
1734 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1735 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1740 if (pos < (arv->region()->position() + 64)) {
1741 fade_length = 64; // this should be a minimum defined somewhere
1742 } else if (pos > arv->region()->last_frame()) {
1743 fade_length = arv->region()->length();
1745 fade_length = pos - arv->region()->position();
1748 begin_reversible_command (_("change fade in length"));
1750 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1752 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1758 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
1759 XMLNode &before = alist->get_state();
1761 tmp->audio_region()->set_fade_in_length (fade_length);
1763 XMLNode &after = alist->get_state();
1764 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1767 commit_reversible_command ();
1771 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1773 drag_info.item = item;
1774 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1775 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1779 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1780 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1784 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1786 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
1790 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1792 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1794 nframes_t fade_length;
1796 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1797 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1802 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1806 if (pos > (arv->region()->last_frame() - 64)) {
1807 fade_length = 64; // this should really be a minimum fade defined somewhere
1809 else if (pos < arv->region()->position()) {
1810 fade_length = arv->region()->length();
1813 fade_length = arv->region()->last_frame() - pos;
1816 /* mapover the region selection */
1818 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1820 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1826 tmp->reset_fade_out_shape_width (fade_length);
1829 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1831 drag_info.first_move = false;
1835 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1837 if (drag_info.first_move) return;
1839 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1841 nframes_t fade_length;
1843 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1844 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1850 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1854 if (pos > (arv->region()->last_frame() - 64)) {
1855 fade_length = 64; // this should really be a minimum fade defined somewhere
1857 else if (pos < arv->region()->position()) {
1858 fade_length = arv->region()->length();
1861 fade_length = arv->region()->last_frame() - pos;
1864 begin_reversible_command (_("change fade out length"));
1866 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1868 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1874 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
1875 XMLNode &before = alist->get_state();
1877 tmp->audio_region()->set_fade_out_length (fade_length);
1879 XMLNode &after = alist->get_state();
1880 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1883 commit_reversible_command ();
1887 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1889 drag_info.item = item;
1890 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1891 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1895 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1896 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1900 Cursor* cursor = (Cursor *) drag_info.data;
1902 if (cursor == playhead_cursor) {
1903 _dragging_playhead = true;
1905 if (session && drag_info.was_rolling) {
1906 session->request_stop ();
1909 if (session && session->is_auditioning()) {
1910 session->cancel_audition ();
1914 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1916 show_verbose_time_cursor (cursor->current_frame, 10);
1920 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1922 Cursor* cursor = (Cursor *) drag_info.data;
1923 nframes_t adjusted_frame;
1925 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1926 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1932 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1933 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1934 snap_to (adjusted_frame);
1938 if (adjusted_frame == drag_info.last_pointer_frame) return;
1940 cursor->set_position (adjusted_frame);
1942 if (cursor == edit_cursor) {
1943 edit_cursor_clock.set (cursor->current_frame);
1945 UpdateAllTransportClocks (cursor->current_frame);
1948 show_verbose_time_cursor (cursor->current_frame, 10);
1950 drag_info.last_pointer_frame = adjusted_frame;
1951 drag_info.first_move = false;
1955 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1957 if (drag_info.first_move) return;
1959 cursor_drag_motion_callback (item, event);
1961 _dragging_playhead = false;
1963 if (item == &playhead_cursor->canvas_item) {
1965 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1967 } else if (item == &edit_cursor->canvas_item) {
1968 edit_cursor->set_position (edit_cursor->current_frame);
1969 edit_cursor_clock.set (edit_cursor->current_frame);
1974 Editor::update_marker_drag_item (Location *location)
1976 double x1 = frame_to_pixel (location->start());
1977 double x2 = frame_to_pixel (location->end());
1979 if (location->is_mark()) {
1980 marker_drag_line_points.front().set_x(x1);
1981 marker_drag_line_points.back().set_x(x1);
1982 marker_drag_line->property_points() = marker_drag_line_points;
1985 range_marker_drag_rect->property_x1() = x1;
1986 range_marker_drag_rect->property_x2() = x2;
1991 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
1995 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1996 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2002 Location *location = find_location_from_marker (marker, is_start);
2004 drag_info.item = item;
2005 drag_info.data = marker;
2006 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2007 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2011 drag_info.copied_location = new Location (*location);
2012 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2014 update_marker_drag_item (location);
2016 if (location->is_mark()) {
2017 marker_drag_line->show();
2018 marker_drag_line->raise_to_top();
2021 range_marker_drag_rect->show();
2022 range_marker_drag_rect->raise_to_top();
2025 if (is_start) show_verbose_time_cursor (location->start(), 10);
2026 else show_verbose_time_cursor (location->end(), 10);
2030 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2033 Marker* marker = (Marker *) drag_info.data;
2034 Location *real_location;
2035 Location *copy_location;
2037 bool move_both = false;
2041 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2042 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2047 nframes_t next = newframe;
2049 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2050 snap_to (newframe, 0, true);
2053 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2057 /* call this to find out if its the start or end */
2059 real_location = find_location_from_marker (marker, is_start);
2061 /* use the copy that we're "dragging" around */
2063 copy_location = drag_info.copied_location;
2065 f_delta = copy_location->end() - copy_location->start();
2067 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2071 if (copy_location->is_mark()) {
2074 copy_location->set_start (newframe);
2078 if (is_start) { // start-of-range marker
2081 copy_location->set_start (newframe);
2082 copy_location->set_end (newframe + f_delta);
2083 } else if (newframe < copy_location->end()) {
2084 copy_location->set_start (newframe);
2086 snap_to (next, 1, true);
2087 copy_location->set_end (next);
2088 copy_location->set_start (newframe);
2091 } else { // end marker
2094 copy_location->set_end (newframe);
2095 copy_location->set_start (newframe - f_delta);
2096 } else if (newframe > copy_location->start()) {
2097 copy_location->set_end (newframe);
2099 } else if (newframe > 0) {
2100 snap_to (next, -1, true);
2101 copy_location->set_start (next);
2102 copy_location->set_end (newframe);
2107 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2108 drag_info.first_move = false;
2110 update_marker_drag_item (copy_location);
2112 LocationMarkers* lm = find_location_markers (real_location);
2113 lm->set_position (copy_location->start(), copy_location->end());
2115 show_verbose_time_cursor (newframe, 10);
2119 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2121 if (drag_info.first_move) {
2122 marker_drag_motion_callback (item, event);
2126 Marker* marker = (Marker *) drag_info.data;
2130 begin_reversible_command ( _("move marker") );
2131 XMLNode &before = session->locations()->get_state();
2133 Location * location = find_location_from_marker (marker, is_start);
2136 if (location->is_mark()) {
2137 location->set_start (drag_info.copied_location->start());
2139 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2143 XMLNode &after = session->locations()->get_state();
2144 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2145 commit_reversible_command ();
2147 marker_drag_line->hide();
2148 range_marker_drag_rect->hide();
2152 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2155 MeterMarker* meter_marker;
2157 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2158 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2162 meter_marker = dynamic_cast<MeterMarker*> (marker);
2164 MetricSection& section (meter_marker->meter());
2166 if (!section.movable()) {
2170 drag_info.item = item;
2171 drag_info.data = marker;
2172 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2173 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2177 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2179 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2183 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2186 MeterMarker* meter_marker;
2188 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2189 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2193 meter_marker = dynamic_cast<MeterMarker*> (marker);
2195 // create a dummy marker for visual representation of moving the copy.
2196 // The actual copying is not done before we reach the finish callback.
2198 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2199 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2200 *new MeterSection(meter_marker->meter()));
2202 drag_info.item = &new_marker->the_item();
2203 drag_info.copy = true;
2204 drag_info.data = new_marker;
2205 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2206 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2210 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2212 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2216 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2218 MeterMarker* marker = (MeterMarker *) drag_info.data;
2219 nframes_t adjusted_frame;
2221 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2222 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2228 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2229 snap_to (adjusted_frame);
2232 if (adjusted_frame == drag_info.last_pointer_frame) return;
2234 marker->set_position (adjusted_frame);
2237 drag_info.last_pointer_frame = adjusted_frame;
2238 drag_info.first_move = false;
2240 show_verbose_time_cursor (adjusted_frame, 10);
2244 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2246 if (drag_info.first_move) return;
2248 meter_marker_drag_motion_callback (drag_info.item, event);
2250 MeterMarker* marker = (MeterMarker *) drag_info.data;
2253 TempoMap& map (session->tempo_map());
2254 map.bbt_time (drag_info.last_pointer_frame, when);
2256 if (drag_info.copy == true) {
2257 begin_reversible_command (_("copy meter mark"));
2258 XMLNode &before = map.get_state();
2259 map.add_meter (marker->meter(), when);
2260 XMLNode &after = map.get_state();
2261 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2262 commit_reversible_command ();
2264 // delete the dummy marker we used for visual representation of copying.
2265 // a new visual marker will show up automatically.
2268 begin_reversible_command (_("move meter mark"));
2269 XMLNode &before = map.get_state();
2270 map.move_meter (marker->meter(), when);
2271 XMLNode &after = map.get_state();
2272 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2273 commit_reversible_command ();
2278 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2281 TempoMarker* tempo_marker;
2283 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2284 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2288 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2289 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2293 MetricSection& section (tempo_marker->tempo());
2295 if (!section.movable()) {
2299 drag_info.item = item;
2300 drag_info.data = marker;
2301 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2302 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2306 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2307 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2311 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2314 TempoMarker* tempo_marker;
2316 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2317 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2321 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2322 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2326 // create a dummy marker for visual representation of moving the copy.
2327 // The actual copying is not done before we reach the finish callback.
2329 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2330 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2331 *new TempoSection(tempo_marker->tempo()));
2333 drag_info.item = &new_marker->the_item();
2334 drag_info.copy = true;
2335 drag_info.data = new_marker;
2336 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2337 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2341 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2343 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2347 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2349 TempoMarker* marker = (TempoMarker *) drag_info.data;
2350 nframes_t adjusted_frame;
2352 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2353 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2359 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2360 snap_to (adjusted_frame);
2363 if (adjusted_frame == drag_info.last_pointer_frame) return;
2365 /* OK, we've moved far enough to make it worth actually move the thing. */
2367 marker->set_position (adjusted_frame);
2369 show_verbose_time_cursor (adjusted_frame, 10);
2371 drag_info.last_pointer_frame = adjusted_frame;
2372 drag_info.first_move = false;
2376 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2378 if (drag_info.first_move) return;
2380 tempo_marker_drag_motion_callback (drag_info.item, event);
2382 TempoMarker* marker = (TempoMarker *) drag_info.data;
2385 TempoMap& map (session->tempo_map());
2386 map.bbt_time (drag_info.last_pointer_frame, when);
2388 if (drag_info.copy == true) {
2389 begin_reversible_command (_("copy tempo mark"));
2390 XMLNode &before = map.get_state();
2391 map.add_tempo (marker->tempo(), when);
2392 XMLNode &after = map.get_state();
2393 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2394 commit_reversible_command ();
2396 // delete the dummy marker we used for visual representation of copying.
2397 // a new visual marker will show up automatically.
2400 begin_reversible_command (_("move tempo mark"));
2401 XMLNode &before = map.get_state();
2402 map.move_tempo (marker->tempo(), when);
2403 XMLNode &after = map.get_state();
2404 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2405 commit_reversible_command ();
2410 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2412 ControlPoint* control_point;
2414 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2415 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2419 // We shouldn't remove the first or last gain point
2420 if (control_point->line.is_last_point(*control_point) ||
2421 control_point->line.is_first_point(*control_point)) {
2425 control_point->line.remove_point (*control_point);
2429 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2431 ControlPoint* control_point;
2433 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2434 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2438 control_point->line.remove_point (*control_point);
2442 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2444 ControlPoint* control_point;
2446 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2447 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2451 drag_info.item = item;
2452 drag_info.data = control_point;
2453 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2454 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2456 start_grab (event, fader_cursor);
2458 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2460 float fraction = 1.0 - ((control_point->get_y() - control_point->line.y_position()) / control_point->line.height());
2461 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2462 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2464 show_verbose_canvas_cursor ();
2468 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2470 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2472 double cx = drag_info.current_pointer_x;
2473 double cy = drag_info.current_pointer_y;
2475 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2476 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2478 if (drag_info.x_constrained) {
2479 cx = drag_info.grab_x;
2481 if (drag_info.y_constrained) {
2482 cy = drag_info.grab_y;
2485 cp->line.parent_group().w2i (cx, cy);
2489 cy = min ((double) (cp->line.y_position() + cp->line.height()), cy);
2491 //translate cx to frames
2492 nframes_t cx_frames = unit_to_frame (cx);
2494 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2495 snap_to (cx_frames);
2498 float const fraction = 1.0 - ((cy - cp->line.y_position()) / cp->line.height());
2502 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2508 cp->line.point_drag (*cp, cx_frames , fraction, push);
2510 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2512 drag_info.first_move = false;
2516 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2518 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2520 if (drag_info.first_move) {
2524 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2525 reset_point_selection ();
2529 control_point_drag_motion_callback (item, event);
2531 cp->line.end_drag (cp);
2535 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2537 switch (mouse_mode) {
2539 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2540 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2548 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2552 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2553 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2557 start_line_grab (al, event);
2561 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2565 nframes_t frame_within_region;
2567 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2571 cx = event->button.x;
2572 cy = event->button.y;
2573 line->parent_group().w2i (cx, cy);
2574 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2576 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2577 current_line_drag_info.after)) {
2578 /* no adjacent points */
2582 drag_info.item = &line->grab_item();
2583 drag_info.data = line;
2584 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2585 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2587 start_grab (event, fader_cursor);
2589 double const fraction = 1.0 - ((cy - line->y_position()) / line->height());
2591 line->start_drag (0, drag_info.grab_frame, fraction);
2593 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2594 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2595 show_verbose_canvas_cursor ();
2599 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2601 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2602 double cx = drag_info.current_pointer_x;
2603 double cy = drag_info.current_pointer_y;
2605 line->parent_group().w2i (cx, cy);
2607 double const fraction = 1.0 - ((cy - line->y_position()) / line->height());
2611 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2617 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2619 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2623 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2625 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2626 line_drag_motion_callback (item, event);
2631 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2633 if (selection->regions.empty() || clicked_regionview == 0) {
2637 drag_info.copy = false;
2638 drag_info.item = item;
2639 drag_info.data = clicked_regionview;
2640 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2641 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2646 TimeAxisView* tvp = clicked_axisview;
2647 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2649 if (tv && tv->is_track()) {
2650 speed = tv->get_diskstream()->speed();
2653 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2654 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2655 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2656 // we want a move threshold
2657 drag_info.want_move_threshold = true;
2659 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2661 begin_reversible_command (_("move region(s)"));
2665 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2667 if (selection->regions.empty() || clicked_regionview == 0) {
2671 drag_info.copy = true;
2672 drag_info.item = item;
2673 drag_info.data = clicked_regionview;
2677 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2678 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2681 if (rtv && rtv->is_track()) {
2682 speed = rtv->get_diskstream()->speed();
2685 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2686 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2687 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2688 // we want a move threshold
2689 drag_info.want_move_threshold = true;
2690 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2691 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2692 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2696 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2698 if (selection->regions.empty() || clicked_regionview == 0) {
2702 drag_info.copy = false;
2703 drag_info.item = item;
2704 drag_info.data = clicked_regionview;
2705 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2706 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2711 TimeAxisView* tvp = clicked_axisview;
2712 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2714 if (tv && tv->is_track()) {
2715 speed = tv->get_diskstream()->speed();
2718 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2719 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2720 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2721 // we want a move threshold
2722 drag_info.want_move_threshold = true;
2723 drag_info.brushing = true;
2725 begin_reversible_command (_("Drag region brush"));
2729 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2733 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2734 nframes_t pending_region_position = 0;
2735 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2736 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2737 bool clamp_y_axis = false;
2738 vector<int32_t> height_list(512) ;
2739 vector<int32_t>::iterator j;
2741 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2743 drag_info.want_move_threshold = false; // don't copy again
2745 /* duplicate the region(s) */
2747 vector<RegionView*> new_regionviews;
2749 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2755 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2756 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2759 nrv = new AudioRegionView (*arv);
2761 nrv = new MidiRegionView (*mrv);
2766 nrv->get_canvas_group()->show ();
2768 new_regionviews.push_back (nrv);
2771 if (new_regionviews.empty()) {
2775 /* reset selection to new regionviews */
2777 selection->set (new_regionviews);
2779 /* reset drag_info data to reflect the fact that we are dragging the copies */
2781 drag_info.data = new_regionviews.front();
2783 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2786 /* Which trackview is this ? */
2788 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2789 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2791 /* The region motion is only processed if the pointer is over
2795 if (!tv || !tv->is_track()) {
2796 /* To make sure we hide the verbose canvas cursor when the mouse is
2797 not held over a track.
2799 hide_verbose_canvas_cursor ();
2803 original_pointer_order = drag_info.last_trackview->order;
2805 /************************************************************
2807 ************************************************************/
2809 if (drag_info.brushing) {
2810 clamp_y_axis = true;
2815 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2817 int32_t children = 0, numtracks = 0;
2818 // XXX hard coding track limit, oh my, so very very bad
2819 bitset <1024> tracks (0x00);
2820 /* get a bitmask representing the visible tracks */
2822 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2823 TimeAxisView *tracklist_timeview;
2824 tracklist_timeview = (*i);
2825 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2826 list<TimeAxisView*> children_list;
2828 /* zeroes are audio tracks. ones are other types. */
2830 if (!rtv2->hidden()) {
2832 if (visible_y_high < rtv2->order) {
2833 visible_y_high = rtv2->order;
2835 if (visible_y_low > rtv2->order) {
2836 visible_y_low = rtv2->order;
2839 if (!rtv2->is_track()) {
2840 tracks = tracks |= (0x01 << rtv2->order);
2843 height_list[rtv2->order] = (*i)->height;
2845 if ((children_list = rtv2->get_child_list()).size() > 0) {
2846 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2847 tracks = tracks |= (0x01 << (rtv2->order + children));
2848 height_list[rtv2->order + children] = (*j)->height;
2856 /* find the actual span according to the canvas */
2858 canvas_pointer_y_span = pointer_y_span;
2859 if (drag_info.last_trackview->order >= tv->order) {
2861 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2862 if (height_list[y] == 0 ) {
2863 canvas_pointer_y_span--;
2868 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2869 if ( height_list[y] == 0 ) {
2870 canvas_pointer_y_span++;
2875 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2876 RegionView* rv2 = (*i);
2877 double ix1, ix2, iy1, iy2;
2880 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2881 rv2->get_canvas_group()->i2w (ix1, iy1);
2882 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2883 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2885 if (rtv2->order != original_pointer_order) {
2886 /* this isn't the pointer track */
2888 if (canvas_pointer_y_span > 0) {
2890 /* moving up the canvas */
2891 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
2893 int32_t visible_tracks = 0;
2894 while (visible_tracks < canvas_pointer_y_span ) {
2897 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2898 /* we're passing through a hidden track */
2903 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2904 clamp_y_axis = true;
2908 clamp_y_axis = true;
2911 } else if (canvas_pointer_y_span < 0) {
2913 /*moving down the canvas*/
2915 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2918 int32_t visible_tracks = 0;
2920 while (visible_tracks > canvas_pointer_y_span ) {
2923 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2927 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2928 clamp_y_axis = true;
2933 clamp_y_axis = true;
2939 /* this is the pointer's track */
2940 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2941 clamp_y_axis = true;
2942 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2943 clamp_y_axis = true;
2951 } else if (drag_info.last_trackview == tv) {
2952 clamp_y_axis = true;
2956 if (!clamp_y_axis) {
2957 drag_info.last_trackview = tv;
2960 /************************************************************
2962 ************************************************************/
2964 /* compute the amount of pointer motion in frames, and where
2965 the region would be if we moved it by that much.
2968 if (drag_info.move_threshold_passed) {
2970 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2972 nframes_t sync_frame;
2973 nframes_t sync_offset;
2976 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2978 sync_offset = rv->region()->sync_offset (sync_dir);
2979 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
2981 /* we snap if the snap modifier is not enabled.
2984 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2985 snap_to (sync_frame);
2988 if (sync_frame - sync_offset <= sync_frame) {
2989 pending_region_position = sync_frame - (sync_dir*sync_offset);
2991 pending_region_position = 0;
2995 pending_region_position = 0;
2998 if (pending_region_position > max_frames - rv->region()->length()) {
2999 pending_region_position = drag_info.last_frame_position;
3002 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3004 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3006 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3007 to make it appear at the new location.
3010 if (pending_region_position > drag_info.last_frame_position) {
3011 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3013 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3016 drag_info.last_frame_position = pending_region_position;
3023 /* threshold not passed */
3028 /*************************************************************
3030 ************************************************************/
3032 if (x_delta == 0 && (pointer_y_span == 0)) {
3033 /* haven't reached next snap point, and we're not switching
3034 trackviews. nothing to do.
3041 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3043 RegionView* rv2 = (*i);
3045 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3047 double ix1, ix2, iy1, iy2;
3048 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3049 rv2->get_canvas_group()->i2w (ix1, iy1);
3058 /*************************************************************
3060 ************************************************************/
3064 if (drag_info.first_move) {
3065 if (drag_info.move_threshold_passed) {
3076 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3077 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3079 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3081 RegionView* rv = (*i);
3082 double ix1, ix2, iy1, iy2;
3083 int32_t temp_pointer_y_span = pointer_y_span;
3085 /* get item BBox, which will be relative to parent. so we have
3086 to query on a child, then convert to world coordinates using
3090 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3091 rv->get_canvas_group()->i2w (ix1, iy1);
3092 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3093 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3094 RouteTimeAxisView* temp_rtv;
3096 if ((pointer_y_span != 0) && !clamp_y_axis) {
3099 for (j = height_list.begin(); j!= height_list.end(); j++) {
3100 if (x == canvas_rtv->order) {
3101 /* we found the track the region is on */
3102 if (x != original_pointer_order) {
3103 /*this isn't from the same track we're dragging from */
3104 temp_pointer_y_span = canvas_pointer_y_span;
3106 while (temp_pointer_y_span > 0) {
3107 /* we're moving up canvas-wise,
3108 so we need to find the next track height
3110 if (j != height_list.begin()) {
3113 if (x != original_pointer_order) {
3114 /* we're not from the dragged track, so ignore hidden tracks. */
3116 temp_pointer_y_span++;
3120 temp_pointer_y_span--;
3122 while (temp_pointer_y_span < 0) {
3124 if (x != original_pointer_order) {
3126 temp_pointer_y_span--;
3130 if (j != height_list.end()) {
3133 temp_pointer_y_span++;
3135 /* find out where we'll be when we move and set height accordingly */
3137 tvp2 = trackview_by_y_position (iy1 + y_delta);
3138 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3139 rv->set_y_position_and_height (0, temp_rtv->height);
3141 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3142 personally, i think this can confuse things, but never mind.
3145 //const GdkColor& col (temp_rtv->view->get_region_color());
3146 //rv->set_color (const_cast<GdkColor&>(col));
3153 /* prevent the regionview from being moved to before
3154 the zero position on the canvas.
3159 if (-x_delta > ix1) {
3162 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3163 x_delta = max_frames - rv->region()->last_frame();
3167 if (drag_info.first_move) {
3169 /* hide any dependent views */
3171 rv->get_time_axis_view().hide_dependent_views (*rv);
3173 /* this is subtle. raising the regionview itself won't help,
3174 because raise_to_top() just puts the item on the top of
3175 its parent's stack. so, we need to put the trackview canvas_display group
3176 on the top, since its parent is the whole canvas.
3179 rv->get_canvas_group()->raise_to_top();
3180 rv->get_time_axis_view().canvas_display->raise_to_top();
3181 cursor_group->raise_to_top();
3183 rv->fake_set_opaque (true);
3186 if (drag_info.brushing) {
3187 mouse_brush_insert_region (rv, pending_region_position);
3189 rv->move (x_delta, y_delta);
3192 } /* foreach region */
3196 if (drag_info.first_move && drag_info.move_threshold_passed) {
3197 cursor_group->raise_to_top();
3198 drag_info.first_move = false;
3201 if (x_delta != 0 && !drag_info.brushing) {
3202 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3207 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3210 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3211 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3212 bool nocommit = true;
3214 RouteTimeAxisView* rtv;
3215 bool regionview_y_movement;
3216 bool regionview_x_movement;
3217 vector<RegionView*> copies;
3219 /* first_move is set to false if the regionview has been moved in the
3223 if (drag_info.first_move) {
3230 /* The regionview has been moved at some stage during the grab so we need
3231 to account for any mouse movement between this event and the last one.
3234 region_drag_motion_callback (item, event);
3236 if (drag_info.brushing) {
3237 /* all changes were made during motion event handlers */
3239 if (drag_info.copy) {
3240 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3241 copies.push_back (*i);
3248 /* adjust for track speed */
3251 rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3252 if (rtv && rtv->get_diskstream()) {
3253 speed = rtv->get_diskstream()->speed();
3256 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3257 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3259 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3260 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3264 if (drag_info.copy) {
3265 if (drag_info.x_constrained) {
3266 op_string = _("fixed time region copy");
3268 op_string = _("region copy");
3271 if (drag_info.x_constrained) {
3272 op_string = _("fixed time region drag");
3274 op_string = _("region drag");
3278 begin_reversible_command (op_string);
3280 if (regionview_y_movement) {
3282 /* moved to a different audio track. */
3284 vector<RegionView*> new_selection;
3286 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3288 RegionView* rv = (*i);
3290 double ix1, ix2, iy1, iy2;
3292 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3293 rv->get_canvas_group()->i2w (ix1, iy1);
3294 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3295 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3297 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3298 boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3300 where = (nframes_t) (unit_to_frame (ix1) * speed);
3301 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3303 /* undo the previous hide_dependent_views so that xfades don't
3304 disappear on copying regions
3307 rv->get_time_axis_view().reveal_dependent_views (*rv);
3309 if (!drag_info.copy) {
3311 /* the region that used to be in the old playlist is not
3312 moved to the new one - we make a copy of it. as a result,
3313 any existing editor for the region should no longer be
3317 rv->hide_region_editor();
3318 rv->fake_set_opaque (false);
3320 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3321 from_playlist->remove_region ((rv->region()));
3322 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3326 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3328 copies.push_back (rv);
3331 latest_regionview = 0;
3333 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3334 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3335 to_playlist->add_region (new_region, where);
3336 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3339 if (latest_regionview) {
3340 new_selection.push_back (latest_regionview);
3343 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3344 was selected in all of them, then removing it from the playlist will have removed all
3345 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3346 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3347 corresponding regionview, and the selection is now empty).
3349 this could have invalidated any and all iterators into the region selection.
3351 the heuristic we use here is: if the region selection is empty, break out of the loop
3352 here. if the region selection is not empty, then restart the loop because we know that
3353 we must have removed at least the region(view) we've just been working on as well as any
3354 that we processed on previous iterations.
3356 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3357 we can just iterate.
3360 if (drag_info.copy) {
3363 if (selection->regions.empty()) {
3366 i = selection->regions.by_layer().begin();
3371 selection->set (new_selection);
3375 /* motion within a single track */
3377 list<RegionView*> regions = selection->regions.by_layer();
3379 if (drag_info.copy) {
3380 selection->clear_regions();
3383 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3387 if (!rv->region()->can_move()) {
3391 if (regionview_x_movement) {
3392 double ownspeed = 1.0;
3393 rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3395 if (rtv && rtv->get_diskstream()) {
3396 ownspeed = rtv->get_diskstream()->speed();
3399 /* base the new region position on the current position of the regionview.*/
3401 double ix1, ix2, iy1, iy2;
3403 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3404 rv->get_canvas_group()->i2w (ix1, iy1);
3405 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3409 where = rv->region()->position();
3412 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3414 assert (to_playlist);
3418 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3420 if (drag_info.copy) {
3422 boost::shared_ptr<Region> newregion;
3423 boost::shared_ptr<Region> ar;
3424 boost::shared_ptr<Region> mr;
3426 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3427 newregion = RegionFactory::create (ar);
3428 } else if ((mr = boost::dynamic_pointer_cast<MidiRegion>(rv->region())) != 0) {
3429 newregion = RegionFactory::create (mr);
3434 latest_regionview = 0;
3435 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3436 to_playlist->add_region (newregion, (nframes_t) (where * rtv->get_diskstream()->speed()));
3439 if (latest_regionview) {
3440 rtv->reveal_dependent_views (*latest_regionview);
3441 selection->add (latest_regionview);
3446 /* just change the model */
3448 rv->region()->set_position (where, (void*) this);
3454 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3456 if (drag_info.copy) {
3457 copies.push_back (rv);
3465 commit_reversible_command ();
3468 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3474 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3476 /* Either add to or set the set the region selection, unless
3477 this is an alignment click (control used)
3480 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3481 TimeAxisView* tv = &rv.get_time_axis_view();
3482 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3484 if (rtv && rtv->is_track()) {
3485 speed = rtv->get_diskstream()->speed();
3488 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3490 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3492 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3494 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3498 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3504 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3510 nframes_t frame_rate;
3517 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3518 case AudioClock::BBT:
3519 session->bbt_time (frame, bbt);
3520 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3523 case AudioClock::SMPTE:
3524 session->smpte_time (frame, smpte);
3525 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3528 case AudioClock::MinSec:
3529 /* XXX this is copied from show_verbose_duration_cursor() */
3530 frame_rate = session->frame_rate();
3531 hours = frame / (frame_rate * 3600);
3532 frame = frame % (frame_rate * 3600);
3533 mins = frame / (frame_rate * 60);
3534 frame = frame % (frame_rate * 60);
3535 secs = (float) frame / (float) frame_rate;
3536 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3540 snprintf (buf, sizeof(buf), "%u", frame);
3544 if (xpos >= 0 && ypos >=0) {
3545 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3548 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3550 show_verbose_canvas_cursor ();
3554 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3561 nframes_t distance, frame_rate;
3563 Meter meter_at_start(session->tempo_map().meter_at(start));
3569 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3570 case AudioClock::BBT:
3571 session->bbt_time (start, sbbt);
3572 session->bbt_time (end, ebbt);
3575 /* XXX this computation won't work well if the
3576 user makes a selection that spans any meter changes.
3579 ebbt.bars -= sbbt.bars;
3580 if (ebbt.beats >= sbbt.beats) {
3581 ebbt.beats -= sbbt.beats;
3584 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3586 if (ebbt.ticks >= sbbt.ticks) {
3587 ebbt.ticks -= sbbt.ticks;
3590 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3593 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3596 case AudioClock::SMPTE:
3597 session->smpte_duration (end - start, smpte);
3598 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3601 case AudioClock::MinSec:
3602 /* XXX this stuff should be elsewhere.. */
3603 distance = end - start;
3604 frame_rate = session->frame_rate();
3605 hours = distance / (frame_rate * 3600);
3606 distance = distance % (frame_rate * 3600);
3607 mins = distance / (frame_rate * 60);
3608 distance = distance % (frame_rate * 60);
3609 secs = (float) distance / (float) frame_rate;
3610 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3614 snprintf (buf, sizeof(buf), "%u", end - start);
3618 if (xpos >= 0 && ypos >=0) {
3619 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3622 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3624 show_verbose_canvas_cursor ();
3628 Editor::collect_new_region_view (RegionView* rv)
3630 latest_regionview = rv;
3634 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3636 if (clicked_regionview == 0) {
3640 /* lets try to create new Region for the selection */
3642 vector<boost::shared_ptr<AudioRegion> > new_regions;
3643 create_region_from_selection (new_regions);
3645 if (new_regions.empty()) {
3649 /* XXX fix me one day to use all new regions */
3651 boost::shared_ptr<Region> region (new_regions.front());
3653 /* add it to the current stream/playlist.
3655 tricky: the streamview for the track will add a new regionview. we will
3656 catch the signal it sends when it creates the regionview to
3657 set the regionview we want to then drag.
3660 latest_regionview = 0;
3661 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3663 /* A selection grab currently creates two undo/redo operations, one for
3664 creating the new region and another for moving it.
3667 begin_reversible_command (_("selection grab"));
3669 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3671 XMLNode *before = &(playlist->get_state());
3672 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3673 XMLNode *after = &(playlist->get_state());
3674 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3676 commit_reversible_command ();
3680 if (latest_regionview == 0) {
3681 /* something went wrong */
3685 /* we need to deselect all other regionviews, and select this one
3686 i'm ignoring undo stuff, because the region creation will take care of it */
3687 selection->set (latest_regionview);
3689 drag_info.item = latest_regionview->get_canvas_group();
3690 drag_info.data = latest_regionview;
3691 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3692 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3696 drag_info.last_trackview = clicked_axisview;
3697 drag_info.last_frame_position = latest_regionview->region()->position();
3698 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3700 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3704 Editor::cancel_selection ()
3706 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3707 (*i)->hide_selection ();
3709 begin_reversible_command (_("cancel selection"));
3710 selection->clear ();
3711 clicked_selection = 0;
3712 commit_reversible_command ();
3716 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3718 nframes_t start = 0;
3725 drag_info.item = item;
3726 drag_info.motion_callback = &Editor::drag_selection;
3727 drag_info.finished_callback = &Editor::end_selection_op;
3732 case CreateSelection:
3733 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3734 drag_info.copy = true;
3736 drag_info.copy = false;
3738 start_grab (event, selector_cursor);
3741 case SelectionStartTrim:
3742 if (clicked_axisview) {
3743 clicked_axisview->order_selection_trims (item, true);
3745 start_grab (event, trimmer_cursor);
3746 start = selection->time[clicked_selection].start;
3747 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3750 case SelectionEndTrim:
3751 if (clicked_axisview) {
3752 clicked_axisview->order_selection_trims (item, false);
3754 start_grab (event, trimmer_cursor);
3755 end = selection->time[clicked_selection].end;
3756 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3760 start = selection->time[clicked_selection].start;
3762 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3766 if (selection_op == SelectionMove) {
3767 show_verbose_time_cursor(start, 10);
3769 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3774 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3776 nframes_t start = 0;
3779 nframes_t pending_position;
3781 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3782 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3784 pending_position = 0;
3787 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3788 snap_to (pending_position);
3791 /* only alter selection if the current frame is
3792 different from the last frame position (adjusted)
3795 if (pending_position == drag_info.last_pointer_frame) return;
3797 switch (selection_op) {
3798 case CreateSelection:
3800 if (drag_info.first_move) {
3801 snap_to (drag_info.grab_frame);
3804 if (pending_position < drag_info.grab_frame) {
3805 start = pending_position;
3806 end = drag_info.grab_frame;
3808 end = pending_position;
3809 start = drag_info.grab_frame;
3812 /* first drag: Either add to the selection
3813 or create a new selection->
3816 if (drag_info.first_move) {
3818 begin_reversible_command (_("range selection"));
3820 if (drag_info.copy) {
3821 /* adding to the selection */
3822 clicked_selection = selection->add (start, end);
3823 drag_info.copy = false;
3825 /* new selection-> */
3826 clicked_selection = selection->set (clicked_axisview, start, end);
3831 case SelectionStartTrim:
3833 if (drag_info.first_move) {
3834 begin_reversible_command (_("trim selection start"));
3837 start = selection->time[clicked_selection].start;
3838 end = selection->time[clicked_selection].end;
3840 if (pending_position > end) {
3843 start = pending_position;
3847 case SelectionEndTrim:
3849 if (drag_info.first_move) {
3850 begin_reversible_command (_("trim selection end"));
3853 start = selection->time[clicked_selection].start;
3854 end = selection->time[clicked_selection].end;
3856 if (pending_position < start) {
3859 end = pending_position;
3866 if (drag_info.first_move) {
3867 begin_reversible_command (_("move selection"));
3870 start = selection->time[clicked_selection].start;
3871 end = selection->time[clicked_selection].end;
3873 length = end - start;
3875 start = pending_position;
3878 end = start + length;
3883 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3884 start_canvas_autoscroll (1);
3888 selection->replace (clicked_selection, start, end);
3891 drag_info.last_pointer_frame = pending_position;
3892 drag_info.first_move = false;
3894 if (selection_op == SelectionMove) {
3895 show_verbose_time_cursor(start, 10);
3897 show_verbose_time_cursor(pending_position, 10);
3902 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3904 if (!drag_info.first_move) {
3905 drag_selection (item, event);
3906 /* XXX this is not object-oriented programming at all. ick */
3907 if (selection->time.consolidate()) {
3908 selection->TimeChanged ();
3910 commit_reversible_command ();
3912 /* just a click, no pointer movement.*/
3914 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3916 selection->clear_time();
3921 /* XXX what happens if its a music selection? */
3922 session->set_audio_range (selection->time);
3923 stop_canvas_autoscroll ();
3927 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3930 TimeAxisView* tvp = clicked_axisview;
3931 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3933 if (tv && tv->is_track()) {
3934 speed = tv->get_diskstream()->speed();
3937 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3938 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3939 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3941 //drag_info.item = clicked_regionview->get_name_highlight();
3942 drag_info.item = item;
3943 drag_info.motion_callback = &Editor::trim_motion_callback;
3944 drag_info.finished_callback = &Editor::trim_finished_callback;
3946 start_grab (event, trimmer_cursor);
3948 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3949 trim_op = ContentsTrim;
3951 /* These will get overridden for a point trim.*/
3952 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3953 /* closer to start */
3954 trim_op = StartTrim;
3955 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3963 show_verbose_time_cursor(region_start, 10);
3966 show_verbose_time_cursor(region_end, 10);
3969 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3975 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3977 RegionView* rv = clicked_regionview;
3978 nframes_t frame_delta = 0;
3979 bool left_direction;
3980 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3982 /* snap modifier works differently here..
3983 its' current state has to be passed to the
3984 various trim functions in order to work properly
3988 TimeAxisView* tvp = clicked_axisview;
3989 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3990 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3992 if (tv && tv->is_track()) {
3993 speed = tv->get_diskstream()->speed();
3996 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3997 left_direction = true;
3999 left_direction = false;
4003 snap_to (drag_info.current_pointer_frame);
4006 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4010 if (drag_info.first_move) {
4016 trim_type = "Region start trim";
4019 trim_type = "Region end trim";
4022 trim_type = "Region content trim";
4026 begin_reversible_command (trim_type);
4028 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4029 (*i)->fake_set_opaque(false);
4030 (*i)->region()->freeze ();
4032 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4034 arv->temporarily_hide_envelope ();
4036 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4037 insert_result = motion_frozen_playlists.insert (pl);
4038 if (insert_result.second) {
4039 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4044 if (left_direction) {
4045 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4047 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4052 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4055 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4056 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4062 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4065 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4066 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4073 bool swap_direction = false;
4075 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4076 swap_direction = true;
4079 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4080 i != selection->regions.by_layer().end(); ++i)
4082 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4090 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4093 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4096 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4100 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4101 drag_info.first_move = false;
4105 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4107 boost::shared_ptr<Region> region (rv.region());
4109 if (region->locked()) {
4113 nframes_t new_bound;
4116 TimeAxisView* tvp = clicked_axisview;
4117 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4119 if (tv && tv->is_track()) {
4120 speed = tv->get_diskstream()->speed();
4123 if (left_direction) {
4124 if (swap_direction) {
4125 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4127 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4130 if (swap_direction) {
4131 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4133 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4138 snap_to (new_bound);
4140 region->trim_start ((nframes_t) (new_bound * speed), this);
4141 rv.region_changed (StartChanged);
4145 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4147 boost::shared_ptr<Region> region (rv.region());
4149 if (region->locked()) {
4153 nframes_t new_bound;
4156 TimeAxisView* tvp = clicked_axisview;
4157 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4159 if (tv && tv->is_track()) {
4160 speed = tv->get_diskstream()->speed();
4163 if (left_direction) {
4164 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4166 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4170 snap_to (new_bound, (left_direction ? 0 : 1));
4173 region->trim_front ((nframes_t) (new_bound * speed), this);
4175 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4179 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4181 boost::shared_ptr<Region> region (rv.region());
4183 if (region->locked()) {
4187 nframes_t new_bound;
4190 TimeAxisView* tvp = clicked_axisview;
4191 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4193 if (tv && tv->is_track()) {
4194 speed = tv->get_diskstream()->speed();
4197 if (left_direction) {
4198 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4200 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4204 snap_to (new_bound);
4206 region->trim_end ((nframes_t) (new_bound * speed), this);
4207 rv.region_changed (LengthChanged);
4211 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4213 if (!drag_info.first_move) {
4214 trim_motion_callback (item, event);
4216 if (!clicked_regionview->get_selected()) {
4217 thaw_region_after_trim (*clicked_regionview);
4220 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4221 i != selection->regions.by_layer().end(); ++i)
4223 thaw_region_after_trim (**i);
4224 (*i)->fake_set_opaque (true);
4228 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4230 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4233 motion_frozen_playlists.clear ();
4235 commit_reversible_command();
4237 /* no mouse movement */
4243 Editor::point_trim (GdkEvent* event)
4245 RegionView* rv = clicked_regionview;
4246 nframes_t new_bound = drag_info.current_pointer_frame;
4248 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4249 snap_to (new_bound);
4252 /* Choose action dependant on which button was pressed */
4253 switch (event->button.button) {
4255 trim_op = StartTrim;
4256 begin_reversible_command (_("Start point trim"));
4258 if (rv->get_selected()) {
4260 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4261 i != selection->regions.by_layer().end(); ++i)
4263 if (!(*i)->region()->locked()) {
4264 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4265 XMLNode &before = pl->get_state();
4266 (*i)->region()->trim_front (new_bound, this);
4267 XMLNode &after = pl->get_state();
4268 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4274 if (!rv->region()->locked()) {
4275 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4276 XMLNode &before = pl->get_state();
4277 rv->region()->trim_front (new_bound, this);
4278 XMLNode &after = pl->get_state();
4279 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4283 commit_reversible_command();
4288 begin_reversible_command (_("End point trim"));
4290 if (rv->get_selected()) {
4292 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4294 if (!(*i)->region()->locked()) {
4295 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4296 XMLNode &before = pl->get_state();
4297 (*i)->region()->trim_end (new_bound, this);
4298 XMLNode &after = pl->get_state();
4299 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4305 if (!rv->region()->locked()) {
4306 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4307 XMLNode &before = pl->get_state();
4308 rv->region()->trim_end (new_bound, this);
4309 XMLNode &after = pl->get_state();
4310 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4314 commit_reversible_command();
4323 Editor::thaw_region_after_trim (RegionView& rv)
4325 boost::shared_ptr<Region> region (rv.region());
4327 if (region->locked()) {
4331 region->thaw (_("trimmed region"));
4332 XMLNode &after = region->playlist()->get_state();
4333 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4335 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4337 arv->unhide_envelope ();
4341 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4346 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4347 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4351 Location* location = find_location_from_marker (marker, is_start);
4352 location->set_hidden (true, this);
4357 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4363 drag_info.item = item;
4364 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4365 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4367 range_marker_op = op;
4369 if (!temp_location) {
4370 temp_location = new Location;
4374 case CreateRangeMarker:
4375 case CreateTransportMarker:
4377 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4378 drag_info.copy = true;
4380 drag_info.copy = false;
4382 start_grab (event, selector_cursor);
4386 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4391 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4393 nframes_t start = 0;
4395 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4397 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4398 snap_to (drag_info.current_pointer_frame);
4401 /* only alter selection if the current frame is
4402 different from the last frame position.
4405 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4407 switch (range_marker_op) {
4408 case CreateRangeMarker:
4409 case CreateTransportMarker:
4410 if (drag_info.first_move) {
4411 snap_to (drag_info.grab_frame);
4414 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4415 start = drag_info.current_pointer_frame;
4416 end = drag_info.grab_frame;
4418 end = drag_info.current_pointer_frame;
4419 start = drag_info.grab_frame;
4422 /* first drag: Either add to the selection
4423 or create a new selection.
4426 if (drag_info.first_move) {
4428 temp_location->set (start, end);
4432 update_marker_drag_item (temp_location);
4433 range_marker_drag_rect->show();
4434 range_marker_drag_rect->raise_to_top();
4440 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4441 start_canvas_autoscroll (1);
4445 temp_location->set (start, end);
4447 double x1 = frame_to_pixel (start);
4448 double x2 = frame_to_pixel (end);
4449 crect->property_x1() = x1;
4450 crect->property_x2() = x2;
4452 update_marker_drag_item (temp_location);
4455 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4456 drag_info.first_move = false;
4458 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4463 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4465 Location * newloc = 0;
4468 if (!drag_info.first_move) {
4469 drag_range_markerbar_op (item, event);
4471 switch (range_marker_op) {
4472 case CreateRangeMarker:
4474 begin_reversible_command (_("new range marker"));
4475 XMLNode &before = session->locations()->get_state();
4476 session->locations()->next_available_name(rangename,"unnamed");
4477 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4478 session->locations()->add (newloc, true);
4479 XMLNode &after = session->locations()->get_state();
4480 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4481 commit_reversible_command ();
4483 range_bar_drag_rect->hide();
4484 range_marker_drag_rect->hide();
4488 case CreateTransportMarker:
4489 // popup menu to pick loop or punch
4490 new_transport_marker_context_menu (&event->button, item);
4495 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4497 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4502 start = session->locations()->first_mark_before (drag_info.grab_frame);
4503 end = session->locations()->first_mark_after (drag_info.grab_frame);
4505 if (end == max_frames) {
4506 end = session->current_end_frame ();
4510 start = session->current_start_frame ();
4513 switch (mouse_mode) {
4515 /* find the two markers on either side and then make the selection from it */
4516 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4520 /* find the two markers on either side of the click and make the range out of it */
4521 selection->set (0, start, end);
4530 stop_canvas_autoscroll ();
4536 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4538 drag_info.item = item;
4539 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4540 drag_info.finished_callback = &Editor::end_mouse_zoom;
4542 start_grab (event, zoom_cursor);
4544 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4548 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4553 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4554 snap_to (drag_info.current_pointer_frame);
4556 if (drag_info.first_move) {
4557 snap_to (drag_info.grab_frame);
4561 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4563 /* base start and end on initial click position */
4564 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4565 start = drag_info.current_pointer_frame;
4566 end = drag_info.grab_frame;
4568 end = drag_info.current_pointer_frame;
4569 start = drag_info.grab_frame;
4574 if (drag_info.first_move) {
4576 zoom_rect->raise_to_top();
4579 reposition_zoom_rect(start, end);
4581 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4582 drag_info.first_move = false;
4584 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4589 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4591 if (!drag_info.first_move) {
4592 drag_mouse_zoom (item, event);
4594 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4595 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4597 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4600 temporal_zoom_to_frame (false, drag_info.grab_frame);
4602 temporal_zoom_step (false);
4603 center_screen (drag_info.grab_frame);
4611 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4613 double x1 = frame_to_pixel (start);
4614 double x2 = frame_to_pixel (end);
4615 double y2 = full_canvas_height - 1.0;
4617 zoom_rect->property_x1() = x1;
4618 zoom_rect->property_y1() = 1.0;
4619 zoom_rect->property_x2() = x2;
4620 zoom_rect->property_y2() = y2;
4624 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4626 drag_info.item = item;
4627 drag_info.motion_callback = &Editor::drag_rubberband_select;
4628 drag_info.finished_callback = &Editor::end_rubberband_select;
4630 start_grab (event, cross_hair_cursor);
4632 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4636 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4643 /* use a bigger drag threshold than the default */
4645 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4649 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4650 if (drag_info.first_move) {
4651 snap_to (drag_info.grab_frame);
4653 snap_to (drag_info.current_pointer_frame);
4656 /* base start and end on initial click position */
4658 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4659 start = drag_info.current_pointer_frame;
4660 end = drag_info.grab_frame;
4662 end = drag_info.current_pointer_frame;
4663 start = drag_info.grab_frame;
4666 if (drag_info.current_pointer_y < drag_info.grab_y) {
4667 y1 = drag_info.current_pointer_y;
4668 y2 = drag_info.grab_y;
4670 y2 = drag_info.current_pointer_y;
4671 y1 = drag_info.grab_y;
4675 if (start != end || y1 != y2) {
4677 double x1 = frame_to_pixel (start);
4678 double x2 = frame_to_pixel (end);
4680 rubberband_rect->property_x1() = x1;
4681 rubberband_rect->property_y1() = y1;
4682 rubberband_rect->property_x2() = x2;
4683 rubberband_rect->property_y2() = y2;
4685 rubberband_rect->show();
4686 rubberband_rect->raise_to_top();
4688 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4689 drag_info.first_move = false;
4691 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4696 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4698 if (!drag_info.first_move) {
4700 drag_rubberband_select (item, event);
4703 if (drag_info.current_pointer_y < drag_info.grab_y) {
4704 y1 = drag_info.current_pointer_y;
4705 y2 = drag_info.grab_y;
4708 y2 = drag_info.current_pointer_y;
4709 y1 = drag_info.grab_y;
4713 Selection::Operation op = Keyboard::selection_type (event->button.state);
4716 begin_reversible_command (_("rubberband selection"));
4718 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4719 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4721 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4725 commit_reversible_command ();
4729 selection->clear_tracks();
4730 selection->clear_regions();
4731 selection->clear_points ();
4732 selection->clear_lines ();
4735 rubberband_rect->hide();
4740 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4742 using namespace Gtkmm2ext;
4744 ArdourPrompter prompter (false);
4746 prompter.set_prompt (_("Name for region:"));
4747 prompter.set_initial_text (clicked_regionview->region()->name());
4748 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4749 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4750 prompter.show_all ();
4751 switch (prompter.run ()) {
4752 case Gtk::RESPONSE_ACCEPT:
4754 prompter.get_result(str);
4756 clicked_regionview->region()->set_name (str);
4764 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4766 drag_info.item = item;
4767 drag_info.motion_callback = &Editor::time_fx_motion;
4768 drag_info.finished_callback = &Editor::end_time_fx;
4772 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4776 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4778 RegionView* rv = clicked_regionview;
4780 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4781 snap_to (drag_info.current_pointer_frame);
4784 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4788 if (drag_info.current_pointer_frame > rv->region()->position()) {
4789 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4792 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4793 drag_info.first_move = false;
4795 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4799 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4801 clicked_regionview->get_time_axis_view().hide_timestretch ();
4803 if (drag_info.first_move) {
4807 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4808 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4810 begin_reversible_command (_("timestretch"));
4812 if (run_timestretch (selection->regions, percentage) == 0) {
4813 session->commit_reversible_command ();
4818 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4820 /* no brushing without a useful snap setting */
4823 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4826 switch (snap_mode) {
4828 return; /* can't work because it allows region to be placed anywhere */
4833 switch (snap_type) {
4836 case SnapToEditCursor:
4843 /* don't brush a copy over the original */
4845 if (pos == rv->region()->position()) {
4849 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4851 if (rtv == 0 || !rtv->is_track()) {
4855 boost::shared_ptr<Playlist> playlist = rtv->playlist();
4856 double speed = rtv->get_diskstream()->speed();
4858 XMLNode &before = playlist->get_state();
4859 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4860 XMLNode &after = playlist->get_state();
4861 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4863 // playlist is frozen, so we have to update manually
4865 playlist->Modified(); /* EMIT SIGNAL */
4869 Editor::track_height_step_timeout ()
4872 struct timeval delta;
4874 gettimeofday (&now, 0);
4875 timersub (&now, &last_track_height_step_timestamp, &delta);
4877 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4878 current_stepping_trackview = 0;