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.
29 #include <pbd/error.h>
30 #include <gtkmm2ext/utils.h>
31 #include <pbd/memento_command.h>
33 #include "ardour_ui.h"
35 #include "time_axis_view.h"
36 #include "audio_time_axis.h"
37 #include "audio_region_view.h"
39 #include "streamview.h"
40 #include "region_gain_line.h"
41 #include "automation_time_axis.h"
44 #include "selection.h"
47 #include "rgb_macros.h"
49 #include <ardour/types.h>
50 #include <ardour/route.h>
51 #include <ardour/audio_track.h>
52 #include <ardour/audio_diskstream.h>
53 #include <ardour/playlist.h>
54 #include <ardour/audioplaylist.h>
55 #include <ardour/audioregion.h>
56 #include <ardour/dB.h>
57 #include <ardour/utils.h>
58 #include <ardour/region_factory.h>
65 using namespace ARDOUR;
69 using namespace Editing;
72 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
86 switch (event->type) {
87 case GDK_BUTTON_RELEASE:
88 case GDK_BUTTON_PRESS:
89 case GDK_2BUTTON_PRESS:
90 case GDK_3BUTTON_PRESS:
91 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
93 case GDK_MOTION_NOTIFY:
94 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
96 case GDK_ENTER_NOTIFY:
97 case GDK_LEAVE_NOTIFY:
98 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
101 case GDK_KEY_RELEASE:
102 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
105 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
109 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
110 position is negative (as can be the case with motion events in particular),
111 the frame location is always positive.
114 return pixel_to_frame (*pcx);
118 Editor::mouse_mode_toggled (MouseMode m)
120 if (ignore_mouse_mode_toggle) {
126 if (mouse_select_button.get_active()) {
132 if (mouse_move_button.get_active()) {
138 if (mouse_gain_button.get_active()) {
144 if (mouse_zoom_button.get_active()) {
150 if (mouse_timefx_button.get_active()) {
156 if (mouse_audition_button.get_active()) {
167 Editor::set_mouse_mode (MouseMode m, bool force)
169 if (drag_info.item) {
173 if (!force && m == mouse_mode) {
181 if (mouse_mode != MouseRange) {
183 /* in all modes except range, hide the range selection,
184 show the object (region) selection.
187 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
188 (*i)->set_should_show_selection (true);
190 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
191 (*i)->hide_selection ();
197 in range mode,show the range selection.
200 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
201 if ((*i)->get_selected()) {
202 (*i)->show_selection (selection->time);
207 /* XXX the hack of unsetting all other buttongs should go
208 away once GTK2 allows us to use regular radio buttons drawn like
209 normal buttons, rather than my silly GroupedButton hack.
212 ignore_mouse_mode_toggle = true;
214 switch (mouse_mode) {
216 mouse_select_button.set_active (true);
217 current_canvas_cursor = selector_cursor;
221 mouse_move_button.set_active (true);
222 current_canvas_cursor = grabber_cursor;
226 mouse_gain_button.set_active (true);
227 current_canvas_cursor = cross_hair_cursor;
231 mouse_zoom_button.set_active (true);
232 current_canvas_cursor = zoom_cursor;
236 mouse_timefx_button.set_active (true);
237 current_canvas_cursor = time_fx_cursor; // just use playhead
241 mouse_audition_button.set_active (true);
242 current_canvas_cursor = speaker_cursor;
246 ignore_mouse_mode_toggle = false;
249 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
254 Editor::step_mouse_mode (bool next)
256 switch (current_mouse_mode()) {
258 if (next) set_mouse_mode (MouseRange);
259 else set_mouse_mode (MouseTimeFX);
263 if (next) set_mouse_mode (MouseZoom);
264 else set_mouse_mode (MouseObject);
268 if (next) set_mouse_mode (MouseGain);
269 else set_mouse_mode (MouseRange);
273 if (next) set_mouse_mode (MouseTimeFX);
274 else set_mouse_mode (MouseZoom);
278 if (next) set_mouse_mode (MouseAudition);
279 else set_mouse_mode (MouseGain);
283 if (next) set_mouse_mode (MouseObject);
284 else set_mouse_mode (MouseTimeFX);
290 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
296 /* in object/audition/timefx mode, any button press sets
297 the selection if the object can be selected. this is a
298 bit of hack, because we want to avoid this if the
299 mouse operation is a region alignment.
301 note: not dbl-click or triple-click
304 if (((mouse_mode != MouseObject) &&
305 (mouse_mode != MouseAudition || item_type != RegionItem) &&
306 (mouse_mode != MouseTimeFX || item_type != RegionItem)) ||
307 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
312 Selection::Operation op = Keyboard::selection_type (event->button.state);
313 bool press = (event->type == GDK_BUTTON_PRESS);
315 begin_reversible_command (_("select on click"));
319 c1 = set_selected_track_from_click (op, true);
320 c2 = set_selected_regionview_from_click (press, op, true);
324 case RegionViewNameHighlight:
326 c1 = set_selected_track_from_click (op, true);
327 c2 = set_selected_regionview_from_click (press, op, true);
331 case GainAutomationControlPointItem:
332 case PanAutomationControlPointItem:
333 case RedirectAutomationControlPointItem:
334 c1 = set_selected_track_from_click (op, true);
335 c2 = set_selected_control_point_from_click (op, false);
340 commit = set_selected_track_from_click (op, true);
343 case AutomationTrackItem:
344 commit = set_selected_track_from_click (op, true);
351 #define SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
352 #ifdef SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
353 /* in range mode, button 1/2/3 press potentially selects a track */
355 if (mouse_mode == MouseRange &&
356 event->type == GDK_BUTTON_PRESS &&
357 event->button.button <= 3) {
362 case AutomationTrackItem:
363 commit = set_selected_track_from_click (op, true);
372 commit_reversible_command ();
377 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
379 nframes_t where = event_frame (event, 0, 0);
381 track_canvas.grab_focus();
383 if (session && session->actively_recording()) {
387 button_selection (item, event, item_type);
389 if (drag_info.item == 0 &&
390 (Keyboard::is_delete_event (&event->button) ||
391 Keyboard::is_context_menu_event (&event->button) ||
392 Keyboard::is_edit_event (&event->button))) {
394 /* handled by button release */
398 switch (event->button.button) {
401 if (event->type == GDK_BUTTON_PRESS) {
403 if (drag_info.item) {
404 drag_info.item->ungrab (event->button.time);
407 /* single mouse clicks on any of these item types operate
408 independent of mouse mode, mostly because they are
409 not on the main track canvas or because we want
415 case PlayheadCursorItem:
416 start_cursor_grab (item, event);
420 if (Keyboard::modifier_state_equals (event->button.state,
421 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
422 hide_marker (item, event);
424 start_marker_grab (item, event);
428 case TempoMarkerItem:
429 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
430 start_tempo_marker_copy_grab (item, event);
432 start_tempo_marker_grab (item, event);
436 case MeterMarkerItem:
437 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
438 start_meter_marker_copy_grab (item, event);
440 start_meter_marker_grab (item, event);
450 case RangeMarkerBarItem:
451 start_range_markerbar_op (item, event, CreateRangeMarker);
455 case TransportMarkerBarItem:
456 start_range_markerbar_op (item, event, CreateTransportMarker);
465 switch (mouse_mode) {
468 case StartSelectionTrimItem:
469 start_selection_op (item, event, SelectionStartTrim);
472 case EndSelectionTrimItem:
473 start_selection_op (item, event, SelectionEndTrim);
477 if (Keyboard::modifier_state_contains
478 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
479 // contains and not equals because I can't use alt as a modifier alone.
480 start_selection_grab (item, event);
481 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
482 /* grab selection for moving */
483 start_selection_op (item, event, SelectionMove);
486 /* this was debated, but decided the more common action was to
487 make a new selection */
488 start_selection_op (item, event, CreateSelection);
493 start_selection_op (item, event, CreateSelection);
499 if (Keyboard::modifier_state_contains (event->button.state,
500 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt))
501 && event->type == GDK_BUTTON_PRESS) {
503 start_rubberband_select (item, event);
505 } else if (event->type == GDK_BUTTON_PRESS) {
508 case FadeInHandleItem:
509 start_fade_in_grab (item, event);
512 case FadeOutHandleItem:
513 start_fade_out_grab (item, event);
517 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
518 start_region_copy_grab (item, event);
519 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
520 start_region_brush_grab (item, event);
522 start_region_grab (item, event);
526 case RegionViewNameHighlight:
527 start_trim (item, event);
532 /* rename happens on edit clicks */
533 start_trim (clicked_regionview->get_name_highlight(), event);
537 case GainAutomationControlPointItem:
538 case PanAutomationControlPointItem:
539 case RedirectAutomationControlPointItem:
540 start_control_point_grab (item, event);
544 case GainAutomationLineItem:
545 case PanAutomationLineItem:
546 case RedirectAutomationLineItem:
547 start_line_grab_from_line (item, event);
552 case AutomationTrackItem:
553 start_rubberband_select (item, event);
556 /* <CMT Additions> */
557 case ImageFrameHandleStartItem:
558 imageframe_start_handle_op(item, event) ;
561 case ImageFrameHandleEndItem:
562 imageframe_end_handle_op(item, event) ;
565 case MarkerViewHandleStartItem:
566 markerview_item_start_handle_op(item, event) ;
569 case MarkerViewHandleEndItem:
570 markerview_item_end_handle_op(item, event) ;
573 /* </CMT Additions> */
575 /* <CMT Additions> */
577 start_markerview_grab(item, event) ;
580 start_imageframe_grab(item, event) ;
582 /* </CMT Additions> */
598 // start_line_grab_from_regionview (item, event);
601 case GainControlPointItem:
602 start_control_point_grab (item, event);
606 start_line_grab_from_line (item, event);
609 case GainAutomationControlPointItem:
610 case PanAutomationControlPointItem:
611 case RedirectAutomationControlPointItem:
612 start_control_point_grab (item, event);
623 case GainAutomationControlPointItem:
624 case PanAutomationControlPointItem:
625 case RedirectAutomationControlPointItem:
626 start_control_point_grab (item, event);
629 case GainAutomationLineItem:
630 case PanAutomationLineItem:
631 case RedirectAutomationLineItem:
632 start_line_grab_from_line (item, event);
636 // XXX need automation mode to identify which
638 // start_line_grab_from_regionview (item, event);
648 if (event->type == GDK_BUTTON_PRESS) {
649 start_mouse_zoom (item, event);
656 if (item_type == RegionItem) {
657 start_time_fx (item, event);
662 /* handled in release */
671 switch (mouse_mode) {
673 if (event->type == GDK_BUTTON_PRESS) {
676 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
677 start_region_copy_grab (item, event);
679 start_region_grab (item, event);
683 case GainAutomationControlPointItem:
684 case PanAutomationControlPointItem:
685 case RedirectAutomationControlPointItem:
686 start_control_point_grab (item, event);
697 case RegionViewNameHighlight:
698 start_trim (item, event);
703 start_trim (clicked_regionview->get_name_highlight(), event);
714 if (event->type == GDK_BUTTON_PRESS) {
715 /* relax till release */
722 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
723 temporal_zoom_session();
725 temporal_zoom_to_frame (true, event_frame(event));
740 switch (mouse_mode) {
742 //temporal_zoom_to_frame (true, where);
743 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
744 temporal_zoom_to_frame (true, where);
747 temporal_zoom_step (true);
752 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
753 scroll_backward (0.6f);
756 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
757 scroll_tracks_up_line ();
759 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
760 if (clicked_trackview) {
761 if (!current_stepping_trackview) {
762 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
763 current_stepping_trackview = clicked_trackview;
765 gettimeofday (&last_track_height_step_timestamp, 0);
766 current_stepping_trackview->step_height (true);
769 else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
770 temporal_zoom_to_frame (true, where);
777 switch (mouse_mode) {
779 // temporal_zoom_to_frame (false, where);
780 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
781 temporal_zoom_to_frame (false, where);
784 temporal_zoom_step (false);
789 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
790 scroll_forward (0.6f);
793 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
794 scroll_tracks_down_line ();
796 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
797 if (clicked_trackview) {
798 if (!current_stepping_trackview) {
799 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
800 current_stepping_trackview = clicked_trackview;
802 gettimeofday (&last_track_height_step_timestamp, 0);
803 current_stepping_trackview->step_height (false);
805 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
806 temporal_zoom_to_frame (false, where);
821 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
823 nframes_t where = event_frame (event, 0, 0);
825 /* no action if we're recording */
827 if (session && session->actively_recording()) {
831 /* first, see if we're finishing a drag ... */
833 if (drag_info.item) {
834 if (end_grab (item, event)) {
835 /* grab dragged, so do nothing else */
840 button_selection (item, event, item_type);
842 /* edit events get handled here */
844 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
850 case TempoMarkerItem:
851 edit_tempo_marker (item);
854 case MeterMarkerItem:
855 edit_meter_marker (item);
859 if (clicked_regionview->name_active()) {
860 return mouse_rename_region (item, event);
870 /* context menu events get handled here */
872 if (Keyboard::is_context_menu_event (&event->button)) {
874 if (drag_info.item == 0) {
876 /* no matter which button pops up the context menu, tell the menu
877 widget to use button 1 to drive menu selection.
882 case FadeInHandleItem:
884 case FadeOutHandleItem:
885 popup_fade_context_menu (1, event->button.time, item, item_type);
889 popup_track_context_menu (1, event->button.time, item_type, false, where);
893 case RegionViewNameHighlight:
895 popup_track_context_menu (1, event->button.time, item_type, false, where);
899 popup_track_context_menu (1, event->button.time, item_type, true, where);
902 case AutomationTrackItem:
903 popup_track_context_menu (1, event->button.time, item_type, false, where);
907 case RangeMarkerBarItem:
908 case TransportMarkerBarItem:
911 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
915 marker_context_menu (&event->button, item);
918 case TempoMarkerItem:
919 tm_marker_context_menu (&event->button, item);
922 case MeterMarkerItem:
923 tm_marker_context_menu (&event->button, item);
926 case CrossfadeViewItem:
927 popup_track_context_menu (1, event->button.time, item_type, false, where);
930 /* <CMT Additions> */
932 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
934 case ImageFrameTimeAxisItem:
935 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
938 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
940 case MarkerTimeAxisItem:
941 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
943 /* <CMT Additions> */
954 /* delete events get handled here */
956 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
959 case TempoMarkerItem:
960 remove_tempo_marker (item);
963 case MeterMarkerItem:
964 remove_meter_marker (item);
968 remove_marker (*item, event);
972 if (mouse_mode == MouseObject) {
973 remove_clicked_region ();
977 case GainControlPointItem:
978 if (mouse_mode == MouseGain) {
979 remove_gain_control_point (item, event);
983 case GainAutomationControlPointItem:
984 case PanAutomationControlPointItem:
985 case RedirectAutomationControlPointItem:
986 remove_control_point (item, event);
995 switch (event->button.button) {
999 /* see comments in button_press_handler */
1000 case EditCursorItem:
1001 case PlayheadCursorItem:
1004 case GainAutomationLineItem:
1005 case PanAutomationLineItem:
1006 case RedirectAutomationLineItem:
1007 case StartSelectionTrimItem:
1008 case EndSelectionTrimItem:
1012 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1013 snap_to (where, 0, true);
1015 mouse_add_new_marker (where);
1019 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1022 mouse_add_new_tempo_event (where);
1026 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1034 switch (mouse_mode) {
1036 switch (item_type) {
1037 case AutomationTrackItem:
1038 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1052 // Gain only makes sense for audio regions
1053 if ( ! dynamic_cast<AudioRegionView*>(clicked_regionview))
1056 switch (item_type) {
1058 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1062 case AutomationTrackItem:
1063 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1064 add_automation_event (item, event, where, event->button.y);
1073 switch (item_type) {
1075 audition_selected_region ();
1092 switch (mouse_mode) {
1095 switch (item_type) {
1097 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1099 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1102 // Button2 click is unused
1115 // x_style_paste (where, 1.0);
1135 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1141 switch (item_type) {
1142 case GainControlPointItem:
1143 if (mouse_mode == MouseGain) {
1144 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1145 cp->set_visible (true);
1149 at_y = cp->get_y ();
1150 cp->item->i2w (at_x, at_y);
1154 fraction = 1.0 - (cp->get_y() / cp->line.height());
1156 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1157 show_verbose_canvas_cursor ();
1159 if (is_drawable()) {
1160 track_canvas.get_window()->set_cursor (*fader_cursor);
1165 case GainAutomationControlPointItem:
1166 case PanAutomationControlPointItem:
1167 case RedirectAutomationControlPointItem:
1168 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1169 cp->set_visible (true);
1173 at_y = cp->get_y ();
1174 cp->item->i2w (at_x, at_y);
1178 fraction = 1.0 - (cp->get_y() / cp->line.height());
1180 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1181 show_verbose_canvas_cursor ();
1183 if (is_drawable()) {
1184 track_canvas.get_window()->set_cursor (*fader_cursor);
1189 if (mouse_mode == MouseGain) {
1190 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1192 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1193 if (is_drawable()) {
1194 track_canvas.get_window()->set_cursor (*fader_cursor);
1199 case GainAutomationLineItem:
1200 case RedirectAutomationLineItem:
1201 case PanAutomationLineItem:
1203 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1205 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1207 if (is_drawable()) {
1208 track_canvas.get_window()->set_cursor (*fader_cursor);
1212 case RegionViewNameHighlight:
1213 if (is_drawable() && mouse_mode == MouseObject) {
1214 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1218 case StartSelectionTrimItem:
1219 case EndSelectionTrimItem:
1220 /* <CMT Additions> */
1221 case ImageFrameHandleStartItem:
1222 case ImageFrameHandleEndItem:
1223 case MarkerViewHandleStartItem:
1224 case MarkerViewHandleEndItem:
1225 /* </CMT Additions> */
1227 if (is_drawable()) {
1228 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1232 case EditCursorItem:
1233 case PlayheadCursorItem:
1234 if (is_drawable()) {
1235 track_canvas.get_window()->set_cursor (*grabber_cursor);
1239 case RegionViewName:
1241 /* when the name is not an active item, the entire name highlight is for trimming */
1243 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1244 if (mouse_mode == MouseObject && is_drawable()) {
1245 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1251 case AutomationTrackItem:
1252 if (is_drawable()) {
1253 Gdk::Cursor *cursor;
1254 switch (mouse_mode) {
1256 cursor = selector_cursor;
1259 cursor = zoom_cursor;
1262 cursor = cross_hair_cursor;
1266 track_canvas.get_window()->set_cursor (*cursor);
1268 AutomationTimeAxisView* atv;
1269 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1270 clear_entered_track = false;
1271 set_entered_track (atv);
1277 case RangeMarkerBarItem:
1278 case TransportMarkerBarItem:
1281 if (is_drawable()) {
1282 time_canvas.get_window()->set_cursor (*timebar_cursor);
1287 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1290 marker->set_color_rgba (color_map[cEnteredMarker]);
1292 case MeterMarkerItem:
1293 case TempoMarkerItem:
1294 if (is_drawable()) {
1295 time_canvas.get_window()->set_cursor (*timebar_cursor);
1298 case FadeInHandleItem:
1299 case FadeOutHandleItem:
1300 if (mouse_mode == MouseObject) {
1301 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1303 rect->property_fill_color_rgba() = 0;
1304 rect->property_outline_pixels() = 1;
1313 /* second pass to handle entered track status in a comprehensible way.
1316 switch (item_type) {
1318 case GainAutomationLineItem:
1319 case RedirectAutomationLineItem:
1320 case PanAutomationLineItem:
1321 case GainControlPointItem:
1322 case GainAutomationControlPointItem:
1323 case PanAutomationControlPointItem:
1324 case RedirectAutomationControlPointItem:
1325 /* these do not affect the current entered track state */
1326 clear_entered_track = false;
1329 case AutomationTrackItem:
1330 /* handled above already */
1334 set_entered_track (0);
1342 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1351 switch (item_type) {
1352 case GainControlPointItem:
1353 case GainAutomationControlPointItem:
1354 case PanAutomationControlPointItem:
1355 case RedirectAutomationControlPointItem:
1356 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1357 if (cp->line.npoints() > 1) {
1358 if (!cp->selected) {
1359 cp->set_visible (false);
1363 if (is_drawable()) {
1364 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1367 hide_verbose_canvas_cursor ();
1370 case RegionViewNameHighlight:
1371 case StartSelectionTrimItem:
1372 case EndSelectionTrimItem:
1373 case EditCursorItem:
1374 case PlayheadCursorItem:
1375 /* <CMT Additions> */
1376 case ImageFrameHandleStartItem:
1377 case ImageFrameHandleEndItem:
1378 case MarkerViewHandleStartItem:
1379 case MarkerViewHandleEndItem:
1380 /* </CMT Additions> */
1381 if (is_drawable()) {
1382 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1387 case GainAutomationLineItem:
1388 case RedirectAutomationLineItem:
1389 case PanAutomationLineItem:
1390 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1392 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1394 line->property_fill_color_rgba() = al->get_line_color();
1396 if (is_drawable()) {
1397 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1401 case RegionViewName:
1402 /* see enter_handler() for notes */
1403 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1404 if (is_drawable() && mouse_mode == MouseObject) {
1405 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1410 case RangeMarkerBarItem:
1411 case TransportMarkerBarItem:
1415 if (is_drawable()) {
1416 time_canvas.get_window()->set_cursor (*timebar_cursor);
1421 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1424 loc = find_location_from_marker (marker, is_start);
1425 if (loc) location_flags_changed (loc, this);
1427 case MeterMarkerItem:
1428 case TempoMarkerItem:
1430 if (is_drawable()) {
1431 time_canvas.get_window()->set_cursor (*timebar_cursor);
1436 case FadeInHandleItem:
1437 case FadeOutHandleItem:
1438 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1440 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1442 rect->property_fill_color_rgba() = rv->get_fill_color();
1443 rect->property_outline_pixels() = 0;
1448 case AutomationTrackItem:
1449 if (is_drawable()) {
1450 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1451 clear_entered_track = true;
1452 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1464 Editor::left_automation_track ()
1466 if (clear_entered_track) {
1467 set_entered_track (0);
1468 clear_entered_track = false;
1474 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1478 /* We call this so that MOTION_NOTIFY events continue to be
1479 delivered to the canvas. We need to do this because we set
1480 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1481 the density of the events, at the expense of a round-trip
1482 to the server. Given that this will mostly occur on cases
1483 where DISPLAY = :0.0, and given the cost of what the motion
1484 event might do, its a good tradeoff.
1487 track_canvas.get_pointer (x, y);
1489 if (current_stepping_trackview) {
1490 /* don't keep the persistent stepped trackview if the mouse moves */
1491 current_stepping_trackview = 0;
1492 step_timeout.disconnect ();
1495 if (session && session->actively_recording()) {
1496 /* Sorry. no dragging stuff around while we record */
1500 drag_info.item_type = item_type;
1501 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1502 &drag_info.current_pointer_y);
1504 if (!from_autoscroll && drag_info.item) {
1505 /* item != 0 is the best test i can think of for dragging.
1507 if (!drag_info.move_threshold_passed) {
1509 drag_info.move_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1511 // and change the initial grab loc/frame if this drag info wants us to
1513 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1514 drag_info.grab_frame = drag_info.current_pointer_frame;
1515 drag_info.grab_x = drag_info.current_pointer_x;
1516 drag_info.grab_y = drag_info.current_pointer_y;
1517 drag_info.last_pointer_frame = drag_info.grab_frame;
1518 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1523 switch (item_type) {
1524 case PlayheadCursorItem:
1525 case EditCursorItem:
1527 case GainControlPointItem:
1528 case RedirectAutomationControlPointItem:
1529 case GainAutomationControlPointItem:
1530 case PanAutomationControlPointItem:
1531 case TempoMarkerItem:
1532 case MeterMarkerItem:
1533 case RegionViewNameHighlight:
1534 case StartSelectionTrimItem:
1535 case EndSelectionTrimItem:
1538 case RedirectAutomationLineItem:
1539 case GainAutomationLineItem:
1540 case PanAutomationLineItem:
1541 case FadeInHandleItem:
1542 case FadeOutHandleItem:
1543 /* <CMT Additions> */
1544 case ImageFrameHandleStartItem:
1545 case ImageFrameHandleEndItem:
1546 case MarkerViewHandleStartItem:
1547 case MarkerViewHandleEndItem:
1548 /* </CMT Additions> */
1549 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1550 (event->motion.state & Gdk::BUTTON2_MASK))) {
1551 if (!from_autoscroll) {
1552 maybe_autoscroll (event);
1554 (this->*(drag_info.motion_callback)) (item, event);
1563 switch (mouse_mode) {
1568 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1569 (event->motion.state & GDK_BUTTON2_MASK))) {
1570 if (!from_autoscroll) {
1571 maybe_autoscroll (event);
1573 (this->*(drag_info.motion_callback)) (item, event);
1584 track_canvas_motion (event);
1585 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1593 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1595 if (drag_info.item == 0) {
1596 fatal << _("programming error: start_grab called without drag item") << endmsg;
1602 cursor = grabber_cursor;
1605 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1607 if (event->button.button == 2) {
1608 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1609 drag_info.y_constrained = true;
1610 drag_info.x_constrained = false;
1612 drag_info.y_constrained = false;
1613 drag_info.x_constrained = true;
1616 drag_info.x_constrained = false;
1617 drag_info.y_constrained = false;
1620 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1621 drag_info.last_pointer_frame = drag_info.grab_frame;
1622 drag_info.current_pointer_frame = drag_info.grab_frame;
1623 drag_info.current_pointer_x = drag_info.grab_x;
1624 drag_info.current_pointer_y = drag_info.grab_y;
1625 drag_info.cumulative_x_drag = 0;
1626 drag_info.cumulative_y_drag = 0;
1627 drag_info.first_move = true;
1628 drag_info.move_threshold_passed = false;
1629 drag_info.want_move_threshold = false;
1630 drag_info.pointer_frame_offset = 0;
1631 drag_info.brushing = false;
1632 drag_info.copied_location = 0;
1634 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1636 event->button.time);
1638 if (session && session->transport_rolling()) {
1639 drag_info.was_rolling = true;
1641 drag_info.was_rolling = false;
1644 switch (snap_type) {
1645 case SnapToRegionStart:
1646 case SnapToRegionEnd:
1647 case SnapToRegionSync:
1648 case SnapToRegionBoundary:
1649 build_region_boundary_cache ();
1657 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1659 drag_info.item->ungrab (0);
1660 drag_info.item = new_item;
1663 cursor = grabber_cursor;
1666 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1670 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1672 bool did_drag = false;
1674 stop_canvas_autoscroll ();
1676 if (drag_info.item == 0) {
1680 drag_info.item->ungrab (event->button.time);
1682 if (drag_info.finished_callback) {
1683 (this->*(drag_info.finished_callback)) (item, event);
1686 did_drag = !drag_info.first_move;
1688 hide_verbose_canvas_cursor();
1691 drag_info.copy = false;
1692 drag_info.motion_callback = 0;
1693 drag_info.finished_callback = 0;
1694 drag_info.last_trackview = 0;
1695 drag_info.last_frame_position = 0;
1696 drag_info.grab_frame = 0;
1697 drag_info.last_pointer_frame = 0;
1698 drag_info.current_pointer_frame = 0;
1699 drag_info.brushing = false;
1701 if (drag_info.copied_location) {
1702 delete drag_info.copied_location;
1703 drag_info.copied_location = 0;
1710 Editor::set_edit_cursor (GdkEvent* event)
1712 nframes_t pointer_frame = event_frame (event);
1714 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1715 if (snap_type != SnapToEditCursor) {
1716 snap_to (pointer_frame);
1720 edit_cursor->set_position (pointer_frame);
1721 edit_cursor_clock.set (pointer_frame);
1725 Editor::set_playhead_cursor (GdkEvent* event)
1727 nframes_t pointer_frame = event_frame (event);
1729 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1730 snap_to (pointer_frame);
1734 session->request_locate (pointer_frame, session->transport_rolling());
1739 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1741 drag_info.item = item;
1742 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1743 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1747 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1748 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1752 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1754 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1758 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1760 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1762 nframes_t fade_length;
1764 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1765 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1771 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1775 if (pos < (arv->region()->position() + 64)) {
1776 fade_length = 64; // this should be a minimum defined somewhere
1777 } else if (pos > arv->region()->last_frame()) {
1778 fade_length = arv->region()->length();
1780 fade_length = pos - arv->region()->position();
1783 arv->reset_fade_in_shape_width (fade_length);
1785 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1787 drag_info.first_move = false;
1791 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1793 if (drag_info.first_move) return;
1795 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1797 nframes_t fade_length;
1799 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1800 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1806 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1810 if (pos < (arv->region()->position() + 64)) {
1811 fade_length = 64; // this should be a minimum defined somewhere
1813 else if (pos > arv->region()->last_frame()) {
1814 fade_length = arv->region()->length();
1817 fade_length = pos - arv->region()->position();
1820 begin_reversible_command (_("change fade in length"));
1821 XMLNode &before = arv->audio_region()->get_state();
1823 arv->audio_region()->set_fade_in_length (fade_length);
1825 XMLNode &after = arv->audio_region()->get_state();
1826 session->add_command(new MementoCommand<ARDOUR::AudioRegion>(*arv->audio_region().get(), &before, &after));
1827 commit_reversible_command ();
1828 fade_in_drag_motion_callback (item, event);
1832 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1834 drag_info.item = item;
1835 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1836 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1840 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1841 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1845 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1847 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1851 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1853 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1855 nframes_t fade_length;
1857 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1858 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1864 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1868 if (pos > (arv->region()->last_frame() - 64)) {
1869 fade_length = 64; // this should really be a minimum fade defined somewhere
1871 else if (pos < arv->region()->position()) {
1872 fade_length = arv->region()->length();
1875 fade_length = arv->region()->last_frame() - pos;
1878 arv->reset_fade_out_shape_width (fade_length);
1880 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1882 drag_info.first_move = false;
1886 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1888 if (drag_info.first_move) return;
1890 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1892 nframes_t fade_length;
1894 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1895 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1901 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1905 if (pos > (arv->region()->last_frame() - 64)) {
1906 fade_length = 64; // this should really be a minimum fade defined somewhere
1908 else if (pos < arv->region()->position()) {
1909 fade_length = arv->region()->length();
1912 fade_length = arv->region()->last_frame() - pos;
1915 begin_reversible_command (_("change fade out length"));
1916 XMLNode &before = arv->region()->get_state();
1918 arv->audio_region()->set_fade_out_length (fade_length);
1920 XMLNode &after = arv->region()->get_state();
1921 session->add_command(new MementoCommand<ARDOUR::Region>(*arv->region().get(), &before, &after));
1922 commit_reversible_command ();
1924 fade_out_drag_motion_callback (item, event);
1928 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1930 drag_info.item = item;
1931 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1932 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1936 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1937 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1941 Cursor* cursor = (Cursor *) drag_info.data;
1943 if (session && cursor == playhead_cursor) {
1944 if (drag_info.was_rolling) {
1945 session->request_stop ();
1949 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1951 show_verbose_time_cursor (cursor->current_frame, 10);
1955 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1957 Cursor* cursor = (Cursor *) drag_info.data;
1958 nframes_t adjusted_frame;
1960 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1961 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1967 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1968 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1969 snap_to (adjusted_frame);
1973 if (adjusted_frame == drag_info.last_pointer_frame) return;
1975 cursor->set_position (adjusted_frame);
1977 if (cursor == edit_cursor) {
1978 edit_cursor_clock.set (cursor->current_frame);
1981 show_verbose_time_cursor (cursor->current_frame, 10);
1983 drag_info.last_pointer_frame = adjusted_frame;
1984 drag_info.first_move = false;
1988 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1990 if (drag_info.first_move) return;
1992 cursor_drag_motion_callback (item, event);
1994 if (item == &playhead_cursor->canvas_item) {
1996 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1998 } else if (item == &edit_cursor->canvas_item) {
1999 edit_cursor->set_position (edit_cursor->current_frame);
2000 edit_cursor_clock.set (edit_cursor->current_frame);
2005 Editor::update_marker_drag_item (Location *location)
2007 double x1 = frame_to_pixel (location->start());
2008 double x2 = frame_to_pixel (location->end());
2010 if (location->is_mark()) {
2011 marker_drag_line_points.front().set_x(x1);
2012 marker_drag_line_points.back().set_x(x1);
2013 marker_drag_line->property_points() = marker_drag_line_points;
2016 range_marker_drag_rect->property_x1() = x1;
2017 range_marker_drag_rect->property_x2() = x2;
2022 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2026 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2027 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2033 Location *location = find_location_from_marker (marker, is_start);
2035 drag_info.item = item;
2036 drag_info.data = marker;
2037 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2038 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2042 drag_info.copied_location = new Location (*location);
2043 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2045 update_marker_drag_item (location);
2047 if (location->is_mark()) {
2048 marker_drag_line->show();
2049 marker_drag_line->raise_to_top();
2052 range_marker_drag_rect->show();
2053 range_marker_drag_rect->raise_to_top();
2056 if (is_start) show_verbose_time_cursor (location->start(), 10);
2057 else show_verbose_time_cursor (location->end(), 10);
2061 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2064 Marker* marker = (Marker *) drag_info.data;
2065 Location *real_location;
2066 Location *copy_location;
2068 bool move_both = false;
2072 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2073 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2079 nframes_t next = newframe;
2081 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2082 snap_to (newframe, 0, true);
2085 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2089 /* call this to find out if its the start or end */
2091 real_location = find_location_from_marker (marker, is_start);
2093 /* use the copy that we're "dragging" around */
2095 copy_location = drag_info.copied_location;
2097 f_delta = copy_location->end() - copy_location->start();
2099 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2103 if (copy_location->is_mark()) {
2106 copy_location->set_start (newframe);
2110 if (is_start) { // start-of-range marker
2113 copy_location->set_start (newframe);
2114 copy_location->set_end (newframe + f_delta);
2115 } else if (newframe < copy_location->end()) {
2116 copy_location->set_start (newframe);
2118 snap_to (next, 1, true);
2119 copy_location->set_end (next);
2120 copy_location->set_start (newframe);
2123 } else { // end marker
2126 copy_location->set_end (newframe);
2127 copy_location->set_start (newframe - f_delta);
2128 } else if (newframe > copy_location->start()) {
2129 copy_location->set_end (newframe);
2131 } else if (newframe > 0) {
2132 snap_to (next, -1, true);
2133 copy_location->set_start (next);
2134 copy_location->set_end (newframe);
2139 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2140 drag_info.first_move = false;
2142 update_marker_drag_item (copy_location);
2144 LocationMarkers* lm = find_location_markers (real_location);
2145 lm->set_position (copy_location->start(), copy_location->end());
2147 show_verbose_time_cursor (newframe, 10);
2151 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2153 if (drag_info.first_move) {
2154 marker_drag_motion_callback (item, event);
2158 Marker* marker = (Marker *) drag_info.data;
2162 begin_reversible_command ( _("move marker") );
2163 XMLNode &before = session->locations()->get_state();
2165 Location * location = find_location_from_marker (marker, is_start);
2168 if (location->is_mark()) {
2169 location->set_start (drag_info.copied_location->start());
2171 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2175 XMLNode &after = session->locations()->get_state();
2176 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2177 commit_reversible_command ();
2179 marker_drag_line->hide();
2180 range_marker_drag_rect->hide();
2184 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2187 MeterMarker* meter_marker;
2189 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2190 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2194 meter_marker = dynamic_cast<MeterMarker*> (marker);
2196 MetricSection& section (meter_marker->meter());
2198 if (!section.movable()) {
2202 drag_info.item = item;
2203 drag_info.data = marker;
2204 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2205 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2209 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2211 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2215 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2218 MeterMarker* meter_marker;
2220 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2221 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2225 meter_marker = dynamic_cast<MeterMarker*> (marker);
2227 // create a dummy marker for visual representation of moving the copy.
2228 // The actual copying is not done before we reach the finish callback.
2230 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2231 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2232 *new MeterSection(meter_marker->meter()));
2234 drag_info.item = &new_marker->the_item();
2235 drag_info.copy = true;
2236 drag_info.data = new_marker;
2237 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2238 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2242 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2244 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2248 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2250 MeterMarker* marker = (MeterMarker *) drag_info.data;
2251 nframes_t adjusted_frame;
2253 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2254 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2260 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2261 snap_to (adjusted_frame);
2264 if (adjusted_frame == drag_info.last_pointer_frame) return;
2266 marker->set_position (adjusted_frame);
2269 drag_info.last_pointer_frame = adjusted_frame;
2270 drag_info.first_move = false;
2272 show_verbose_time_cursor (adjusted_frame, 10);
2276 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2278 if (drag_info.first_move) return;
2280 meter_marker_drag_motion_callback (drag_info.item, event);
2282 MeterMarker* marker = (MeterMarker *) drag_info.data;
2285 TempoMap& map (session->tempo_map());
2286 map.bbt_time (drag_info.last_pointer_frame, when);
2288 if (drag_info.copy == true) {
2289 begin_reversible_command (_("copy meter mark"));
2290 XMLNode &before = map.get_state();
2291 map.add_meter (marker->meter(), when);
2292 XMLNode &after = map.get_state();
2293 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2294 commit_reversible_command ();
2296 // delete the dummy marker we used for visual representation of copying.
2297 // a new visual marker will show up automatically.
2300 begin_reversible_command (_("move meter mark"));
2301 XMLNode &before = map.get_state();
2302 map.move_meter (marker->meter(), when);
2303 XMLNode &after = map.get_state();
2304 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2305 commit_reversible_command ();
2310 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2313 TempoMarker* tempo_marker;
2315 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2316 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2320 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2321 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2325 MetricSection& section (tempo_marker->tempo());
2327 if (!section.movable()) {
2331 drag_info.item = item;
2332 drag_info.data = marker;
2333 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2334 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2338 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2339 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2343 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2346 TempoMarker* tempo_marker;
2348 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2349 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2353 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2354 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2358 // create a dummy marker for visual representation of moving the copy.
2359 // The actual copying is not done before we reach the finish callback.
2361 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2362 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2363 *new TempoSection(tempo_marker->tempo()));
2365 drag_info.item = &new_marker->the_item();
2366 drag_info.copy = true;
2367 drag_info.data = new_marker;
2368 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2369 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2373 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2375 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2379 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2381 TempoMarker* marker = (TempoMarker *) drag_info.data;
2382 nframes_t adjusted_frame;
2384 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2385 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2391 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2392 snap_to (adjusted_frame);
2395 if (adjusted_frame == drag_info.last_pointer_frame) return;
2397 /* OK, we've moved far enough to make it worth actually move the thing. */
2399 marker->set_position (adjusted_frame);
2401 show_verbose_time_cursor (adjusted_frame, 10);
2403 drag_info.last_pointer_frame = adjusted_frame;
2404 drag_info.first_move = false;
2408 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2410 if (drag_info.first_move) return;
2412 tempo_marker_drag_motion_callback (drag_info.item, event);
2414 TempoMarker* marker = (TempoMarker *) drag_info.data;
2417 TempoMap& map (session->tempo_map());
2418 map.bbt_time (drag_info.last_pointer_frame, when);
2420 if (drag_info.copy == true) {
2421 begin_reversible_command (_("copy tempo mark"));
2422 XMLNode &before = map.get_state();
2423 map.add_tempo (marker->tempo(), when);
2424 XMLNode &after = map.get_state();
2425 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2426 commit_reversible_command ();
2428 // delete the dummy marker we used for visual representation of copying.
2429 // a new visual marker will show up automatically.
2432 begin_reversible_command (_("move tempo mark"));
2433 XMLNode &before = map.get_state();
2434 map.move_tempo (marker->tempo(), when);
2435 XMLNode &after = map.get_state();
2436 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2437 commit_reversible_command ();
2442 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2444 ControlPoint* control_point;
2446 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2447 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2451 // We shouldn't remove the first or last gain point
2452 if (control_point->line.is_last_point(*control_point) ||
2453 control_point->line.is_first_point(*control_point)) {
2457 control_point->line.remove_point (*control_point);
2461 Editor::remove_control_point (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 control_point->line.remove_point (*control_point);
2474 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2476 ControlPoint* control_point;
2478 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2479 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2483 drag_info.item = item;
2484 drag_info.data = control_point;
2485 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2486 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2488 start_grab (event, fader_cursor);
2490 control_point->line.start_drag (control_point, 0);
2492 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2493 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2494 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2496 show_verbose_canvas_cursor ();
2500 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2502 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2504 double cx = drag_info.current_pointer_x;
2505 double cy = drag_info.current_pointer_y;
2507 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2508 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2510 if (drag_info.x_constrained) {
2511 cx = drag_info.grab_x;
2513 if (drag_info.y_constrained) {
2514 cy = drag_info.grab_y;
2517 cp->line.parent_group().w2i (cx, cy);
2521 cy = min ((double) cp->line.height(), cy);
2523 //translate cx to frames
2524 nframes_t cx_frames = unit_to_frame (cx);
2526 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2527 snap_to (cx_frames);
2530 float fraction = 1.0 - (cy / cp->line.height());
2534 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2540 cp->line.point_drag (*cp, cx_frames , fraction, push);
2542 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2544 drag_info.first_move = false;
2548 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2550 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2552 if (drag_info.first_move) {
2556 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2557 reset_point_selection ();
2561 control_point_drag_motion_callback (item, event);
2563 cp->line.end_drag (cp);
2567 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2569 switch (mouse_mode) {
2571 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2572 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2580 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2584 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2585 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2589 start_line_grab (al, event);
2593 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2597 nframes_t frame_within_region;
2599 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2603 cx = event->button.x;
2604 cy = event->button.y;
2605 line->parent_group().w2i (cx, cy);
2606 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2608 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2609 current_line_drag_info.after)) {
2610 /* no adjacent points */
2614 drag_info.item = &line->grab_item();
2615 drag_info.data = line;
2616 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2617 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2619 start_grab (event, fader_cursor);
2621 double fraction = 1.0 - (cy / line->height());
2623 line->start_drag (0, fraction);
2625 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2626 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2627 show_verbose_canvas_cursor ();
2631 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2633 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2634 double cx = drag_info.current_pointer_x;
2635 double cy = drag_info.current_pointer_y;
2637 line->parent_group().w2i (cx, cy);
2640 fraction = 1.0 - (cy / line->height());
2644 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2650 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2652 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2656 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2658 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2659 line_drag_motion_callback (item, event);
2664 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2666 if (selection->regions.empty() || clicked_regionview == 0) {
2670 drag_info.copy = false;
2671 drag_info.item = item;
2672 drag_info.data = clicked_regionview;
2673 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2674 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2679 TimeAxisView* tvp = clicked_trackview;
2680 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2682 if (tv && tv->is_audio_track()) {
2683 speed = tv->get_diskstream()->speed();
2686 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2687 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2688 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2689 // we want a move threshold
2690 drag_info.want_move_threshold = true;
2692 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2694 begin_reversible_command (_("move region(s)"));
2698 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2700 if (selection->regions.empty() || clicked_regionview == 0) {
2704 drag_info.copy = true;
2705 drag_info.item = item;
2706 drag_info.data = clicked_regionview;
2710 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2711 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2714 if (atv && atv->is_audio_track()) {
2715 speed = atv->get_diskstream()->speed();
2718 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2719 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2720 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2721 // we want a move threshold
2722 drag_info.want_move_threshold = true;
2723 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2724 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2728 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2730 if (selection->regions.empty() || clicked_regionview == 0) {
2734 drag_info.copy = false;
2735 drag_info.item = item;
2736 drag_info.data = clicked_regionview;
2737 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2738 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2743 TimeAxisView* tvp = clicked_trackview;
2744 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2746 if (tv && tv->is_audio_track()) {
2747 speed = tv->get_diskstream()->speed();
2750 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2751 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2752 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2753 // we want a move threshold
2754 drag_info.want_move_threshold = true;
2755 drag_info.brushing = true;
2757 begin_reversible_command (_("Drag region brush"));
2761 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2765 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2766 nframes_t pending_region_position = 0;
2767 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2768 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2769 bool clamp_y_axis = false;
2770 vector<int32_t> height_list(512) ;
2771 vector<int32_t>::iterator j;
2773 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2775 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2777 drag_info.want_move_threshold = false; // don't copy again
2779 /* this is committed in the grab finished callback. */
2781 begin_reversible_command (_("Drag region copy"));
2783 /* duplicate the region(s) */
2785 vector<RegionView*> new_regionviews;
2787 set<Playlist*> affected_playlists;
2788 pair<set<Playlist*>::iterator,bool> insert_result;
2790 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2795 Playlist* to_playlist = rv->region()->playlist();
2796 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2798 insert_result = affected_playlists.insert (to_playlist);
2799 if (insert_result.second) {
2800 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
2803 latest_regionview = 0;
2805 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2807 /* create a new region with the same name. */
2809 // FIXME: ew. need a (virtual) Region::duplicate() or something?
2811 boost::shared_ptr<Region> newregion;
2812 boost::shared_ptr<Region> ar;
2814 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
2815 newregion = RegionFactory::create (ar);
2817 assert(newregion != 0);
2819 /* if the original region was locked, we don't care */
2821 newregion->set_locked (false);
2823 to_playlist->add_region (newregion, (nframes_t) (rv->region()->position() * atv->get_diskstream()->speed()));
2827 if (latest_regionview) {
2828 new_regionviews.push_back (latest_regionview);
2832 if (new_regionviews.empty()) {
2836 /* reset selection to new regionviews */
2838 selection->set (new_regionviews);
2840 /* reset drag_info data to reflect the fact that we are dragging the copies */
2842 drag_info.data = new_regionviews.front();
2843 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2846 /* Which trackview is this ? */
2848 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2849 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2851 /* The region motion is only processed if the pointer is over
2855 if (!tv || !tv->is_audio_track()) {
2856 /* To make sure we hide the verbose canvas cursor when the mouse is
2857 not held over and audiotrack.
2859 hide_verbose_canvas_cursor ();
2863 original_pointer_order = drag_info.last_trackview->order;
2865 /************************************************************
2867 ************************************************************/
2869 if (drag_info.brushing) {
2870 clamp_y_axis = true;
2875 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2877 int32_t children = 0, numtracks = 0;
2878 // XXX hard coding track limit, oh my, so very very bad
2879 bitset <1024> tracks (0x00);
2880 /* get a bitmask representing the visible tracks */
2882 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2883 TimeAxisView *tracklist_timeview;
2884 tracklist_timeview = (*i);
2885 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2886 list<TimeAxisView*> children_list;
2888 /* zeroes are audio tracks. ones are other types. */
2890 if (!atv2->hidden()) {
2892 if (visible_y_high < atv2->order) {
2893 visible_y_high = atv2->order;
2895 if (visible_y_low > atv2->order) {
2896 visible_y_low = atv2->order;
2899 if (!atv2->is_audio_track()) {
2900 tracks = tracks |= (0x01 << atv2->order);
2903 height_list[atv2->order] = (*i)->height;
2905 if ((children_list = atv2->get_child_list()).size() > 0) {
2906 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2907 tracks = tracks |= (0x01 << (atv2->order + children));
2908 height_list[atv2->order + children] = (*j)->height;
2916 /* find the actual span according to the canvas */
2918 canvas_pointer_y_span = pointer_y_span;
2919 if (drag_info.last_trackview->order >= tv->order) {
2921 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2922 if (height_list[y] == 0 ) {
2923 canvas_pointer_y_span--;
2928 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2929 if ( height_list[y] == 0 ) {
2930 canvas_pointer_y_span++;
2935 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2936 RegionView* rv2 = (*i);
2937 double ix1, ix2, iy1, iy2;
2940 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2941 rv2->get_canvas_group()->i2w (ix1, iy1);
2942 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2943 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2945 if (atv2->order != original_pointer_order) {
2946 /* this isn't the pointer track */
2948 if (canvas_pointer_y_span > 0) {
2950 /* moving up the canvas */
2951 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2953 int32_t visible_tracks = 0;
2954 while (visible_tracks < canvas_pointer_y_span ) {
2957 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2958 /* we're passing through a hidden track */
2963 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2964 clamp_y_axis = true;
2968 clamp_y_axis = true;
2971 } else if (canvas_pointer_y_span < 0) {
2973 /*moving down the canvas*/
2975 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2978 int32_t visible_tracks = 0;
2980 while (visible_tracks > canvas_pointer_y_span ) {
2983 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2987 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2988 clamp_y_axis = true;
2993 clamp_y_axis = true;
2999 /* this is the pointer's track */
3000 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3001 clamp_y_axis = true;
3002 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3003 clamp_y_axis = true;
3011 } else if (drag_info.last_trackview == tv) {
3012 clamp_y_axis = true;
3016 if (!clamp_y_axis) {
3017 drag_info.last_trackview = tv;
3020 /************************************************************
3022 ************************************************************/
3024 /* compute the amount of pointer motion in frames, and where
3025 the region would be if we moved it by that much.
3028 if (drag_info.move_threshold_passed) {
3030 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3032 nframes_t sync_frame;
3033 nframes_t sync_offset;
3036 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3038 sync_offset = rv->region()->sync_offset (sync_dir);
3039 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3041 /* we snap if the snap modifier is not enabled.
3044 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3045 snap_to (sync_frame);
3048 if (sync_frame - sync_offset <= sync_frame) {
3049 pending_region_position = sync_frame - (sync_dir*sync_offset);
3051 pending_region_position = 0;
3055 pending_region_position = 0;
3058 if (pending_region_position > max_frames - rv->region()->length()) {
3059 pending_region_position = drag_info.last_frame_position;
3062 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3064 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3066 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3067 to make it appear at the new location.
3070 if (pending_region_position > drag_info.last_frame_position) {
3071 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3073 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3076 drag_info.last_frame_position = pending_region_position;
3083 /* threshold not passed */
3088 /*************************************************************
3090 ************************************************************/
3092 if (x_delta == 0 && (pointer_y_span == 0)) {
3093 /* haven't reached next snap point, and we're not switching
3094 trackviews. nothing to do.
3100 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3102 RegionView* rv2 = (*i);
3104 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3106 double ix1, ix2, iy1, iy2;
3107 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3108 rv2->get_canvas_group()->i2w (ix1, iy1);
3117 /*************************************************************
3119 ************************************************************/
3121 pair<set<Playlist*>::iterator,bool> insert_result;
3122 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3124 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3126 RegionView* rv = (*i);
3127 double ix1, ix2, iy1, iy2;
3128 int32_t temp_pointer_y_span = pointer_y_span;
3130 /* get item BBox, which will be relative to parent. so we have
3131 to query on a child, then convert to world coordinates using
3135 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3136 rv->get_canvas_group()->i2w (ix1, iy1);
3137 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3138 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3139 AudioTimeAxisView* temp_atv;
3141 if ((pointer_y_span != 0) && !clamp_y_axis) {
3144 for (j = height_list.begin(); j!= height_list.end(); j++) {
3145 if (x == canvas_atv->order) {
3146 /* we found the track the region is on */
3147 if (x != original_pointer_order) {
3148 /*this isn't from the same track we're dragging from */
3149 temp_pointer_y_span = canvas_pointer_y_span;
3151 while (temp_pointer_y_span > 0) {
3152 /* we're moving up canvas-wise,
3153 so we need to find the next track height
3155 if (j != height_list.begin()) {
3158 if (x != original_pointer_order) {
3159 /* we're not from the dragged track, so ignore hidden tracks. */
3161 temp_pointer_y_span++;
3165 temp_pointer_y_span--;
3167 while (temp_pointer_y_span < 0) {
3169 if (x != original_pointer_order) {
3171 temp_pointer_y_span--;
3175 if (j != height_list.end()) {
3178 temp_pointer_y_span++;
3180 /* find out where we'll be when we move and set height accordingly */
3182 tvp2 = trackview_by_y_position (iy1 + y_delta);
3183 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3184 rv->set_height (temp_atv->height);
3186 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3187 personally, i think this can confuse things, but never mind.
3190 //const GdkColor& col (temp_atv->view->get_region_color());
3191 //rv->set_color (const_cast<GdkColor&>(col));
3198 /* prevent the regionview from being moved to before
3199 the zero position on the canvas.
3204 if (-x_delta > ix1) {
3207 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3208 x_delta = max_frames - rv->region()->last_frame();
3211 if (drag_info.first_move) {
3213 /* hide any dependent views */
3215 // rv->get_time_axis_view().hide_dependent_views (*rv);
3217 /* this is subtle. raising the regionview itself won't help,
3218 because raise_to_top() just puts the item on the top of
3219 its parent's stack. so, we need to put the trackview canvas_display group
3220 on the top, since its parent is the whole canvas.
3223 rv->get_canvas_group()->raise_to_top();
3224 rv->get_time_axis_view().canvas_display->raise_to_top();
3225 cursor_group->raise_to_top();
3227 /* freeze the playlists from notifying till
3231 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3232 if (atv && atv->is_audio_track()) {
3233 AudioPlaylist* pl = dynamic_cast<AudioPlaylist*>(atv->get_diskstream()->playlist());
3235 /* only freeze and capture state once */
3237 insert_result = motion_frozen_playlists.insert (pl);
3238 if (insert_result.second) {
3240 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
3246 if (drag_info.brushing) {
3247 mouse_brush_insert_region (rv, pending_region_position);
3249 rv->move (x_delta, y_delta);
3253 if (drag_info.first_move) {
3254 cursor_group->raise_to_top();
3257 drag_info.first_move = false;
3259 if (x_delta != 0 && !drag_info.brushing) {
3260 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3266 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3269 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3270 pair<set<Playlist*>::iterator,bool> insert_result;
3271 bool nocommit = true;
3273 RouteTimeAxisView* atv;
3274 bool regionview_y_movement;
3275 bool regionview_x_movement;
3277 /* first_move is set to false if the regionview has been moved in the
3281 if (drag_info.first_move) {
3288 /* The regionview has been moved at some stage during the grab so we need
3289 to account for any mouse movement between this event and the last one.
3292 region_drag_motion_callback (item, event);
3294 if (drag_info.brushing) {
3295 /* all changes were made during motion event handlers */
3299 /* adjust for track speed */
3302 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3303 if (atv && atv->get_diskstream()) {
3304 speed = atv->get_diskstream()->speed();
3307 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3308 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3310 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3311 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3313 if (regionview_y_movement) {
3315 /* motion between tracks */
3317 list<RegionView*> new_selection;
3319 /* moved to a different audio track. */
3321 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3323 RegionView* rv2 = (*i);
3325 /* the region that used to be in the old playlist is not
3326 moved to the new one - we make a copy of it. as a result,
3327 any existing editor for the region should no longer be
3331 if (!drag_info.copy) {
3332 rv2->hide_region_editor();
3334 new_selection.push_back (rv2);
3338 /* first, freeze the target tracks */
3340 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3342 Playlist* from_playlist;
3343 Playlist* to_playlist;
3345 double ix1, ix2, iy1, iy2;
3347 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3348 (*i)->get_canvas_group()->i2w (ix1, iy1);
3349 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3350 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3352 from_playlist = (*i)->region()->playlist();
3353 to_playlist = atv2->playlist();
3355 /* the from_playlist was frozen in the "first_move" case
3356 of the motion handler. the insert can fail,
3357 but that doesn't matter. it just means
3358 we already have the playlist in the list.
3361 motion_frozen_playlists.insert (from_playlist);
3363 /* only freeze the to_playlist once */
3365 insert_result = motion_frozen_playlists.insert(to_playlist);
3366 if (insert_result.second) {
3367 to_playlist->freeze();
3368 session->add_command(new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3373 /* now do it again with the actual operations */
3375 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3377 Playlist* from_playlist;
3378 Playlist* to_playlist;
3380 double ix1, ix2, iy1, iy2;
3382 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3383 (*i)->get_canvas_group()->i2w (ix1, iy1);
3384 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3385 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3387 from_playlist = (*i)->region()->playlist();
3388 to_playlist = atv2->playlist();
3390 latest_regionview = 0;
3392 where = (nframes_t) (unit_to_frame (ix1) * speed);
3393 boost::shared_ptr<Region> new_region (RegionFactory::create ((*i)->region()));
3395 from_playlist->remove_region (((*i)->region()));
3397 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3398 to_playlist->add_region (new_region, where);
3401 if (latest_regionview) {
3402 selection->add (latest_regionview);
3408 /* motion within a single track */
3410 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3414 if (rv->region()->locked()) {
3418 if (regionview_x_movement) {
3419 double ownspeed = 1.0;
3420 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3422 if (atv && atv->get_diskstream()) {
3423 ownspeed = atv->get_diskstream()->speed();
3426 /* base the new region position on the current position of the regionview.*/
3428 double ix1, ix2, iy1, iy2;
3430 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3431 rv->get_canvas_group()->i2w (ix1, iy1);
3432 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3436 where = rv->region()->position();
3439 rv->get_time_axis_view().reveal_dependent_views (*rv);
3441 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3443 rv->region()->set_position (where, (void *) this);
3448 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3450 session->add_command (new MementoCommand<Playlist>(*(*p), 0, & (*p)->get_state()));
3453 motion_frozen_playlists.clear ();
3456 commit_reversible_command ();
3461 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3463 /* Either add to or set the set the region selection, unless
3464 this is an alignment click (control used)
3467 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3468 TimeAxisView* tv = &rv.get_time_axis_view();
3469 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3471 if (atv && atv->is_audio_track()) {
3472 speed = atv->get_diskstream()->speed();
3475 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3477 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3479 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3481 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3485 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3491 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3502 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3503 case AudioClock::BBT:
3504 session->bbt_time (frame, bbt);
3505 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3508 case AudioClock::SMPTE:
3509 session->smpte_time (frame, smpte);
3510 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3513 case AudioClock::MinSec:
3514 /* XXX fix this to compute min/sec properly */
3515 session->smpte_time (frame, smpte);
3516 secs = smpte.seconds + ((float) smpte.frames / Config->get_smpte_frames_per_second());
3517 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3521 snprintf (buf, sizeof(buf), "%u", frame);
3525 if (xpos >= 0 && ypos >=0) {
3526 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3529 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3531 show_verbose_canvas_cursor ();
3535 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3542 Meter meter_at_start(session->tempo_map().meter_at(start));
3548 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3549 case AudioClock::BBT:
3550 session->bbt_time (start, sbbt);
3551 session->bbt_time (end, ebbt);
3554 /* XXX this computation won't work well if the
3555 user makes a selection that spans any meter changes.
3558 ebbt.bars -= sbbt.bars;
3559 if (ebbt.beats >= sbbt.beats) {
3560 ebbt.beats -= sbbt.beats;
3563 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3565 if (ebbt.ticks >= sbbt.ticks) {
3566 ebbt.ticks -= sbbt.ticks;
3569 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3572 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3575 case AudioClock::SMPTE:
3576 session->smpte_duration (end - start, smpte);
3577 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3580 case AudioClock::MinSec:
3581 /* XXX fix this to compute min/sec properly */
3582 session->smpte_duration (end - start, smpte);
3583 secs = smpte.seconds + ((float) smpte.frames / Config->get_smpte_frames_per_second());
3584 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3588 snprintf (buf, sizeof(buf), "%u", end - start);
3592 if (xpos >= 0 && ypos >=0) {
3593 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3596 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3598 show_verbose_canvas_cursor ();
3602 Editor::collect_new_region_view (RegionView* rv)
3604 latest_regionview = rv;
3608 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3610 if (clicked_regionview == 0) {
3614 /* lets try to create new Region for the selection */
3616 vector<boost::shared_ptr<AudioRegion> > new_regions;
3617 create_region_from_selection (new_regions);
3619 if (new_regions.empty()) {
3623 /* XXX fix me one day to use all new regions */
3625 boost::shared_ptr<Region> region (new_regions.front());
3627 /* add it to the current stream/playlist.
3629 tricky: the streamview for the track will add a new regionview. we will
3630 catch the signal it sends when it creates the regionview to
3631 set the regionview we want to then drag.
3634 latest_regionview = 0;
3635 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3637 /* A selection grab currently creates two undo/redo operations, one for
3638 creating the new region and another for moving it.
3641 begin_reversible_command (_("selection grab"));
3643 Playlist* playlist = clicked_trackview->playlist();
3645 XMLNode *before = &(playlist->get_state());
3646 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3647 XMLNode *after = &(playlist->get_state());
3648 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3650 commit_reversible_command ();
3654 if (latest_regionview == 0) {
3655 /* something went wrong */
3659 /* we need to deselect all other regionviews, and select this one
3660 i'm ignoring undo stuff, because the region creation will take care of it */
3661 selection->set (latest_regionview);
3663 drag_info.item = latest_regionview->get_canvas_group();
3664 drag_info.data = latest_regionview;
3665 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3666 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3670 drag_info.last_trackview = clicked_trackview;
3671 drag_info.last_frame_position = latest_regionview->region()->position();
3672 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3674 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3678 Editor::cancel_selection ()
3680 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3681 (*i)->hide_selection ();
3683 begin_reversible_command (_("cancel selection"));
3684 selection->clear ();
3685 clicked_selection = 0;
3686 commit_reversible_command ();
3690 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3692 nframes_t start = 0;
3699 drag_info.item = item;
3700 drag_info.motion_callback = &Editor::drag_selection;
3701 drag_info.finished_callback = &Editor::end_selection_op;
3706 case CreateSelection:
3707 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3708 drag_info.copy = true;
3710 drag_info.copy = false;
3712 start_grab (event, selector_cursor);
3715 case SelectionStartTrim:
3716 if (clicked_trackview) {
3717 clicked_trackview->order_selection_trims (item, true);
3719 start_grab (event, trimmer_cursor);
3720 start = selection->time[clicked_selection].start;
3721 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3724 case SelectionEndTrim:
3725 if (clicked_trackview) {
3726 clicked_trackview->order_selection_trims (item, false);
3728 start_grab (event, trimmer_cursor);
3729 end = selection->time[clicked_selection].end;
3730 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3734 start = selection->time[clicked_selection].start;
3736 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3740 if (selection_op == SelectionMove) {
3741 show_verbose_time_cursor(start, 10);
3743 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3748 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3750 nframes_t start = 0;
3753 nframes_t pending_position;
3755 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3756 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3759 pending_position = 0;
3762 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3763 snap_to (pending_position);
3766 /* only alter selection if the current frame is
3767 different from the last frame position (adjusted)
3770 if (pending_position == drag_info.last_pointer_frame) return;
3772 switch (selection_op) {
3773 case CreateSelection:
3775 if (drag_info.first_move) {
3776 snap_to (drag_info.grab_frame);
3779 if (pending_position < drag_info.grab_frame) {
3780 start = pending_position;
3781 end = drag_info.grab_frame;
3783 end = pending_position;
3784 start = drag_info.grab_frame;
3787 /* first drag: Either add to the selection
3788 or create a new selection->
3791 if (drag_info.first_move) {
3793 begin_reversible_command (_("range selection"));
3795 if (drag_info.copy) {
3796 /* adding to the selection */
3797 clicked_selection = selection->add (start, end);
3798 drag_info.copy = false;
3800 /* new selection-> */
3801 clicked_selection = selection->set (clicked_trackview, start, end);
3806 case SelectionStartTrim:
3808 if (drag_info.first_move) {
3809 begin_reversible_command (_("trim selection start"));
3812 start = selection->time[clicked_selection].start;
3813 end = selection->time[clicked_selection].end;
3815 if (pending_position > end) {
3818 start = pending_position;
3822 case SelectionEndTrim:
3824 if (drag_info.first_move) {
3825 begin_reversible_command (_("trim selection end"));
3828 start = selection->time[clicked_selection].start;
3829 end = selection->time[clicked_selection].end;
3831 if (pending_position < start) {
3834 end = pending_position;
3841 if (drag_info.first_move) {
3842 begin_reversible_command (_("move selection"));
3845 start = selection->time[clicked_selection].start;
3846 end = selection->time[clicked_selection].end;
3848 length = end - start;
3850 start = pending_position;
3853 end = start + length;
3858 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3859 start_canvas_autoscroll (1);
3863 selection->replace (clicked_selection, start, end);
3866 drag_info.last_pointer_frame = pending_position;
3867 drag_info.first_move = false;
3869 if (selection_op == SelectionMove) {
3870 show_verbose_time_cursor(start, 10);
3872 show_verbose_time_cursor(pending_position, 10);
3877 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3879 if (!drag_info.first_move) {
3880 drag_selection (item, event);
3881 /* XXX this is not object-oriented programming at all. ick */
3882 if (selection->time.consolidate()) {
3883 selection->TimeChanged ();
3885 commit_reversible_command ();
3887 /* just a click, no pointer movement.*/
3889 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3891 selection->clear_time();
3896 /* XXX what happens if its a music selection? */
3897 session->set_audio_range (selection->time);
3898 stop_canvas_autoscroll ();
3902 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3905 TimeAxisView* tvp = clicked_trackview;
3906 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3908 if (tv && tv->is_audio_track()) {
3909 speed = tv->get_diskstream()->speed();
3912 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3913 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3914 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3916 motion_frozen_playlists.clear();
3918 //drag_info.item = clicked_regionview->get_name_highlight();
3919 drag_info.item = item;
3920 drag_info.motion_callback = &Editor::trim_motion_callback;
3921 drag_info.finished_callback = &Editor::trim_finished_callback;
3923 start_grab (event, trimmer_cursor);
3925 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3926 trim_op = ContentsTrim;
3928 /* These will get overridden for a point trim.*/
3929 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3930 /* closer to start */
3931 trim_op = StartTrim;
3932 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3940 show_verbose_time_cursor(region_start, 10);
3943 show_verbose_time_cursor(region_end, 10);
3946 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3952 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3954 RegionView* rv = clicked_regionview;
3955 nframes_t frame_delta = 0;
3956 bool left_direction;
3957 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3959 /* snap modifier works differently here..
3960 its' current state has to be passed to the
3961 various trim functions in order to work properly
3965 TimeAxisView* tvp = clicked_trackview;
3966 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3967 pair<set<Playlist*>::iterator,bool> insert_result;
3969 if (tv && tv->is_audio_track()) {
3970 speed = tv->get_diskstream()->speed();
3973 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3974 left_direction = true;
3976 left_direction = false;
3980 snap_to (drag_info.current_pointer_frame);
3983 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3987 if (drag_info.first_move) {
3993 trim_type = "Region start trim";
3996 trim_type = "Region end trim";
3999 trim_type = "Region content trim";
4003 begin_reversible_command (trim_type);
4005 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4006 (*i)->region()->freeze ();
4008 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4010 arv->temporarily_hide_envelope ();
4012 Playlist * pl = (*i)->region()->playlist();
4013 insert_result = motion_frozen_playlists.insert (pl);
4014 if (insert_result.second) {
4015 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4020 if (left_direction) {
4021 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4023 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4028 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4031 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4032 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4038 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4041 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4042 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4049 bool swap_direction = false;
4051 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4052 swap_direction = true;
4055 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4056 i != selection->regions.by_layer().end(); ++i)
4058 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4066 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4069 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4072 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4076 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4077 drag_info.first_move = false;
4081 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4083 boost::shared_ptr<Region> region (rv.region());
4085 if (region->locked()) {
4089 nframes_t new_bound;
4092 TimeAxisView* tvp = clicked_trackview;
4093 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4095 if (tv && tv->is_audio_track()) {
4096 speed = tv->get_diskstream()->speed();
4099 if (left_direction) {
4100 if (swap_direction) {
4101 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4103 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4106 if (swap_direction) {
4107 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4109 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4114 snap_to (new_bound);
4116 region->trim_start ((nframes_t) (new_bound * speed), this);
4117 rv.region_changed (StartChanged);
4121 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4123 boost::shared_ptr<Region> region (rv.region());
4125 if (region->locked()) {
4129 nframes_t new_bound;
4132 TimeAxisView* tvp = clicked_trackview;
4133 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4135 if (tv && tv->is_audio_track()) {
4136 speed = tv->get_diskstream()->speed();
4139 if (left_direction) {
4140 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4142 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4146 snap_to (new_bound, (left_direction ? 0 : 1));
4149 region->trim_front ((nframes_t) (new_bound * speed), this);
4151 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4155 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4157 boost::shared_ptr<Region> region (rv.region());
4159 if (region->locked()) {
4163 nframes_t new_bound;
4166 TimeAxisView* tvp = clicked_trackview;
4167 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4169 if (tv && tv->is_audio_track()) {
4170 speed = tv->get_diskstream()->speed();
4173 if (left_direction) {
4174 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4176 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4180 snap_to (new_bound);
4182 region->trim_end ((nframes_t) (new_bound * speed), this);
4183 rv.region_changed (LengthChanged);
4187 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4189 if (!drag_info.first_move) {
4190 trim_motion_callback (item, event);
4192 if (!clicked_regionview->get_selected()) {
4193 thaw_region_after_trim (*clicked_regionview);
4196 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4197 i != selection->regions.by_layer().end(); ++i)
4199 thaw_region_after_trim (**i);
4203 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4205 session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
4208 motion_frozen_playlists.clear ();
4210 commit_reversible_command();
4212 /* no mouse movement */
4218 Editor::point_trim (GdkEvent* event)
4220 RegionView* rv = clicked_regionview;
4221 nframes_t new_bound = drag_info.current_pointer_frame;
4223 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4224 snap_to (new_bound);
4227 /* Choose action dependant on which button was pressed */
4228 switch (event->button.button) {
4230 trim_op = StartTrim;
4231 begin_reversible_command (_("Start point trim"));
4233 if (rv->get_selected()) {
4235 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4236 i != selection->regions.by_layer().end(); ++i)
4238 if (!(*i)->region()->locked()) {
4239 Playlist *pl = (*i)->region()->playlist();
4240 XMLNode &before = pl->get_state();
4241 (*i)->region()->trim_front (new_bound, this);
4242 XMLNode &after = pl->get_state();
4243 session->add_command(new MementoCommand<Playlist>(*pl, &before, &after));
4249 if (!rv->region()->locked()) {
4250 Playlist *pl = rv->region()->playlist();
4251 XMLNode &before = pl->get_state();
4252 rv->region()->trim_front (new_bound, this);
4253 XMLNode &after = pl->get_state();
4254 session->add_command(new MementoCommand<Playlist>(*pl, &before, &after));
4258 commit_reversible_command();
4263 begin_reversible_command (_("End point trim"));
4265 if (rv->get_selected()) {
4267 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4269 if (!(*i)->region()->locked()) {
4270 Playlist *pl = (*i)->region()->playlist();
4271 XMLNode &before = pl->get_state();
4272 (*i)->region()->trim_end (new_bound, this);
4273 XMLNode &after = pl->get_state();
4274 session->add_command(new MementoCommand<Playlist>(*pl, &before, &after));
4280 if (!rv->region()->locked()) {
4281 Playlist *pl = rv->region()->playlist();
4282 XMLNode &before = pl->get_state();
4283 rv->region()->trim_end (new_bound, this);
4284 XMLNode &after = pl->get_state();
4285 session->add_command (new MementoCommand<Playlist>(*pl, &before, &after));
4289 commit_reversible_command();
4298 Editor::thaw_region_after_trim (RegionView& rv)
4300 boost::shared_ptr<Region> region (rv.region());
4302 if (region->locked()) {
4306 region->thaw (_("trimmed region"));
4307 XMLNode &after = region->playlist()->get_state();
4308 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4310 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4312 arv->unhide_envelope ();
4316 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4321 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4322 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4326 Location* location = find_location_from_marker (marker, is_start);
4327 location->set_hidden (true, this);
4332 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4338 drag_info.item = item;
4339 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4340 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4342 range_marker_op = op;
4344 if (!temp_location) {
4345 temp_location = new Location;
4349 case CreateRangeMarker:
4350 case CreateTransportMarker:
4352 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4353 drag_info.copy = true;
4355 drag_info.copy = false;
4357 start_grab (event, selector_cursor);
4361 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4366 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4368 nframes_t start = 0;
4370 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4372 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4373 snap_to (drag_info.current_pointer_frame);
4376 /* only alter selection if the current frame is
4377 different from the last frame position.
4380 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4382 switch (range_marker_op) {
4383 case CreateRangeMarker:
4384 case CreateTransportMarker:
4385 if (drag_info.first_move) {
4386 snap_to (drag_info.grab_frame);
4389 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4390 start = drag_info.current_pointer_frame;
4391 end = drag_info.grab_frame;
4393 end = drag_info.current_pointer_frame;
4394 start = drag_info.grab_frame;
4397 /* first drag: Either add to the selection
4398 or create a new selection.
4401 if (drag_info.first_move) {
4403 temp_location->set (start, end);
4407 update_marker_drag_item (temp_location);
4408 range_marker_drag_rect->show();
4409 range_marker_drag_rect->raise_to_top();
4415 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4416 start_canvas_autoscroll (1);
4420 temp_location->set (start, end);
4422 double x1 = frame_to_pixel (start);
4423 double x2 = frame_to_pixel (end);
4424 crect->property_x1() = x1;
4425 crect->property_x2() = x2;
4427 update_marker_drag_item (temp_location);
4430 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4431 drag_info.first_move = false;
4433 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4438 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4440 Location * newloc = 0;
4442 if (!drag_info.first_move) {
4443 drag_range_markerbar_op (item, event);
4445 switch (range_marker_op) {
4446 case CreateRangeMarker:
4448 begin_reversible_command (_("new range marker"));
4449 XMLNode &before = session->locations()->get_state();
4450 newloc = new Location(temp_location->start(), temp_location->end(), "unnamed", Location::IsRangeMarker);
4451 session->locations()->add (newloc, true);
4452 XMLNode &after = session->locations()->get_state();
4453 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4454 commit_reversible_command ();
4456 range_bar_drag_rect->hide();
4457 range_marker_drag_rect->hide();
4461 case CreateTransportMarker:
4462 // popup menu to pick loop or punch
4463 new_transport_marker_context_menu (&event->button, item);
4468 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4470 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4475 start = session->locations()->first_mark_before (drag_info.grab_frame);
4476 end = session->locations()->first_mark_after (drag_info.grab_frame);
4478 if (end == max_frames) {
4479 end = session->current_end_frame ();
4483 start = session->current_start_frame ();
4486 switch (mouse_mode) {
4488 /* find the two markers on either side and then make the selection from it */
4489 cerr << "select between " << start << " .. " << end << endl;
4490 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4494 /* find the two markers on either side of the click and make the range out of it */
4495 selection->set (0, start, end);
4504 stop_canvas_autoscroll ();
4510 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4512 drag_info.item = item;
4513 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4514 drag_info.finished_callback = &Editor::end_mouse_zoom;
4516 start_grab (event, zoom_cursor);
4518 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4522 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4527 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4528 snap_to (drag_info.current_pointer_frame);
4530 if (drag_info.first_move) {
4531 snap_to (drag_info.grab_frame);
4535 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4537 /* base start and end on initial click position */
4538 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4539 start = drag_info.current_pointer_frame;
4540 end = drag_info.grab_frame;
4542 end = drag_info.current_pointer_frame;
4543 start = drag_info.grab_frame;
4548 if (drag_info.first_move) {
4550 zoom_rect->raise_to_top();
4553 reposition_zoom_rect(start, end);
4555 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4556 drag_info.first_move = false;
4558 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4563 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4565 if (!drag_info.first_move) {
4566 drag_mouse_zoom (item, event);
4568 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4569 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4571 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4574 temporal_zoom_to_frame (false, drag_info.grab_frame);
4576 temporal_zoom_step (false);
4577 center_screen (drag_info.grab_frame);
4585 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4587 double x1 = frame_to_pixel (start);
4588 double x2 = frame_to_pixel (end);
4589 double y2 = canvas_height - 2;
4591 zoom_rect->property_x1() = x1;
4592 zoom_rect->property_y1() = 1.0;
4593 zoom_rect->property_x2() = x2;
4594 zoom_rect->property_y2() = y2;
4598 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4600 drag_info.item = item;
4601 drag_info.motion_callback = &Editor::drag_rubberband_select;
4602 drag_info.finished_callback = &Editor::end_rubberband_select;
4604 start_grab (event, cross_hair_cursor);
4606 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4610 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4617 /* use a bigger drag threshold than the default */
4619 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4623 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4624 // snap_to (drag_info.current_pointer_frame);
4626 // if (drag_info.first_move) {
4627 // snap_to (drag_info.grab_frame);
4632 /* base start and end on initial click position */
4633 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4634 start = drag_info.current_pointer_frame;
4635 end = drag_info.grab_frame;
4637 end = drag_info.current_pointer_frame;
4638 start = drag_info.grab_frame;
4641 if (drag_info.current_pointer_y < drag_info.grab_y) {
4642 y1 = drag_info.current_pointer_y;
4643 y2 = drag_info.grab_y;
4646 y2 = drag_info.current_pointer_y;
4647 y1 = drag_info.grab_y;
4651 if (start != end || y1 != y2) {
4653 double x1 = frame_to_pixel (start);
4654 double x2 = frame_to_pixel (end);
4656 rubberband_rect->property_x1() = x1;
4657 rubberband_rect->property_y1() = y1;
4658 rubberband_rect->property_x2() = x2;
4659 rubberband_rect->property_y2() = y2;
4661 rubberband_rect->show();
4662 rubberband_rect->raise_to_top();
4664 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4665 drag_info.first_move = false;
4667 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4672 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4674 if (!drag_info.first_move) {
4676 drag_rubberband_select (item, event);
4679 if (drag_info.current_pointer_y < drag_info.grab_y) {
4680 y1 = drag_info.current_pointer_y;
4681 y2 = drag_info.grab_y;
4684 y2 = drag_info.current_pointer_y;
4685 y1 = drag_info.grab_y;
4689 Selection::Operation op = Keyboard::selection_type (event->button.state);
4692 begin_reversible_command (_("select regions"));
4694 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4695 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4697 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4701 commit_reversible_command ();
4705 selection->clear_regions();
4706 selection->clear_points ();
4707 selection->clear_lines ();
4710 rubberband_rect->hide();
4715 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4717 using namespace Gtkmm2ext;
4719 ArdourPrompter prompter (false);
4721 prompter.set_prompt (_("Name for region:"));
4722 prompter.set_initial_text (clicked_regionview->region()->name());
4723 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4724 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4725 prompter.show_all ();
4726 switch (prompter.run ()) {
4727 case Gtk::RESPONSE_ACCEPT:
4729 prompter.get_result(str);
4731 clicked_regionview->region()->set_name (str);
4739 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4741 drag_info.item = item;
4742 drag_info.motion_callback = &Editor::time_fx_motion;
4743 drag_info.finished_callback = &Editor::end_time_fx;
4747 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4751 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4753 RegionView* rv = clicked_regionview;
4755 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4756 snap_to (drag_info.current_pointer_frame);
4759 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4763 if (drag_info.current_pointer_frame > rv->region()->position()) {
4764 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4767 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4768 drag_info.first_move = false;
4770 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4774 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4776 clicked_regionview->get_time_axis_view().hide_timestretch ();
4778 if (drag_info.first_move) {
4782 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4783 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4785 begin_reversible_command (_("timestretch"));
4787 if (run_timestretch (selection->regions, percentage) == 0) {
4788 session->commit_reversible_command ();
4793 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4795 /* no brushing without a useful snap setting */
4798 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4801 switch (snap_mode) {
4803 return; /* can't work because it allows region to be placed anywhere */
4808 switch (snap_type) {
4811 case SnapToEditCursor:
4818 /* don't brush a copy over the original */
4820 if (pos == rv->region()->position()) {
4824 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4826 if (atv == 0 || !atv->is_audio_track()) {
4830 Playlist* playlist = atv->playlist();
4831 double speed = atv->get_diskstream()->speed();
4833 XMLNode &before = playlist->get_state();
4834 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4835 XMLNode &after = playlist->get_state();
4836 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
4838 // playlist is frozen, so we have to update manually
4840 playlist->Modified(); /* EMIT SIGNAL */
4844 Editor::track_height_step_timeout ()
4847 struct timeval delta;
4849 gettimeofday (&now, 0);
4850 timersub (&now, &last_track_height_step_timestamp, &delta);
4852 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4853 current_stepping_trackview = 0;