2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include <pbd/error.h>
29 #include <gtkmm2ext/utils.h>
30 #include <pbd/memento_command.h>
32 #include "ardour_ui.h"
34 #include "time_axis_view.h"
35 #include "audio_time_axis.h"
36 #include "audio_region_view.h"
38 #include "streamview.h"
39 #include "region_gain_line.h"
40 #include "automation_time_axis.h"
43 #include "selection.h"
46 #include "rgb_macros.h"
48 #include <ardour/types.h>
49 #include <ardour/route.h>
50 #include <ardour/audio_track.h>
51 #include <ardour/audio_diskstream.h>
52 #include <ardour/playlist.h>
53 #include <ardour/audioplaylist.h>
54 #include <ardour/audioregion.h>
55 #include <ardour/dB.h>
56 #include <ardour/utils.h>
57 #include <ardour/region_factory.h>
64 using namespace ARDOUR;
68 using namespace Editing;
71 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
85 switch (event->type) {
86 case GDK_BUTTON_RELEASE:
87 case GDK_BUTTON_PRESS:
88 case GDK_2BUTTON_PRESS:
89 case GDK_3BUTTON_PRESS:
90 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
92 case GDK_MOTION_NOTIFY:
93 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
95 case GDK_ENTER_NOTIFY:
96 case GDK_LEAVE_NOTIFY:
97 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
100 case GDK_KEY_RELEASE:
101 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
104 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
108 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
109 position is negative (as can be the case with motion events in particular),
110 the frame location is always positive.
113 return pixel_to_frame (*pcx);
117 Editor::mouse_mode_toggled (MouseMode m)
119 if (ignore_mouse_mode_toggle) {
125 if (mouse_select_button.get_active()) {
131 if (mouse_move_button.get_active()) {
137 if (mouse_gain_button.get_active()) {
143 if (mouse_zoom_button.get_active()) {
149 if (mouse_timefx_button.get_active()) {
155 if (mouse_audition_button.get_active()) {
166 Editor::set_mouse_mode (MouseMode m, bool force)
168 if (drag_info.item) {
172 if (!force && m == mouse_mode) {
180 if (mouse_mode != MouseRange) {
182 /* in all modes except range, hide the range selection,
183 show the object (region) selection.
186 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
187 (*i)->set_should_show_selection (true);
189 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
190 (*i)->hide_selection ();
196 in range mode,show the range selection.
199 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
200 if ((*i)->get_selected()) {
201 (*i)->show_selection (selection->time);
206 /* XXX the hack of unsetting all other buttongs should go
207 away once GTK2 allows us to use regular radio buttons drawn like
208 normal buttons, rather than my silly GroupedButton hack.
211 ignore_mouse_mode_toggle = true;
213 switch (mouse_mode) {
215 mouse_select_button.set_active (true);
216 current_canvas_cursor = selector_cursor;
220 mouse_move_button.set_active (true);
221 current_canvas_cursor = grabber_cursor;
225 mouse_gain_button.set_active (true);
226 current_canvas_cursor = cross_hair_cursor;
230 mouse_zoom_button.set_active (true);
231 current_canvas_cursor = zoom_cursor;
235 mouse_timefx_button.set_active (true);
236 current_canvas_cursor = time_fx_cursor; // just use playhead
240 mouse_audition_button.set_active (true);
241 current_canvas_cursor = speaker_cursor;
245 ignore_mouse_mode_toggle = false;
248 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
253 Editor::step_mouse_mode (bool next)
255 switch (current_mouse_mode()) {
257 if (next) set_mouse_mode (MouseRange);
258 else set_mouse_mode (MouseTimeFX);
262 if (next) set_mouse_mode (MouseZoom);
263 else set_mouse_mode (MouseObject);
267 if (next) set_mouse_mode (MouseGain);
268 else set_mouse_mode (MouseRange);
272 if (next) set_mouse_mode (MouseTimeFX);
273 else set_mouse_mode (MouseZoom);
277 if (next) set_mouse_mode (MouseAudition);
278 else set_mouse_mode (MouseGain);
282 if (next) set_mouse_mode (MouseObject);
283 else set_mouse_mode (MouseTimeFX);
289 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
293 /* in object/audition/timefx mode, any button press sets
294 the selection if the object can be selected. this is a
295 bit of hack, because we want to avoid this if the
296 mouse operation is a region alignment.
298 note: not dbl-click or triple-click
301 if (((mouse_mode != MouseObject) &&
302 (mouse_mode != MouseAudition || item_type != RegionItem) &&
303 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
304 (mouse_mode != MouseRange)) ||
306 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
311 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
313 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
315 /* almost no selection action on modified button-2 or button-3 events */
317 if (item_type != RegionItem && event->button.button != 2) {
323 Selection::Operation op = Keyboard::selection_type (event->button.state);
324 bool press = (event->type == GDK_BUTTON_PRESS);
326 // begin_reversible_command (_("select on click"));
330 if (mouse_mode != MouseRange) {
331 commit = set_selected_regionview_from_click (press, op, true);
332 } else if (event->type == GDK_BUTTON_PRESS) {
333 commit = set_selected_track_from_click (press, op, false);
337 case RegionViewNameHighlight:
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 FadeInHandleItem:
348 case FadeOutHandleItem:
350 if (mouse_mode != MouseRange) {
351 commit = set_selected_regionview_from_click (press, op, true);
352 } else if (event->type == GDK_BUTTON_PRESS) {
353 commit = set_selected_track_from_click (press, op, false);
357 case GainAutomationControlPointItem:
358 case PanAutomationControlPointItem:
359 case RedirectAutomationControlPointItem:
360 if (mouse_mode != MouseRange) {
361 commit = set_selected_control_point_from_click (op, false);
366 /* for context click or range selection, select track */
367 if (event->button.button == 3) {
368 commit = set_selected_track_from_click (press, op, true);
369 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
370 commit = set_selected_track_from_click (press, op, false);
374 case AutomationTrackItem:
375 commit = set_selected_track_from_click (press, op, true);
383 // commit_reversible_command ();
388 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
390 track_canvas.grab_focus();
392 if (session && session->actively_recording()) {
396 button_selection (item, event, item_type);
398 if (drag_info.item == 0 &&
399 (Keyboard::is_delete_event (&event->button) ||
400 Keyboard::is_context_menu_event (&event->button) ||
401 Keyboard::is_edit_event (&event->button))) {
403 /* handled by button release */
407 switch (event->button.button) {
410 if (event->type == GDK_BUTTON_PRESS) {
412 if (drag_info.item) {
413 drag_info.item->ungrab (event->button.time);
416 /* single mouse clicks on any of these item types operate
417 independent of mouse mode, mostly because they are
418 not on the main track canvas or because we want
424 case PlayheadCursorItem:
425 start_cursor_grab (item, event);
429 if (Keyboard::modifier_state_equals (event->button.state,
430 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
431 hide_marker (item, event);
433 start_marker_grab (item, event);
437 case TempoMarkerItem:
438 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
439 start_tempo_marker_copy_grab (item, event);
441 start_tempo_marker_grab (item, event);
445 case MeterMarkerItem:
446 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
447 start_meter_marker_copy_grab (item, event);
449 start_meter_marker_grab (item, event);
459 case RangeMarkerBarItem:
460 start_range_markerbar_op (item, event, CreateRangeMarker);
464 case TransportMarkerBarItem:
465 start_range_markerbar_op (item, event, CreateTransportMarker);
474 switch (mouse_mode) {
477 case StartSelectionTrimItem:
478 start_selection_op (item, event, SelectionStartTrim);
481 case EndSelectionTrimItem:
482 start_selection_op (item, event, SelectionEndTrim);
486 if (Keyboard::modifier_state_contains
487 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
488 // contains and not equals because I can't use alt as a modifier alone.
489 start_selection_grab (item, event);
490 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
491 /* grab selection for moving */
492 start_selection_op (item, event, SelectionMove);
495 /* this was debated, but decided the more common action was to
496 make a new selection */
497 start_selection_op (item, event, CreateSelection);
502 start_selection_op (item, event, CreateSelection);
508 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
509 event->type == GDK_BUTTON_PRESS) {
511 start_rubberband_select (item, event);
513 } else if (event->type == GDK_BUTTON_PRESS) {
516 case FadeInHandleItem:
517 start_fade_in_grab (item, event);
520 case FadeOutHandleItem:
521 start_fade_out_grab (item, event);
525 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
526 start_region_copy_grab (item, event);
527 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
528 start_region_brush_grab (item, event);
530 start_region_grab (item, event);
534 case RegionViewNameHighlight:
535 start_trim (item, event);
540 /* rename happens on edit clicks */
541 start_trim (clicked_regionview->get_name_highlight(), event);
545 case GainAutomationControlPointItem:
546 case PanAutomationControlPointItem:
547 case RedirectAutomationControlPointItem:
548 start_control_point_grab (item, event);
552 case GainAutomationLineItem:
553 case PanAutomationLineItem:
554 case RedirectAutomationLineItem:
555 start_line_grab_from_line (item, event);
560 case AutomationTrackItem:
561 start_rubberband_select (item, event);
564 /* <CMT Additions> */
565 case ImageFrameHandleStartItem:
566 imageframe_start_handle_op(item, event) ;
569 case ImageFrameHandleEndItem:
570 imageframe_end_handle_op(item, event) ;
573 case MarkerViewHandleStartItem:
574 markerview_item_start_handle_op(item, event) ;
577 case MarkerViewHandleEndItem:
578 markerview_item_end_handle_op(item, event) ;
581 /* </CMT Additions> */
583 /* <CMT Additions> */
585 start_markerview_grab(item, event) ;
588 start_imageframe_grab(item, event) ;
590 /* </CMT Additions> */
606 // start_line_grab_from_regionview (item, event);
609 case GainControlPointItem:
610 start_control_point_grab (item, event);
614 start_line_grab_from_line (item, event);
617 case GainAutomationControlPointItem:
618 case PanAutomationControlPointItem:
619 case RedirectAutomationControlPointItem:
620 start_control_point_grab (item, event);
631 case GainAutomationControlPointItem:
632 case PanAutomationControlPointItem:
633 case RedirectAutomationControlPointItem:
634 start_control_point_grab (item, event);
637 case GainAutomationLineItem:
638 case PanAutomationLineItem:
639 case RedirectAutomationLineItem:
640 start_line_grab_from_line (item, event);
644 // XXX need automation mode to identify which
646 // start_line_grab_from_regionview (item, event);
656 if (event->type == GDK_BUTTON_PRESS) {
657 start_mouse_zoom (item, event);
664 if (item_type == RegionItem) {
665 start_time_fx (item, event);
670 /* handled in release */
679 switch (mouse_mode) {
681 if (event->type == GDK_BUTTON_PRESS) {
684 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
685 start_region_copy_grab (item, event);
687 start_region_grab (item, event);
691 case GainAutomationControlPointItem:
692 case PanAutomationControlPointItem:
693 case RedirectAutomationControlPointItem:
694 start_control_point_grab (item, event);
705 case RegionViewNameHighlight:
706 start_trim (item, event);
711 start_trim (clicked_regionview->get_name_highlight(), event);
722 if (event->type == GDK_BUTTON_PRESS) {
723 /* relax till release */
730 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
731 temporal_zoom_session();
733 temporal_zoom_to_frame (true, event_frame(event));
756 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
758 nframes_t where = event_frame (event, 0, 0);
760 /* no action if we're recording */
762 if (session && session->actively_recording()) {
766 /* first, see if we're finishing a drag ... */
768 if (drag_info.item) {
769 if (end_grab (item, event)) {
770 /* grab dragged, so do nothing else */
775 button_selection (item, event, item_type);
777 /* edit events get handled here */
779 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
785 case TempoMarkerItem:
786 edit_tempo_marker (item);
789 case MeterMarkerItem:
790 edit_meter_marker (item);
794 if (clicked_regionview->name_active()) {
795 return mouse_rename_region (item, event);
805 /* context menu events get handled here */
807 if (Keyboard::is_context_menu_event (&event->button)) {
809 if (drag_info.item == 0) {
811 /* no matter which button pops up the context menu, tell the menu
812 widget to use button 1 to drive menu selection.
817 case FadeInHandleItem:
819 case FadeOutHandleItem:
820 popup_fade_context_menu (1, event->button.time, item, item_type);
824 popup_track_context_menu (1, event->button.time, item_type, false, where);
828 case RegionViewNameHighlight:
830 popup_track_context_menu (1, event->button.time, item_type, false, where);
834 popup_track_context_menu (1, event->button.time, item_type, true, where);
837 case AutomationTrackItem:
838 popup_track_context_menu (1, event->button.time, item_type, false, where);
842 case RangeMarkerBarItem:
843 case TransportMarkerBarItem:
846 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
850 marker_context_menu (&event->button, item);
853 case TempoMarkerItem:
854 tm_marker_context_menu (&event->button, item);
857 case MeterMarkerItem:
858 tm_marker_context_menu (&event->button, item);
861 case CrossfadeViewItem:
862 popup_track_context_menu (1, event->button.time, item_type, false, where);
865 /* <CMT Additions> */
867 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
869 case ImageFrameTimeAxisItem:
870 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
873 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
875 case MarkerTimeAxisItem:
876 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
878 /* <CMT Additions> */
889 /* delete events get handled here */
891 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
894 case TempoMarkerItem:
895 remove_tempo_marker (item);
898 case MeterMarkerItem:
899 remove_meter_marker (item);
903 remove_marker (*item, event);
907 if (mouse_mode == MouseObject) {
908 remove_clicked_region ();
912 case GainControlPointItem:
913 if (mouse_mode == MouseGain) {
914 remove_gain_control_point (item, event);
918 case GainAutomationControlPointItem:
919 case PanAutomationControlPointItem:
920 case RedirectAutomationControlPointItem:
921 remove_control_point (item, event);
930 switch (event->button.button) {
934 /* see comments in button_press_handler */
936 case PlayheadCursorItem:
939 case GainAutomationLineItem:
940 case PanAutomationLineItem:
941 case RedirectAutomationLineItem:
942 case StartSelectionTrimItem:
943 case EndSelectionTrimItem:
947 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
948 snap_to (where, 0, true);
950 mouse_add_new_marker (where);
954 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
957 mouse_add_new_tempo_event (where);
961 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
969 switch (mouse_mode) {
972 case AutomationTrackItem:
973 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
987 // Gain only makes sense for audio regions
988 if ( ! dynamic_cast<AudioRegionView*>(clicked_regionview))
993 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
997 case AutomationTrackItem:
998 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
999 add_automation_event (item, event, where, event->button.y);
1008 switch (item_type) {
1010 audition_selected_region ();
1027 switch (mouse_mode) {
1030 switch (item_type) {
1032 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1034 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1037 // Button2 click is unused
1050 // x_style_paste (where, 1.0);
1070 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1076 switch (item_type) {
1077 case GainControlPointItem:
1078 if (mouse_mode == MouseGain) {
1079 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1080 cp->set_visible (true);
1084 at_y = cp->get_y ();
1085 cp->item->i2w (at_x, at_y);
1089 fraction = 1.0 - (cp->get_y() / cp->line.height());
1091 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1092 show_verbose_canvas_cursor ();
1094 if (is_drawable()) {
1095 track_canvas.get_window()->set_cursor (*fader_cursor);
1100 case GainAutomationControlPointItem:
1101 case PanAutomationControlPointItem:
1102 case RedirectAutomationControlPointItem:
1103 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1104 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1105 cp->set_visible (true);
1109 at_y = cp->get_y ();
1110 cp->item->i2w (at_x, at_y);
1114 fraction = 1.0 - (cp->get_y() / cp->line.height());
1116 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1117 show_verbose_canvas_cursor ();
1119 if (is_drawable()) {
1120 track_canvas.get_window()->set_cursor (*fader_cursor);
1126 if (mouse_mode == MouseGain) {
1127 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1129 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1130 if (is_drawable()) {
1131 track_canvas.get_window()->set_cursor (*fader_cursor);
1136 case GainAutomationLineItem:
1137 case RedirectAutomationLineItem:
1138 case PanAutomationLineItem:
1139 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1141 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1143 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1145 if (is_drawable()) {
1146 track_canvas.get_window()->set_cursor (*fader_cursor);
1151 case RegionViewNameHighlight:
1152 if (is_drawable() && mouse_mode == MouseObject) {
1153 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1157 case StartSelectionTrimItem:
1158 case EndSelectionTrimItem:
1159 /* <CMT Additions> */
1160 case ImageFrameHandleStartItem:
1161 case ImageFrameHandleEndItem:
1162 case MarkerViewHandleStartItem:
1163 case MarkerViewHandleEndItem:
1164 /* </CMT Additions> */
1166 if (is_drawable()) {
1167 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1171 case EditCursorItem:
1172 case PlayheadCursorItem:
1173 if (is_drawable()) {
1174 track_canvas.get_window()->set_cursor (*grabber_cursor);
1178 case RegionViewName:
1180 /* when the name is not an active item, the entire name highlight is for trimming */
1182 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1183 if (mouse_mode == MouseObject && is_drawable()) {
1184 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1190 case AutomationTrackItem:
1191 if (is_drawable()) {
1192 Gdk::Cursor *cursor;
1193 switch (mouse_mode) {
1195 cursor = selector_cursor;
1198 cursor = zoom_cursor;
1201 cursor = cross_hair_cursor;
1205 track_canvas.get_window()->set_cursor (*cursor);
1207 AutomationTimeAxisView* atv;
1208 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1209 clear_entered_track = false;
1210 set_entered_track (atv);
1216 case RangeMarkerBarItem:
1217 case TransportMarkerBarItem:
1220 if (is_drawable()) {
1221 time_canvas.get_window()->set_cursor (*timebar_cursor);
1226 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1229 marker->set_color_rgba (color_map[cEnteredMarker]);
1231 case MeterMarkerItem:
1232 case TempoMarkerItem:
1233 if (is_drawable()) {
1234 time_canvas.get_window()->set_cursor (*timebar_cursor);
1237 case FadeInHandleItem:
1238 case FadeOutHandleItem:
1239 if (mouse_mode == MouseObject) {
1240 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1242 rect->property_fill_color_rgba() = 0;
1243 rect->property_outline_pixels() = 1;
1252 /* second pass to handle entered track status in a comprehensible way.
1255 switch (item_type) {
1257 case GainAutomationLineItem:
1258 case RedirectAutomationLineItem:
1259 case PanAutomationLineItem:
1260 case GainControlPointItem:
1261 case GainAutomationControlPointItem:
1262 case PanAutomationControlPointItem:
1263 case RedirectAutomationControlPointItem:
1264 /* these do not affect the current entered track state */
1265 clear_entered_track = false;
1268 case AutomationTrackItem:
1269 /* handled above already */
1273 set_entered_track (0);
1281 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1290 switch (item_type) {
1291 case GainControlPointItem:
1292 case GainAutomationControlPointItem:
1293 case PanAutomationControlPointItem:
1294 case RedirectAutomationControlPointItem:
1295 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1296 if (cp->line.npoints() > 1) {
1297 if (!cp->selected) {
1298 cp->set_visible (false);
1302 if (is_drawable()) {
1303 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1306 hide_verbose_canvas_cursor ();
1309 case RegionViewNameHighlight:
1310 case StartSelectionTrimItem:
1311 case EndSelectionTrimItem:
1312 case EditCursorItem:
1313 case PlayheadCursorItem:
1314 /* <CMT Additions> */
1315 case ImageFrameHandleStartItem:
1316 case ImageFrameHandleEndItem:
1317 case MarkerViewHandleStartItem:
1318 case MarkerViewHandleEndItem:
1319 /* </CMT Additions> */
1320 if (is_drawable()) {
1321 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1326 case GainAutomationLineItem:
1327 case RedirectAutomationLineItem:
1328 case PanAutomationLineItem:
1329 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1331 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1333 line->property_fill_color_rgba() = al->get_line_color();
1335 if (is_drawable()) {
1336 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1340 case RegionViewName:
1341 /* see enter_handler() for notes */
1342 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1343 if (is_drawable() && mouse_mode == MouseObject) {
1344 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1349 case RangeMarkerBarItem:
1350 case TransportMarkerBarItem:
1354 if (is_drawable()) {
1355 time_canvas.get_window()->set_cursor (*timebar_cursor);
1360 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1363 loc = find_location_from_marker (marker, is_start);
1364 if (loc) location_flags_changed (loc, this);
1366 case MeterMarkerItem:
1367 case TempoMarkerItem:
1369 if (is_drawable()) {
1370 time_canvas.get_window()->set_cursor (*timebar_cursor);
1375 case FadeInHandleItem:
1376 case FadeOutHandleItem:
1377 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1379 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1381 rect->property_fill_color_rgba() = rv->get_fill_color();
1382 rect->property_outline_pixels() = 0;
1387 case AutomationTrackItem:
1388 if (is_drawable()) {
1389 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1390 clear_entered_track = true;
1391 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1403 Editor::left_automation_track ()
1405 if (clear_entered_track) {
1406 set_entered_track (0);
1407 clear_entered_track = false;
1413 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1417 /* We call this so that MOTION_NOTIFY events continue to be
1418 delivered to the canvas. We need to do this because we set
1419 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1420 the density of the events, at the expense of a round-trip
1421 to the server. Given that this will mostly occur on cases
1422 where DISPLAY = :0.0, and given the cost of what the motion
1423 event might do, its a good tradeoff.
1426 track_canvas.get_pointer (x, y);
1428 if (current_stepping_trackview) {
1429 /* don't keep the persistent stepped trackview if the mouse moves */
1430 current_stepping_trackview = 0;
1431 step_timeout.disconnect ();
1434 if (session && session->actively_recording()) {
1435 /* Sorry. no dragging stuff around while we record */
1439 drag_info.item_type = item_type;
1440 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1441 &drag_info.current_pointer_y);
1443 if (!from_autoscroll && drag_info.item) {
1444 /* item != 0 is the best test i can think of for dragging.
1446 if (!drag_info.move_threshold_passed) {
1448 bool x_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1449 bool y_threshold_passed = (abs ((int) (drag_info.current_pointer_y - drag_info.grab_y)) > 4);
1451 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1453 // and change the initial grab loc/frame if this drag info wants us to
1455 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1456 drag_info.grab_frame = drag_info.current_pointer_frame;
1457 drag_info.grab_x = drag_info.current_pointer_x;
1458 drag_info.grab_y = drag_info.current_pointer_y;
1459 drag_info.last_pointer_frame = drag_info.grab_frame;
1460 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1465 switch (item_type) {
1466 case PlayheadCursorItem:
1467 case EditCursorItem:
1469 case GainControlPointItem:
1470 case RedirectAutomationControlPointItem:
1471 case GainAutomationControlPointItem:
1472 case PanAutomationControlPointItem:
1473 case TempoMarkerItem:
1474 case MeterMarkerItem:
1475 case RegionViewNameHighlight:
1476 case StartSelectionTrimItem:
1477 case EndSelectionTrimItem:
1480 case RedirectAutomationLineItem:
1481 case GainAutomationLineItem:
1482 case PanAutomationLineItem:
1483 case FadeInHandleItem:
1484 case FadeOutHandleItem:
1485 /* <CMT Additions> */
1486 case ImageFrameHandleStartItem:
1487 case ImageFrameHandleEndItem:
1488 case MarkerViewHandleStartItem:
1489 case MarkerViewHandleEndItem:
1490 /* </CMT Additions> */
1491 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1492 (event->motion.state & Gdk::BUTTON2_MASK))) {
1493 if (!from_autoscroll) {
1494 maybe_autoscroll (event);
1496 (this->*(drag_info.motion_callback)) (item, event);
1505 switch (mouse_mode) {
1510 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1511 (event->motion.state & GDK_BUTTON2_MASK))) {
1512 if (!from_autoscroll) {
1513 maybe_autoscroll (event);
1515 (this->*(drag_info.motion_callback)) (item, event);
1526 track_canvas_motion (event);
1527 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1535 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1537 if (drag_info.item == 0) {
1538 fatal << _("programming error: start_grab called without drag item") << endmsg;
1544 cursor = grabber_cursor;
1547 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1549 if (event->button.button == 2) {
1550 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1551 drag_info.y_constrained = true;
1552 drag_info.x_constrained = false;
1554 drag_info.y_constrained = false;
1555 drag_info.x_constrained = true;
1558 drag_info.x_constrained = false;
1559 drag_info.y_constrained = false;
1562 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1563 drag_info.last_pointer_frame = drag_info.grab_frame;
1564 drag_info.current_pointer_frame = drag_info.grab_frame;
1565 drag_info.current_pointer_x = drag_info.grab_x;
1566 drag_info.current_pointer_y = drag_info.grab_y;
1567 drag_info.cumulative_x_drag = 0;
1568 drag_info.cumulative_y_drag = 0;
1569 drag_info.first_move = true;
1570 drag_info.move_threshold_passed = false;
1571 drag_info.want_move_threshold = false;
1572 drag_info.pointer_frame_offset = 0;
1573 drag_info.brushing = false;
1574 drag_info.copied_location = 0;
1576 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1578 event->button.time);
1580 if (session && session->transport_rolling()) {
1581 drag_info.was_rolling = true;
1583 drag_info.was_rolling = false;
1586 switch (snap_type) {
1587 case SnapToRegionStart:
1588 case SnapToRegionEnd:
1589 case SnapToRegionSync:
1590 case SnapToRegionBoundary:
1591 build_region_boundary_cache ();
1599 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1601 drag_info.item->ungrab (0);
1602 drag_info.item = new_item;
1605 cursor = grabber_cursor;
1608 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1612 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1614 bool did_drag = false;
1616 stop_canvas_autoscroll ();
1618 if (drag_info.item == 0) {
1622 drag_info.item->ungrab (event->button.time);
1624 if (drag_info.finished_callback) {
1625 (this->*(drag_info.finished_callback)) (item, event);
1628 did_drag = !drag_info.first_move;
1630 hide_verbose_canvas_cursor();
1633 drag_info.copy = false;
1634 drag_info.motion_callback = 0;
1635 drag_info.finished_callback = 0;
1636 drag_info.last_trackview = 0;
1637 drag_info.last_frame_position = 0;
1638 drag_info.grab_frame = 0;
1639 drag_info.last_pointer_frame = 0;
1640 drag_info.current_pointer_frame = 0;
1641 drag_info.brushing = false;
1643 if (drag_info.copied_location) {
1644 delete drag_info.copied_location;
1645 drag_info.copied_location = 0;
1652 Editor::set_edit_cursor (GdkEvent* event)
1654 nframes_t pointer_frame = event_frame (event);
1656 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1657 if (snap_type != SnapToEditCursor) {
1658 snap_to (pointer_frame);
1662 edit_cursor->set_position (pointer_frame);
1663 edit_cursor_clock.set (pointer_frame);
1667 Editor::set_playhead_cursor (GdkEvent* event)
1669 nframes_t pointer_frame = event_frame (event);
1671 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1672 snap_to (pointer_frame);
1676 session->request_locate (pointer_frame, session->transport_rolling());
1681 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1683 drag_info.item = item;
1684 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1685 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1689 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1690 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1694 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1696 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1700 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1702 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1704 nframes_t fade_length;
1706 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1707 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1713 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1717 if (pos < (arv->region()->position() + 64)) {
1718 fade_length = 64; // this should be a minimum defined somewhere
1719 } else if (pos > arv->region()->last_frame()) {
1720 fade_length = arv->region()->length();
1722 fade_length = pos - arv->region()->position();
1724 /* mapover the region selection */
1726 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1728 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1734 tmp->reset_fade_in_shape_width (fade_length);
1737 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1739 drag_info.first_move = false;
1743 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1745 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1747 nframes_t fade_length;
1749 if (drag_info.first_move) return;
1751 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1752 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1757 if (pos < (arv->region()->position() + 64)) {
1758 fade_length = 64; // this should be a minimum defined somewhere
1759 } else if (pos > arv->region()->last_frame()) {
1760 fade_length = arv->region()->length();
1762 fade_length = pos - arv->region()->position();
1765 begin_reversible_command (_("change fade in length"));
1767 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1769 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1775 AutomationList& alist = tmp->audio_region()->fade_in();
1776 XMLNode &before = alist.get_state();
1778 tmp->audio_region()->set_fade_in_length (fade_length);
1780 XMLNode &after = alist.get_state();
1781 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1784 commit_reversible_command ();
1788 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1790 drag_info.item = item;
1791 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1792 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1796 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1797 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1801 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1803 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1807 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1809 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1811 nframes_t fade_length;
1813 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1814 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1820 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1824 if (pos > (arv->region()->last_frame() - 64)) {
1825 fade_length = 64; // this should really be a minimum fade defined somewhere
1827 else if (pos < arv->region()->position()) {
1828 fade_length = arv->region()->length();
1831 fade_length = arv->region()->last_frame() - pos;
1834 /* mapover the region selection */
1836 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1838 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1844 tmp->reset_fade_out_shape_width (fade_length);
1847 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1849 drag_info.first_move = false;
1853 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1855 if (drag_info.first_move) return;
1857 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1859 nframes_t fade_length;
1861 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1862 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1868 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1872 if (pos > (arv->region()->last_frame() - 64)) {
1873 fade_length = 64; // this should really be a minimum fade defined somewhere
1875 else if (pos < arv->region()->position()) {
1876 fade_length = arv->region()->length();
1879 fade_length = arv->region()->last_frame() - pos;
1882 begin_reversible_command (_("change fade out length"));
1884 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1886 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1892 AutomationList& alist = tmp->audio_region()->fade_out();
1893 XMLNode &before = alist.get_state();
1895 tmp->audio_region()->set_fade_out_length (fade_length);
1897 XMLNode &after = alist.get_state();
1898 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1901 commit_reversible_command ();
1905 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1907 drag_info.item = item;
1908 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1909 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1913 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1914 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1918 Cursor* cursor = (Cursor *) drag_info.data;
1920 if (cursor == playhead_cursor) {
1921 _dragging_playhead = true;
1923 if (session && drag_info.was_rolling) {
1924 session->request_stop ();
1927 if (session && session->is_auditioning()) {
1928 session->cancel_audition ();
1932 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1934 show_verbose_time_cursor (cursor->current_frame, 10);
1938 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1940 Cursor* cursor = (Cursor *) drag_info.data;
1941 nframes_t adjusted_frame;
1943 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1944 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1950 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1951 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1952 snap_to (adjusted_frame);
1956 if (adjusted_frame == drag_info.last_pointer_frame) return;
1958 cursor->set_position (adjusted_frame);
1960 if (cursor == edit_cursor) {
1961 edit_cursor_clock.set (cursor->current_frame);
1963 UpdateAllTransportClocks (cursor->current_frame);
1966 show_verbose_time_cursor (cursor->current_frame, 10);
1968 drag_info.last_pointer_frame = adjusted_frame;
1969 drag_info.first_move = false;
1973 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1975 if (drag_info.first_move) return;
1977 cursor_drag_motion_callback (item, event);
1979 _dragging_playhead = false;
1981 if (item == &playhead_cursor->canvas_item) {
1983 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1985 } else if (item == &edit_cursor->canvas_item) {
1986 edit_cursor->set_position (edit_cursor->current_frame);
1987 edit_cursor_clock.set (edit_cursor->current_frame);
1992 Editor::update_marker_drag_item (Location *location)
1994 double x1 = frame_to_pixel (location->start());
1995 double x2 = frame_to_pixel (location->end());
1997 if (location->is_mark()) {
1998 marker_drag_line_points.front().set_x(x1);
1999 marker_drag_line_points.back().set_x(x1);
2000 marker_drag_line->property_points() = marker_drag_line_points;
2003 range_marker_drag_rect->property_x1() = x1;
2004 range_marker_drag_rect->property_x2() = x2;
2009 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2013 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2014 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2020 Location *location = find_location_from_marker (marker, is_start);
2022 drag_info.item = item;
2023 drag_info.data = marker;
2024 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2025 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2029 drag_info.copied_location = new Location (*location);
2030 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2032 update_marker_drag_item (location);
2034 if (location->is_mark()) {
2035 marker_drag_line->show();
2036 marker_drag_line->raise_to_top();
2039 range_marker_drag_rect->show();
2040 range_marker_drag_rect->raise_to_top();
2043 if (is_start) show_verbose_time_cursor (location->start(), 10);
2044 else show_verbose_time_cursor (location->end(), 10);
2048 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2051 Marker* marker = (Marker *) drag_info.data;
2052 Location *real_location;
2053 Location *copy_location;
2055 bool move_both = false;
2059 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2060 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2066 nframes_t next = newframe;
2068 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2069 snap_to (newframe, 0, true);
2072 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2076 /* call this to find out if its the start or end */
2078 real_location = find_location_from_marker (marker, is_start);
2080 /* use the copy that we're "dragging" around */
2082 copy_location = drag_info.copied_location;
2084 f_delta = copy_location->end() - copy_location->start();
2086 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2090 if (copy_location->is_mark()) {
2093 copy_location->set_start (newframe);
2097 if (is_start) { // start-of-range marker
2100 copy_location->set_start (newframe);
2101 copy_location->set_end (newframe + f_delta);
2102 } else if (newframe < copy_location->end()) {
2103 copy_location->set_start (newframe);
2105 snap_to (next, 1, true);
2106 copy_location->set_end (next);
2107 copy_location->set_start (newframe);
2110 } else { // end marker
2113 copy_location->set_end (newframe);
2114 copy_location->set_start (newframe - f_delta);
2115 } else if (newframe > copy_location->start()) {
2116 copy_location->set_end (newframe);
2118 } else if (newframe > 0) {
2119 snap_to (next, -1, true);
2120 copy_location->set_start (next);
2121 copy_location->set_end (newframe);
2126 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2127 drag_info.first_move = false;
2129 update_marker_drag_item (copy_location);
2131 LocationMarkers* lm = find_location_markers (real_location);
2132 lm->set_position (copy_location->start(), copy_location->end());
2134 show_verbose_time_cursor (newframe, 10);
2138 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2140 if (drag_info.first_move) {
2141 marker_drag_motion_callback (item, event);
2145 Marker* marker = (Marker *) drag_info.data;
2149 begin_reversible_command ( _("move marker") );
2150 XMLNode &before = session->locations()->get_state();
2152 Location * location = find_location_from_marker (marker, is_start);
2155 if (location->is_mark()) {
2156 location->set_start (drag_info.copied_location->start());
2158 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2162 XMLNode &after = session->locations()->get_state();
2163 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2164 commit_reversible_command ();
2166 marker_drag_line->hide();
2167 range_marker_drag_rect->hide();
2171 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2174 MeterMarker* meter_marker;
2176 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2177 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2181 meter_marker = dynamic_cast<MeterMarker*> (marker);
2183 MetricSection& section (meter_marker->meter());
2185 if (!section.movable()) {
2189 drag_info.item = item;
2190 drag_info.data = marker;
2191 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2192 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2196 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2198 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2202 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2205 MeterMarker* meter_marker;
2207 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2208 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2212 meter_marker = dynamic_cast<MeterMarker*> (marker);
2214 // create a dummy marker for visual representation of moving the copy.
2215 // The actual copying is not done before we reach the finish callback.
2217 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2218 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2219 *new MeterSection(meter_marker->meter()));
2221 drag_info.item = &new_marker->the_item();
2222 drag_info.copy = true;
2223 drag_info.data = new_marker;
2224 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2225 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2229 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2231 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2235 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2237 MeterMarker* marker = (MeterMarker *) drag_info.data;
2238 nframes_t adjusted_frame;
2240 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2241 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2247 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2248 snap_to (adjusted_frame);
2251 if (adjusted_frame == drag_info.last_pointer_frame) return;
2253 marker->set_position (adjusted_frame);
2256 drag_info.last_pointer_frame = adjusted_frame;
2257 drag_info.first_move = false;
2259 show_verbose_time_cursor (adjusted_frame, 10);
2263 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2265 if (drag_info.first_move) return;
2267 meter_marker_drag_motion_callback (drag_info.item, event);
2269 MeterMarker* marker = (MeterMarker *) drag_info.data;
2272 TempoMap& map (session->tempo_map());
2273 map.bbt_time (drag_info.last_pointer_frame, when);
2275 if (drag_info.copy == true) {
2276 begin_reversible_command (_("copy meter mark"));
2277 XMLNode &before = map.get_state();
2278 map.add_meter (marker->meter(), when);
2279 XMLNode &after = map.get_state();
2280 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2281 commit_reversible_command ();
2283 // delete the dummy marker we used for visual representation of copying.
2284 // a new visual marker will show up automatically.
2287 begin_reversible_command (_("move meter mark"));
2288 XMLNode &before = map.get_state();
2289 map.move_meter (marker->meter(), when);
2290 XMLNode &after = map.get_state();
2291 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2292 commit_reversible_command ();
2297 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2300 TempoMarker* tempo_marker;
2302 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2303 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2307 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2308 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2312 MetricSection& section (tempo_marker->tempo());
2314 if (!section.movable()) {
2318 drag_info.item = item;
2319 drag_info.data = marker;
2320 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2321 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2325 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2326 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2330 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2333 TempoMarker* tempo_marker;
2335 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2336 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2340 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2341 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2345 // create a dummy marker for visual representation of moving the copy.
2346 // The actual copying is not done before we reach the finish callback.
2348 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2349 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2350 *new TempoSection(tempo_marker->tempo()));
2352 drag_info.item = &new_marker->the_item();
2353 drag_info.copy = true;
2354 drag_info.data = new_marker;
2355 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2356 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2360 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2362 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2366 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2368 TempoMarker* marker = (TempoMarker *) drag_info.data;
2369 nframes_t adjusted_frame;
2371 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2372 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2378 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2379 snap_to (adjusted_frame);
2382 if (adjusted_frame == drag_info.last_pointer_frame) return;
2384 /* OK, we've moved far enough to make it worth actually move the thing. */
2386 marker->set_position (adjusted_frame);
2388 show_verbose_time_cursor (adjusted_frame, 10);
2390 drag_info.last_pointer_frame = adjusted_frame;
2391 drag_info.first_move = false;
2395 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2397 if (drag_info.first_move) return;
2399 tempo_marker_drag_motion_callback (drag_info.item, event);
2401 TempoMarker* marker = (TempoMarker *) drag_info.data;
2404 TempoMap& map (session->tempo_map());
2405 map.bbt_time (drag_info.last_pointer_frame, when);
2407 if (drag_info.copy == true) {
2408 begin_reversible_command (_("copy tempo mark"));
2409 XMLNode &before = map.get_state();
2410 map.add_tempo (marker->tempo(), when);
2411 XMLNode &after = map.get_state();
2412 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2413 commit_reversible_command ();
2415 // delete the dummy marker we used for visual representation of copying.
2416 // a new visual marker will show up automatically.
2419 begin_reversible_command (_("move tempo mark"));
2420 XMLNode &before = map.get_state();
2421 map.move_tempo (marker->tempo(), when);
2422 XMLNode &after = map.get_state();
2423 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2424 commit_reversible_command ();
2429 Editor::remove_gain_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 // We shouldn't remove the first or last gain point
2439 if (control_point->line.is_last_point(*control_point) ||
2440 control_point->line.is_first_point(*control_point)) {
2444 control_point->line.remove_point (*control_point);
2448 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2450 ControlPoint* control_point;
2452 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2453 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2457 control_point->line.remove_point (*control_point);
2461 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2463 ControlPoint* control_point;
2465 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2466 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2470 drag_info.item = item;
2471 drag_info.data = control_point;
2472 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2473 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2475 start_grab (event, fader_cursor);
2477 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2479 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2480 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2481 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2483 show_verbose_canvas_cursor ();
2487 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2489 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2491 double cx = drag_info.current_pointer_x;
2492 double cy = drag_info.current_pointer_y;
2494 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2495 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2497 if (drag_info.x_constrained) {
2498 cx = drag_info.grab_x;
2500 if (drag_info.y_constrained) {
2501 cy = drag_info.grab_y;
2504 cp->line.parent_group().w2i (cx, cy);
2508 cy = min ((double) cp->line.height(), cy);
2510 //translate cx to frames
2511 nframes_t cx_frames = unit_to_frame (cx);
2513 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2514 snap_to (cx_frames);
2517 float fraction = 1.0 - (cy / cp->line.height());
2521 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2527 cp->line.point_drag (*cp, cx_frames , fraction, push);
2529 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2531 drag_info.first_move = false;
2535 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2537 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2539 if (drag_info.first_move) {
2543 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2544 reset_point_selection ();
2548 control_point_drag_motion_callback (item, event);
2550 cp->line.end_drag (cp);
2554 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2556 switch (mouse_mode) {
2558 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2559 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2567 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2571 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2572 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2576 start_line_grab (al, event);
2580 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2584 nframes_t frame_within_region;
2586 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2590 cx = event->button.x;
2591 cy = event->button.y;
2592 line->parent_group().w2i (cx, cy);
2593 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2595 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2596 current_line_drag_info.after)) {
2597 /* no adjacent points */
2601 drag_info.item = &line->grab_item();
2602 drag_info.data = line;
2603 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2604 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2606 start_grab (event, fader_cursor);
2608 double fraction = 1.0 - (cy / line->height());
2610 line->start_drag (0, drag_info.grab_frame, fraction);
2612 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2613 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2614 show_verbose_canvas_cursor ();
2618 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2620 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2621 double cx = drag_info.current_pointer_x;
2622 double cy = drag_info.current_pointer_y;
2624 line->parent_group().w2i (cx, cy);
2627 fraction = 1.0 - (cy / line->height());
2631 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2637 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2639 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2643 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2645 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2646 line_drag_motion_callback (item, event);
2651 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2653 if (selection->regions.empty() || clicked_regionview == 0) {
2657 drag_info.copy = false;
2658 drag_info.item = item;
2659 drag_info.data = clicked_regionview;
2660 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2661 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2666 TimeAxisView* tvp = clicked_trackview;
2667 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2669 if (tv && tv->is_audio_track()) {
2670 speed = tv->get_diskstream()->speed();
2673 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2674 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2675 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2676 // we want a move threshold
2677 drag_info.want_move_threshold = true;
2679 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2681 begin_reversible_command (_("move region(s)"));
2685 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2687 if (selection->regions.empty() || clicked_regionview == 0) {
2691 drag_info.copy = true;
2692 drag_info.item = item;
2693 drag_info.data = clicked_regionview;
2697 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2698 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2701 if (atv && atv->is_audio_track()) {
2702 speed = atv->get_diskstream()->speed();
2705 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2706 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2707 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2708 // we want a move threshold
2709 drag_info.want_move_threshold = true;
2710 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2711 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2712 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2716 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2718 if (selection->regions.empty() || clicked_regionview == 0) {
2722 drag_info.copy = false;
2723 drag_info.item = item;
2724 drag_info.data = clicked_regionview;
2725 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2726 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2731 TimeAxisView* tvp = clicked_trackview;
2732 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2734 if (tv && tv->is_audio_track()) {
2735 speed = tv->get_diskstream()->speed();
2738 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2739 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2740 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2741 // we want a move threshold
2742 drag_info.want_move_threshold = true;
2743 drag_info.brushing = true;
2745 begin_reversible_command (_("Drag region brush"));
2749 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2753 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2754 nframes_t pending_region_position = 0;
2755 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2756 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2757 bool clamp_y_axis = false;
2758 vector<int32_t> height_list(512) ;
2759 vector<int32_t>::iterator j;
2761 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2763 drag_info.want_move_threshold = false; // don't copy again
2765 /* duplicate the region(s) */
2767 vector<RegionView*> new_regionviews;
2769 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2772 AudioRegionView* arv;
2777 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
2778 /* XXX handle MIDI here */
2782 nrv = new AudioRegionView (*arv);
2783 nrv->get_canvas_group()->show ();
2785 new_regionviews.push_back (nrv);
2788 if (new_regionviews.empty()) {
2792 /* reset selection to new regionviews */
2794 selection->set (new_regionviews);
2796 /* reset drag_info data to reflect the fact that we are dragging the copies */
2798 drag_info.data = new_regionviews.front();
2800 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2803 /* Which trackview is this ? */
2805 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2806 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2808 /* The region motion is only processed if the pointer is over
2812 if (!tv || !tv->is_audio_track()) {
2813 /* To make sure we hide the verbose canvas cursor when the mouse is
2814 not held over and audiotrack.
2816 hide_verbose_canvas_cursor ();
2820 original_pointer_order = drag_info.last_trackview->order;
2822 /************************************************************
2824 ************************************************************/
2826 if (drag_info.brushing) {
2827 clamp_y_axis = true;
2832 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2834 int32_t children = 0, numtracks = 0;
2835 // XXX hard coding track limit, oh my, so very very bad
2836 bitset <1024> tracks (0x00);
2837 /* get a bitmask representing the visible tracks */
2839 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2840 TimeAxisView *tracklist_timeview;
2841 tracklist_timeview = (*i);
2842 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2843 list<TimeAxisView*> children_list;
2845 /* zeroes are audio tracks. ones are other types. */
2847 if (!atv2->hidden()) {
2849 if (visible_y_high < atv2->order) {
2850 visible_y_high = atv2->order;
2852 if (visible_y_low > atv2->order) {
2853 visible_y_low = atv2->order;
2856 if (!atv2->is_audio_track()) {
2857 tracks = tracks |= (0x01 << atv2->order);
2860 height_list[atv2->order] = (*i)->height;
2862 if ((children_list = atv2->get_child_list()).size() > 0) {
2863 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2864 tracks = tracks |= (0x01 << (atv2->order + children));
2865 height_list[atv2->order + children] = (*j)->height;
2873 /* find the actual span according to the canvas */
2875 canvas_pointer_y_span = pointer_y_span;
2876 if (drag_info.last_trackview->order >= tv->order) {
2878 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2879 if (height_list[y] == 0 ) {
2880 canvas_pointer_y_span--;
2885 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2886 if ( height_list[y] == 0 ) {
2887 canvas_pointer_y_span++;
2892 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2893 RegionView* rv2 = (*i);
2894 double ix1, ix2, iy1, iy2;
2897 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2898 rv2->get_canvas_group()->i2w (ix1, iy1);
2899 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2900 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2902 if (atv2->order != original_pointer_order) {
2903 /* this isn't the pointer track */
2905 if (canvas_pointer_y_span > 0) {
2907 /* moving up the canvas */
2908 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2910 int32_t visible_tracks = 0;
2911 while (visible_tracks < canvas_pointer_y_span ) {
2914 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2915 /* we're passing through a hidden track */
2920 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2921 clamp_y_axis = true;
2925 clamp_y_axis = true;
2928 } else if (canvas_pointer_y_span < 0) {
2930 /*moving down the canvas*/
2932 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2935 int32_t visible_tracks = 0;
2937 while (visible_tracks > canvas_pointer_y_span ) {
2940 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2944 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2945 clamp_y_axis = true;
2950 clamp_y_axis = true;
2956 /* this is the pointer's track */
2957 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2958 clamp_y_axis = true;
2959 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2960 clamp_y_axis = true;
2968 } else if (drag_info.last_trackview == tv) {
2969 clamp_y_axis = true;
2973 if (!clamp_y_axis) {
2974 drag_info.last_trackview = tv;
2977 /************************************************************
2979 ************************************************************/
2981 /* compute the amount of pointer motion in frames, and where
2982 the region would be if we moved it by that much.
2985 if (drag_info.move_threshold_passed) {
2987 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2989 nframes_t sync_frame;
2990 nframes_t sync_offset;
2993 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2995 sync_offset = rv->region()->sync_offset (sync_dir);
2996 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
2998 /* we snap if the snap modifier is not enabled.
3001 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3002 snap_to (sync_frame);
3005 if (sync_frame - sync_offset <= sync_frame) {
3006 pending_region_position = sync_frame - (sync_dir*sync_offset);
3008 pending_region_position = 0;
3012 pending_region_position = 0;
3015 if (pending_region_position > max_frames - rv->region()->length()) {
3016 pending_region_position = drag_info.last_frame_position;
3019 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3021 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3023 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3024 to make it appear at the new location.
3027 if (pending_region_position > drag_info.last_frame_position) {
3028 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3030 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3033 drag_info.last_frame_position = pending_region_position;
3040 /* threshold not passed */
3045 /*************************************************************
3047 ************************************************************/
3049 if (x_delta == 0 && (pointer_y_span == 0)) {
3050 /* haven't reached next snap point, and we're not switching
3051 trackviews. nothing to do.
3057 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3059 RegionView* rv2 = (*i);
3061 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3063 double ix1, ix2, iy1, iy2;
3064 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3065 rv2->get_canvas_group()->i2w (ix1, iy1);
3074 /*************************************************************
3076 ************************************************************/
3080 if (drag_info.first_move) {
3081 if (drag_info.move_threshold_passed) {
3092 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3093 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3095 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3097 RegionView* rv = (*i);
3098 double ix1, ix2, iy1, iy2;
3099 int32_t temp_pointer_y_span = pointer_y_span;
3101 /* get item BBox, which will be relative to parent. so we have
3102 to query on a child, then convert to world coordinates using
3106 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3107 rv->get_canvas_group()->i2w (ix1, iy1);
3108 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3109 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3110 AudioTimeAxisView* temp_atv;
3112 if ((pointer_y_span != 0) && !clamp_y_axis) {
3115 for (j = height_list.begin(); j!= height_list.end(); j++) {
3116 if (x == canvas_atv->order) {
3117 /* we found the track the region is on */
3118 if (x != original_pointer_order) {
3119 /*this isn't from the same track we're dragging from */
3120 temp_pointer_y_span = canvas_pointer_y_span;
3122 while (temp_pointer_y_span > 0) {
3123 /* we're moving up canvas-wise,
3124 so we need to find the next track height
3126 if (j != height_list.begin()) {
3129 if (x != original_pointer_order) {
3130 /* we're not from the dragged track, so ignore hidden tracks. */
3132 temp_pointer_y_span++;
3136 temp_pointer_y_span--;
3138 while (temp_pointer_y_span < 0) {
3140 if (x != original_pointer_order) {
3142 temp_pointer_y_span--;
3146 if (j != height_list.end()) {
3149 temp_pointer_y_span++;
3151 /* find out where we'll be when we move and set height accordingly */
3153 tvp2 = trackview_by_y_position (iy1 + y_delta);
3154 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3155 rv->set_height (temp_atv->height);
3157 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3158 personally, i think this can confuse things, but never mind.
3161 //const GdkColor& col (temp_atv->view->get_region_color());
3162 //rv->set_color (const_cast<GdkColor&>(col));
3169 /* prevent the regionview from being moved to before
3170 the zero position on the canvas.
3175 if (-x_delta > ix1) {
3178 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3179 x_delta = max_frames - rv->region()->last_frame();
3182 if (drag_info.first_move) {
3184 /* hide any dependent views */
3186 rv->get_time_axis_view().hide_dependent_views (*rv);
3188 /* this is subtle. raising the regionview itself won't help,
3189 because raise_to_top() just puts the item on the top of
3190 its parent's stack. so, we need to put the trackview canvas_display group
3191 on the top, since its parent is the whole canvas.
3194 rv->get_canvas_group()->raise_to_top();
3195 rv->get_time_axis_view().canvas_display->raise_to_top();
3196 cursor_group->raise_to_top();
3198 rv->fake_set_opaque (true);
3201 if (drag_info.brushing) {
3202 mouse_brush_insert_region (rv, pending_region_position);
3204 rv->move (x_delta, y_delta);
3207 } /* foreach region */
3211 if (drag_info.first_move && drag_info.move_threshold_passed) {
3212 cursor_group->raise_to_top();
3213 drag_info.first_move = false;
3216 if (x_delta != 0 && !drag_info.brushing) {
3217 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3222 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3225 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3226 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3227 bool nocommit = true;
3229 RouteTimeAxisView* atv;
3230 bool regionview_y_movement;
3231 bool regionview_x_movement;
3233 /* first_move is set to false if the regionview has been moved in the
3237 if (drag_info.first_move) {
3244 /* The regionview has been moved at some stage during the grab so we need
3245 to account for any mouse movement between this event and the last one.
3248 region_drag_motion_callback (item, event);
3250 if (drag_info.brushing) {
3251 /* all changes were made during motion event handlers */
3255 /* adjust for track speed */
3258 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3259 if (atv && atv->get_diskstream()) {
3260 speed = atv->get_diskstream()->speed();
3263 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3264 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3266 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3267 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3271 if (drag_info.copy) {
3272 if (drag_info.x_constrained) {
3273 op_string = _("fixed time region copy");
3275 op_string = _("region copy");
3278 if (drag_info.x_constrained) {
3279 op_string = _("fixed time region drag");
3281 op_string = _("region drag");
3285 begin_reversible_command (op_string);
3287 if (regionview_y_movement) {
3289 /* moved to a different audio track. */
3291 vector<RegionView*> new_selection;
3293 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3295 RegionView* rv = (*i);
3297 double ix1, ix2, iy1, iy2;
3299 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3300 rv->get_canvas_group()->i2w (ix1, iy1);
3301 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3302 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3304 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3305 boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
3307 where = (nframes_t) (unit_to_frame (ix1) * speed);
3308 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3310 if (!drag_info.copy) {
3312 /* the region that used to be in the old playlist is not
3313 moved to the new one - we make a copy of it. as a result,
3314 any existing editor for the region should no longer be
3318 rv->hide_region_editor();
3319 rv->fake_set_opaque (false);
3321 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3322 from_playlist->remove_region ((rv->region()));
3323 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3326 latest_regionview = 0;
3328 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3329 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3330 to_playlist->add_region (new_region, where);
3331 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3334 if (latest_regionview) {
3335 new_selection.push_back (latest_regionview);
3338 if (drag_info.copy) {
3339 // get rid of the copy
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.
3357 if (selection->regions.empty()) {
3360 i = selection->regions.by_layer().begin();
3364 selection->set (new_selection);
3368 /* motion within a single track */
3370 list<RegionView*> regions = selection->regions.by_layer();
3372 if (drag_info.copy) {
3373 selection->clear_regions();
3376 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3380 if (rv->region()->locked()) {
3385 if (regionview_x_movement) {
3386 double ownspeed = 1.0;
3387 atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3389 if (atv && atv->get_diskstream()) {
3390 ownspeed = atv->get_diskstream()->speed();
3393 /* base the new region position on the current position of the regionview.*/
3395 double ix1, ix2, iy1, iy2;
3397 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3398 rv->get_canvas_group()->i2w (ix1, iy1);
3399 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3403 where = rv->region()->position();
3406 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3408 assert (to_playlist);
3412 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3414 if (drag_info.copy) {
3416 boost::shared_ptr<Region> newregion;
3417 boost::shared_ptr<Region> ar;
3419 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3420 newregion = RegionFactory::create (ar);
3422 /* XXX MIDI HERE drobilla */
3428 latest_regionview = 0;
3429 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3430 to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
3433 if (latest_regionview) {
3434 atv->reveal_dependent_views (*latest_regionview);
3435 selection->add (latest_regionview);
3438 /* if the original region was locked, we don't care for the new one */
3440 newregion->set_locked (false);
3444 /* just change the model */
3446 rv->region()->set_position (where, (void*) this);
3452 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3454 /* get rid of the copy */
3456 if (drag_info.copy) {
3465 commit_reversible_command ();
3470 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3472 /* Either add to or set the set the region selection, unless
3473 this is an alignment click (control used)
3476 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3477 TimeAxisView* tv = &rv.get_time_axis_view();
3478 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3480 if (atv && atv->is_audio_track()) {
3481 speed = atv->get_diskstream()->speed();
3484 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3486 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3488 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3490 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3494 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3500 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3506 nframes_t frame_rate;
3513 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3514 case AudioClock::BBT:
3515 session->bbt_time (frame, bbt);
3516 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3519 case AudioClock::SMPTE:
3520 session->smpte_time (frame, smpte);
3521 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3524 case AudioClock::MinSec:
3525 /* XXX this is copied from show_verbose_duration_cursor() */
3526 frame_rate = session->frame_rate();
3527 hours = frame / (frame_rate * 3600);
3528 frame = frame % (frame_rate * 3600);
3529 mins = frame / (frame_rate * 60);
3530 frame = frame % (frame_rate * 60);
3531 secs = (float) frame / (float) frame_rate;
3532 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3536 snprintf (buf, sizeof(buf), "%u", frame);
3540 if (xpos >= 0 && ypos >=0) {
3541 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3544 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3546 show_verbose_canvas_cursor ();
3550 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3557 nframes_t distance, frame_rate;
3559 Meter meter_at_start(session->tempo_map().meter_at(start));
3565 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3566 case AudioClock::BBT:
3567 session->bbt_time (start, sbbt);
3568 session->bbt_time (end, ebbt);
3571 /* XXX this computation won't work well if the
3572 user makes a selection that spans any meter changes.
3575 ebbt.bars -= sbbt.bars;
3576 if (ebbt.beats >= sbbt.beats) {
3577 ebbt.beats -= sbbt.beats;
3580 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3582 if (ebbt.ticks >= sbbt.ticks) {
3583 ebbt.ticks -= sbbt.ticks;
3586 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3589 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3592 case AudioClock::SMPTE:
3593 session->smpte_duration (end - start, smpte);
3594 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3597 case AudioClock::MinSec:
3598 /* XXX this stuff should be elsewhere.. */
3599 distance = end - start;
3600 frame_rate = session->frame_rate();
3601 hours = distance / (frame_rate * 3600);
3602 distance = distance % (frame_rate * 3600);
3603 mins = distance / (frame_rate * 60);
3604 distance = distance % (frame_rate * 60);
3605 secs = (float) distance / (float) frame_rate;
3606 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3610 snprintf (buf, sizeof(buf), "%u", end - start);
3614 if (xpos >= 0 && ypos >=0) {
3615 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3618 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3620 show_verbose_canvas_cursor ();
3624 Editor::collect_new_region_view (RegionView* rv)
3626 latest_regionview = rv;
3630 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3632 if (clicked_regionview == 0) {
3636 /* lets try to create new Region for the selection */
3638 vector<boost::shared_ptr<AudioRegion> > new_regions;
3639 create_region_from_selection (new_regions);
3641 if (new_regions.empty()) {
3645 /* XXX fix me one day to use all new regions */
3647 boost::shared_ptr<Region> region (new_regions.front());
3649 /* add it to the current stream/playlist.
3651 tricky: the streamview for the track will add a new regionview. we will
3652 catch the signal it sends when it creates the regionview to
3653 set the regionview we want to then drag.
3656 latest_regionview = 0;
3657 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3659 /* A selection grab currently creates two undo/redo operations, one for
3660 creating the new region and another for moving it.
3663 begin_reversible_command (_("selection grab"));
3665 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3667 XMLNode *before = &(playlist->get_state());
3668 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3669 XMLNode *after = &(playlist->get_state());
3670 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3672 commit_reversible_command ();
3676 if (latest_regionview == 0) {
3677 /* something went wrong */
3681 /* we need to deselect all other regionviews, and select this one
3682 i'm ignoring undo stuff, because the region creation will take care of it */
3683 selection->set (latest_regionview);
3685 drag_info.item = latest_regionview->get_canvas_group();
3686 drag_info.data = latest_regionview;
3687 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3688 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3692 drag_info.last_trackview = clicked_trackview;
3693 drag_info.last_frame_position = latest_regionview->region()->position();
3694 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3696 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3700 Editor::cancel_selection ()
3702 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3703 (*i)->hide_selection ();
3705 begin_reversible_command (_("cancel selection"));
3706 selection->clear ();
3707 clicked_selection = 0;
3708 commit_reversible_command ();
3712 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3714 nframes_t start = 0;
3721 drag_info.item = item;
3722 drag_info.motion_callback = &Editor::drag_selection;
3723 drag_info.finished_callback = &Editor::end_selection_op;
3728 case CreateSelection:
3729 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3730 drag_info.copy = true;
3732 drag_info.copy = false;
3734 start_grab (event, selector_cursor);
3737 case SelectionStartTrim:
3738 if (clicked_trackview) {
3739 clicked_trackview->order_selection_trims (item, true);
3741 start_grab (event, trimmer_cursor);
3742 start = selection->time[clicked_selection].start;
3743 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3746 case SelectionEndTrim:
3747 if (clicked_trackview) {
3748 clicked_trackview->order_selection_trims (item, false);
3750 start_grab (event, trimmer_cursor);
3751 end = selection->time[clicked_selection].end;
3752 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3756 start = selection->time[clicked_selection].start;
3758 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3762 if (selection_op == SelectionMove) {
3763 show_verbose_time_cursor(start, 10);
3765 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3770 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3772 nframes_t start = 0;
3775 nframes_t pending_position;
3777 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3778 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3781 pending_position = 0;
3784 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3785 snap_to (pending_position);
3788 /* only alter selection if the current frame is
3789 different from the last frame position (adjusted)
3792 if (pending_position == drag_info.last_pointer_frame) return;
3794 switch (selection_op) {
3795 case CreateSelection:
3797 if (drag_info.first_move) {
3798 snap_to (drag_info.grab_frame);
3801 if (pending_position < drag_info.grab_frame) {
3802 start = pending_position;
3803 end = drag_info.grab_frame;
3805 end = pending_position;
3806 start = drag_info.grab_frame;
3809 /* first drag: Either add to the selection
3810 or create a new selection->
3813 if (drag_info.first_move) {
3815 begin_reversible_command (_("range selection"));
3817 if (drag_info.copy) {
3818 /* adding to the selection */
3819 clicked_selection = selection->add (start, end);
3820 drag_info.copy = false;
3822 /* new selection-> */
3823 clicked_selection = selection->set (clicked_trackview, start, end);
3828 case SelectionStartTrim:
3830 if (drag_info.first_move) {
3831 begin_reversible_command (_("trim selection start"));
3834 start = selection->time[clicked_selection].start;
3835 end = selection->time[clicked_selection].end;
3837 if (pending_position > end) {
3840 start = pending_position;
3844 case SelectionEndTrim:
3846 if (drag_info.first_move) {
3847 begin_reversible_command (_("trim selection end"));
3850 start = selection->time[clicked_selection].start;
3851 end = selection->time[clicked_selection].end;
3853 if (pending_position < start) {
3856 end = pending_position;
3863 if (drag_info.first_move) {
3864 begin_reversible_command (_("move selection"));
3867 start = selection->time[clicked_selection].start;
3868 end = selection->time[clicked_selection].end;
3870 length = end - start;
3872 start = pending_position;
3875 end = start + length;
3880 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3881 start_canvas_autoscroll (1);
3885 selection->replace (clicked_selection, start, end);
3888 drag_info.last_pointer_frame = pending_position;
3889 drag_info.first_move = false;
3891 if (selection_op == SelectionMove) {
3892 show_verbose_time_cursor(start, 10);
3894 show_verbose_time_cursor(pending_position, 10);
3899 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3901 if (!drag_info.first_move) {
3902 drag_selection (item, event);
3903 /* XXX this is not object-oriented programming at all. ick */
3904 if (selection->time.consolidate()) {
3905 selection->TimeChanged ();
3907 commit_reversible_command ();
3909 /* just a click, no pointer movement.*/
3911 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3913 selection->clear_time();
3918 /* XXX what happens if its a music selection? */
3919 session->set_audio_range (selection->time);
3920 stop_canvas_autoscroll ();
3924 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3927 TimeAxisView* tvp = clicked_trackview;
3928 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3930 if (tv && tv->is_audio_track()) {
3931 speed = tv->get_diskstream()->speed();
3934 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3935 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3936 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3938 //drag_info.item = clicked_regionview->get_name_highlight();
3939 drag_info.item = item;
3940 drag_info.motion_callback = &Editor::trim_motion_callback;
3941 drag_info.finished_callback = &Editor::trim_finished_callback;
3943 start_grab (event, trimmer_cursor);
3945 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3946 trim_op = ContentsTrim;
3948 /* These will get overridden for a point trim.*/
3949 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3950 /* closer to start */
3951 trim_op = StartTrim;
3952 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3960 show_verbose_time_cursor(region_start, 10);
3963 show_verbose_time_cursor(region_end, 10);
3966 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3972 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3974 RegionView* rv = clicked_regionview;
3975 nframes_t frame_delta = 0;
3976 bool left_direction;
3977 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3979 /* snap modifier works differently here..
3980 its' current state has to be passed to the
3981 various trim functions in order to work properly
3985 TimeAxisView* tvp = clicked_trackview;
3986 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3987 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3989 if (tv && tv->is_audio_track()) {
3990 speed = tv->get_diskstream()->speed();
3993 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3994 left_direction = true;
3996 left_direction = false;
4000 snap_to (drag_info.current_pointer_frame);
4003 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4007 if (drag_info.first_move) {
4013 trim_type = "Region start trim";
4016 trim_type = "Region end trim";
4019 trim_type = "Region content trim";
4023 begin_reversible_command (trim_type);
4025 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4026 (*i)->fake_set_opaque(false);
4027 (*i)->region()->freeze ();
4029 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4031 arv->temporarily_hide_envelope ();
4033 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4034 insert_result = motion_frozen_playlists.insert (pl);
4035 if (insert_result.second) {
4036 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4041 if (left_direction) {
4042 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4044 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4049 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4052 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4053 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4059 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4062 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4063 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4070 bool swap_direction = false;
4072 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4073 swap_direction = true;
4076 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4077 i != selection->regions.by_layer().end(); ++i)
4079 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4087 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4090 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4093 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4097 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4098 drag_info.first_move = false;
4102 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4104 boost::shared_ptr<Region> region (rv.region());
4106 if (region->locked()) {
4110 nframes_t new_bound;
4113 TimeAxisView* tvp = clicked_trackview;
4114 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4116 if (tv && tv->is_audio_track()) {
4117 speed = tv->get_diskstream()->speed();
4120 if (left_direction) {
4121 if (swap_direction) {
4122 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4124 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4127 if (swap_direction) {
4128 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4130 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4135 snap_to (new_bound);
4137 region->trim_start ((nframes_t) (new_bound * speed), this);
4138 rv.region_changed (StartChanged);
4142 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4144 boost::shared_ptr<Region> region (rv.region());
4146 if (region->locked()) {
4150 nframes_t new_bound;
4153 TimeAxisView* tvp = clicked_trackview;
4154 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4156 if (tv && tv->is_audio_track()) {
4157 speed = tv->get_diskstream()->speed();
4160 if (left_direction) {
4161 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4163 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4167 snap_to (new_bound, (left_direction ? 0 : 1));
4170 region->trim_front ((nframes_t) (new_bound * speed), this);
4172 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4176 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4178 boost::shared_ptr<Region> region (rv.region());
4180 if (region->locked()) {
4184 nframes_t new_bound;
4187 TimeAxisView* tvp = clicked_trackview;
4188 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4190 if (tv && tv->is_audio_track()) {
4191 speed = tv->get_diskstream()->speed();
4194 if (left_direction) {
4195 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4197 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4201 snap_to (new_bound);
4203 region->trim_end ((nframes_t) (new_bound * speed), this);
4204 rv.region_changed (LengthChanged);
4208 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4210 if (!drag_info.first_move) {
4211 trim_motion_callback (item, event);
4213 if (!clicked_regionview->get_selected()) {
4214 thaw_region_after_trim (*clicked_regionview);
4217 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4218 i != selection->regions.by_layer().end(); ++i)
4220 thaw_region_after_trim (**i);
4221 (*i)->fake_set_opaque (true);
4225 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4227 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4230 motion_frozen_playlists.clear ();
4232 commit_reversible_command();
4234 /* no mouse movement */
4240 Editor::point_trim (GdkEvent* event)
4242 RegionView* rv = clicked_regionview;
4243 nframes_t new_bound = drag_info.current_pointer_frame;
4245 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4246 snap_to (new_bound);
4249 /* Choose action dependant on which button was pressed */
4250 switch (event->button.button) {
4252 trim_op = StartTrim;
4253 begin_reversible_command (_("Start point trim"));
4255 if (rv->get_selected()) {
4257 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4258 i != selection->regions.by_layer().end(); ++i)
4260 if (!(*i)->region()->locked()) {
4261 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4262 XMLNode &before = pl->get_state();
4263 (*i)->region()->trim_front (new_bound, this);
4264 XMLNode &after = pl->get_state();
4265 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4271 if (!rv->region()->locked()) {
4272 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4273 XMLNode &before = pl->get_state();
4274 rv->region()->trim_front (new_bound, this);
4275 XMLNode &after = pl->get_state();
4276 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4280 commit_reversible_command();
4285 begin_reversible_command (_("End point trim"));
4287 if (rv->get_selected()) {
4289 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4291 if (!(*i)->region()->locked()) {
4292 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4293 XMLNode &before = pl->get_state();
4294 (*i)->region()->trim_end (new_bound, this);
4295 XMLNode &after = pl->get_state();
4296 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4302 if (!rv->region()->locked()) {
4303 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4304 XMLNode &before = pl->get_state();
4305 rv->region()->trim_end (new_bound, this);
4306 XMLNode &after = pl->get_state();
4307 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4311 commit_reversible_command();
4320 Editor::thaw_region_after_trim (RegionView& rv)
4322 boost::shared_ptr<Region> region (rv.region());
4324 if (region->locked()) {
4328 region->thaw (_("trimmed region"));
4329 XMLNode &after = region->playlist()->get_state();
4330 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4332 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4334 arv->unhide_envelope ();
4338 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4343 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4344 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4348 Location* location = find_location_from_marker (marker, is_start);
4349 location->set_hidden (true, this);
4354 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4360 drag_info.item = item;
4361 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4362 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4364 range_marker_op = op;
4366 if (!temp_location) {
4367 temp_location = new Location;
4371 case CreateRangeMarker:
4372 case CreateTransportMarker:
4374 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4375 drag_info.copy = true;
4377 drag_info.copy = false;
4379 start_grab (event, selector_cursor);
4383 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4388 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4390 nframes_t start = 0;
4392 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4394 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4395 snap_to (drag_info.current_pointer_frame);
4398 /* only alter selection if the current frame is
4399 different from the last frame position.
4402 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4404 switch (range_marker_op) {
4405 case CreateRangeMarker:
4406 case CreateTransportMarker:
4407 if (drag_info.first_move) {
4408 snap_to (drag_info.grab_frame);
4411 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4412 start = drag_info.current_pointer_frame;
4413 end = drag_info.grab_frame;
4415 end = drag_info.current_pointer_frame;
4416 start = drag_info.grab_frame;
4419 /* first drag: Either add to the selection
4420 or create a new selection.
4423 if (drag_info.first_move) {
4425 temp_location->set (start, end);
4429 update_marker_drag_item (temp_location);
4430 range_marker_drag_rect->show();
4431 range_marker_drag_rect->raise_to_top();
4437 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4438 start_canvas_autoscroll (1);
4442 temp_location->set (start, end);
4444 double x1 = frame_to_pixel (start);
4445 double x2 = frame_to_pixel (end);
4446 crect->property_x1() = x1;
4447 crect->property_x2() = x2;
4449 update_marker_drag_item (temp_location);
4452 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4453 drag_info.first_move = false;
4455 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4460 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4462 Location * newloc = 0;
4465 if (!drag_info.first_move) {
4466 drag_range_markerbar_op (item, event);
4468 switch (range_marker_op) {
4469 case CreateRangeMarker:
4471 begin_reversible_command (_("new range marker"));
4472 XMLNode &before = session->locations()->get_state();
4473 session->locations()->next_available_name(rangename,"unnamed");
4474 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4475 session->locations()->add (newloc, true);
4476 XMLNode &after = session->locations()->get_state();
4477 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4478 commit_reversible_command ();
4480 range_bar_drag_rect->hide();
4481 range_marker_drag_rect->hide();
4485 case CreateTransportMarker:
4486 // popup menu to pick loop or punch
4487 new_transport_marker_context_menu (&event->button, item);
4492 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4494 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4499 start = session->locations()->first_mark_before (drag_info.grab_frame);
4500 end = session->locations()->first_mark_after (drag_info.grab_frame);
4502 if (end == max_frames) {
4503 end = session->current_end_frame ();
4507 start = session->current_start_frame ();
4510 switch (mouse_mode) {
4512 /* find the two markers on either side and then make the selection from it */
4513 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4517 /* find the two markers on either side of the click and make the range out of it */
4518 selection->set (0, start, end);
4527 stop_canvas_autoscroll ();
4533 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4535 drag_info.item = item;
4536 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4537 drag_info.finished_callback = &Editor::end_mouse_zoom;
4539 start_grab (event, zoom_cursor);
4541 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4545 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4550 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4551 snap_to (drag_info.current_pointer_frame);
4553 if (drag_info.first_move) {
4554 snap_to (drag_info.grab_frame);
4558 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4560 /* base start and end on initial click position */
4561 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4562 start = drag_info.current_pointer_frame;
4563 end = drag_info.grab_frame;
4565 end = drag_info.current_pointer_frame;
4566 start = drag_info.grab_frame;
4571 if (drag_info.first_move) {
4573 zoom_rect->raise_to_top();
4576 reposition_zoom_rect(start, end);
4578 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4579 drag_info.first_move = false;
4581 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4586 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4588 if (!drag_info.first_move) {
4589 drag_mouse_zoom (item, event);
4591 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4592 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4594 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4597 temporal_zoom_to_frame (false, drag_info.grab_frame);
4599 temporal_zoom_step (false);
4600 center_screen (drag_info.grab_frame);
4608 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4610 double x1 = frame_to_pixel (start);
4611 double x2 = frame_to_pixel (end);
4612 double y2 = full_canvas_height - 1.0;
4614 zoom_rect->property_x1() = x1;
4615 zoom_rect->property_y1() = 1.0;
4616 zoom_rect->property_x2() = x2;
4617 zoom_rect->property_y2() = y2;
4621 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4623 drag_info.item = item;
4624 drag_info.motion_callback = &Editor::drag_rubberband_select;
4625 drag_info.finished_callback = &Editor::end_rubberband_select;
4627 start_grab (event, cross_hair_cursor);
4629 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4633 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4640 /* use a bigger drag threshold than the default */
4642 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4646 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4647 if (drag_info.first_move) {
4648 snap_to (drag_info.grab_frame);
4650 snap_to (drag_info.current_pointer_frame);
4653 /* base start and end on initial click position */
4655 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4656 start = drag_info.current_pointer_frame;
4657 end = drag_info.grab_frame;
4659 end = drag_info.current_pointer_frame;
4660 start = drag_info.grab_frame;
4663 if (drag_info.current_pointer_y < drag_info.grab_y) {
4664 y1 = drag_info.current_pointer_y;
4665 y2 = drag_info.grab_y;
4667 y2 = drag_info.current_pointer_y;
4668 y1 = drag_info.grab_y;
4672 if (start != end || y1 != y2) {
4674 double x1 = frame_to_pixel (start);
4675 double x2 = frame_to_pixel (end);
4677 rubberband_rect->property_x1() = x1;
4678 rubberband_rect->property_y1() = y1;
4679 rubberband_rect->property_x2() = x2;
4680 rubberband_rect->property_y2() = y2;
4682 rubberband_rect->show();
4683 rubberband_rect->raise_to_top();
4685 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4686 drag_info.first_move = false;
4688 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4693 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4695 if (!drag_info.first_move) {
4697 drag_rubberband_select (item, event);
4700 if (drag_info.current_pointer_y < drag_info.grab_y) {
4701 y1 = drag_info.current_pointer_y;
4702 y2 = drag_info.grab_y;
4705 y2 = drag_info.current_pointer_y;
4706 y1 = drag_info.grab_y;
4710 Selection::Operation op = Keyboard::selection_type (event->button.state);
4713 begin_reversible_command (_("rubberband selection"));
4715 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4716 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4718 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4722 commit_reversible_command ();
4726 selection->clear_tracks();
4727 selection->clear_regions();
4728 selection->clear_points ();
4729 selection->clear_lines ();
4732 rubberband_rect->hide();
4737 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4739 using namespace Gtkmm2ext;
4741 ArdourPrompter prompter (false);
4743 prompter.set_prompt (_("Name for region:"));
4744 prompter.set_initial_text (clicked_regionview->region()->name());
4745 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4746 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4747 prompter.show_all ();
4748 switch (prompter.run ()) {
4749 case Gtk::RESPONSE_ACCEPT:
4751 prompter.get_result(str);
4753 clicked_regionview->region()->set_name (str);
4761 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4763 drag_info.item = item;
4764 drag_info.motion_callback = &Editor::time_fx_motion;
4765 drag_info.finished_callback = &Editor::end_time_fx;
4769 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4773 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4775 RegionView* rv = clicked_regionview;
4777 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4778 snap_to (drag_info.current_pointer_frame);
4781 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4785 if (drag_info.current_pointer_frame > rv->region()->position()) {
4786 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4789 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4790 drag_info.first_move = false;
4792 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4796 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4798 clicked_regionview->get_time_axis_view().hide_timestretch ();
4800 if (drag_info.first_move) {
4804 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4805 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4807 begin_reversible_command (_("timestretch"));
4809 if (run_timestretch (selection->regions, percentage) == 0) {
4810 session->commit_reversible_command ();
4815 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4817 /* no brushing without a useful snap setting */
4820 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4823 switch (snap_mode) {
4825 return; /* can't work because it allows region to be placed anywhere */
4830 switch (snap_type) {
4833 case SnapToEditCursor:
4840 /* don't brush a copy over the original */
4842 if (pos == rv->region()->position()) {
4846 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4848 if (atv == 0 || !atv->is_audio_track()) {
4852 boost::shared_ptr<Playlist> playlist = atv->playlist();
4853 double speed = atv->get_diskstream()->speed();
4855 XMLNode &before = playlist->get_state();
4856 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4857 XMLNode &after = playlist->get_state();
4858 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4860 // playlist is frozen, so we have to update manually
4862 playlist->Modified(); /* EMIT SIGNAL */
4866 Editor::track_height_step_timeout ()
4869 struct timeval delta;
4871 gettimeofday (&now, 0);
4872 timersub (&now, &last_track_height_step_timestamp, &delta);
4874 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4875 current_stepping_trackview = 0;