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/midi_region.h>
57 #include <ardour/dB.h>
58 #include <ardour/utils.h>
59 #include <ardour/region_factory.h>
66 using namespace ARDOUR;
70 using namespace Editing;
73 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
87 switch (event->type) {
88 case GDK_BUTTON_RELEASE:
89 case GDK_BUTTON_PRESS:
90 case GDK_2BUTTON_PRESS:
91 case GDK_3BUTTON_PRESS:
92 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
94 case GDK_MOTION_NOTIFY:
95 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
97 case GDK_ENTER_NOTIFY:
98 case GDK_LEAVE_NOTIFY:
99 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
102 case GDK_KEY_RELEASE:
103 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
106 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
110 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
111 position is negative (as can be the case with motion events in particular),
112 the frame location is always positive.
115 return pixel_to_frame (*pcx);
119 Editor::mouse_mode_toggled (MouseMode m)
121 if (ignore_mouse_mode_toggle) {
127 if (mouse_select_button.get_active()) {
133 if (mouse_move_button.get_active()) {
139 if (mouse_gain_button.get_active()) {
145 if (mouse_zoom_button.get_active()) {
151 if (mouse_timefx_button.get_active()) {
157 if (mouse_audition_button.get_active()) {
168 Editor::set_mouse_mode (MouseMode m, bool force)
170 if (drag_info.item) {
174 if (!force && m == mouse_mode) {
182 if (mouse_mode != MouseRange) {
184 /* in all modes except range, hide the range selection,
185 show the object (region) selection.
188 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
189 (*i)->set_should_show_selection (true);
191 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
192 (*i)->hide_selection ();
198 in range mode,show the range selection.
201 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
202 if ((*i)->get_selected()) {
203 (*i)->show_selection (selection->time);
208 /* XXX the hack of unsetting all other buttongs should go
209 away once GTK2 allows us to use regular radio buttons drawn like
210 normal buttons, rather than my silly GroupedButton hack.
213 ignore_mouse_mode_toggle = true;
215 switch (mouse_mode) {
217 mouse_select_button.set_active (true);
218 current_canvas_cursor = selector_cursor;
222 mouse_move_button.set_active (true);
223 current_canvas_cursor = grabber_cursor;
227 mouse_gain_button.set_active (true);
228 current_canvas_cursor = cross_hair_cursor;
232 mouse_zoom_button.set_active (true);
233 current_canvas_cursor = zoom_cursor;
237 mouse_timefx_button.set_active (true);
238 current_canvas_cursor = time_fx_cursor; // just use playhead
242 mouse_audition_button.set_active (true);
243 current_canvas_cursor = speaker_cursor;
247 ignore_mouse_mode_toggle = false;
250 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
255 Editor::step_mouse_mode (bool next)
257 switch (current_mouse_mode()) {
259 if (next) set_mouse_mode (MouseRange);
260 else set_mouse_mode (MouseTimeFX);
264 if (next) set_mouse_mode (MouseZoom);
265 else set_mouse_mode (MouseObject);
269 if (next) set_mouse_mode (MouseGain);
270 else set_mouse_mode (MouseRange);
274 if (next) set_mouse_mode (MouseTimeFX);
275 else set_mouse_mode (MouseZoom);
279 if (next) set_mouse_mode (MouseAudition);
280 else set_mouse_mode (MouseGain);
284 if (next) set_mouse_mode (MouseObject);
285 else set_mouse_mode (MouseTimeFX);
291 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
297 /* in object/audition/timefx mode, any button press sets
298 the selection if the object can be selected. this is a
299 bit of hack, because we want to avoid this if the
300 mouse operation is a region alignment.
302 note: not dbl-click or triple-click
305 if (((mouse_mode != MouseObject) &&
306 (mouse_mode != MouseAudition || item_type != RegionItem) &&
307 (mouse_mode != MouseTimeFX || item_type != RegionItem)) ||
308 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
313 Selection::Operation op = Keyboard::selection_type (event->button.state);
314 bool press = (event->type == GDK_BUTTON_PRESS);
316 begin_reversible_command (_("select on click"));
320 c1 = set_selected_track_from_click (press, op, true, true);
321 c2 = set_selected_regionview_from_click (press, op, true);
325 case RegionViewNameHighlight:
327 c1 = set_selected_track_from_click (press, op, true, true);
328 c2 = set_selected_regionview_from_click (press, op, true);
332 case GainAutomationControlPointItem:
333 case PanAutomationControlPointItem:
334 case RedirectAutomationControlPointItem:
335 c1 = set_selected_track_from_click (press, op, true, true);
336 c2 = set_selected_control_point_from_click (press, op, false);
341 commit = set_selected_track_from_click (press, op, true, true);
344 case AutomationTrackItem:
345 commit = set_selected_track_from_click (press, op, true, true);
352 #define SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
353 #ifdef SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
354 /* in range mode, button 1/2/3 press potentially selects a track */
356 if (mouse_mode == MouseRange &&
357 event->type == GDK_BUTTON_PRESS &&
358 event->button.button <= 3) {
363 case AutomationTrackItem:
364 commit = set_selected_track_from_click (press, op, true, true);
373 commit_reversible_command ();
378 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
380 nframes_t where = event_frame (event, 0, 0);
382 track_canvas.grab_focus();
384 if (session && session->actively_recording()) {
388 button_selection (item, event, item_type);
390 if (drag_info.item == 0 &&
391 (Keyboard::is_delete_event (&event->button) ||
392 Keyboard::is_context_menu_event (&event->button) ||
393 Keyboard::is_edit_event (&event->button))) {
395 /* handled by button release */
399 switch (event->button.button) {
402 if (event->type == GDK_BUTTON_PRESS) {
404 if (drag_info.item) {
405 drag_info.item->ungrab (event->button.time);
408 /* single mouse clicks on any of these item types operate
409 independent of mouse mode, mostly because they are
410 not on the main track canvas or because we want
416 case PlayheadCursorItem:
417 start_cursor_grab (item, event);
421 if (Keyboard::modifier_state_equals (event->button.state,
422 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
423 hide_marker (item, event);
425 start_marker_grab (item, event);
429 case TempoMarkerItem:
430 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
431 start_tempo_marker_copy_grab (item, event);
433 start_tempo_marker_grab (item, event);
437 case MeterMarkerItem:
438 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
439 start_meter_marker_copy_grab (item, event);
441 start_meter_marker_grab (item, event);
451 case RangeMarkerBarItem:
452 start_range_markerbar_op (item, event, CreateRangeMarker);
456 case TransportMarkerBarItem:
457 start_range_markerbar_op (item, event, CreateTransportMarker);
466 switch (mouse_mode) {
469 case StartSelectionTrimItem:
470 start_selection_op (item, event, SelectionStartTrim);
473 case EndSelectionTrimItem:
474 start_selection_op (item, event, SelectionEndTrim);
478 if (Keyboard::modifier_state_contains
479 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
480 // contains and not equals because I can't use alt as a modifier alone.
481 start_selection_grab (item, event);
482 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
483 /* grab selection for moving */
484 start_selection_op (item, event, SelectionMove);
487 /* this was debated, but decided the more common action was to
488 make a new selection */
489 start_selection_op (item, event, CreateSelection);
494 start_selection_op (item, event, CreateSelection);
500 if (Keyboard::modifier_state_contains (event->button.state,
501 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt))
502 && event->type == GDK_BUTTON_PRESS) {
504 start_rubberband_select (item, event);
506 } else if (event->type == GDK_BUTTON_PRESS) {
509 case FadeInHandleItem:
510 start_fade_in_grab (item, event);
513 case FadeOutHandleItem:
514 start_fade_out_grab (item, event);
518 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
519 start_region_copy_grab (item, event);
520 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
521 start_region_brush_grab (item, event);
523 start_region_grab (item, event);
527 case RegionViewNameHighlight:
528 start_trim (item, event);
533 /* rename happens on edit clicks */
534 start_trim (clicked_regionview->get_name_highlight(), event);
538 case GainAutomationControlPointItem:
539 case PanAutomationControlPointItem:
540 case RedirectAutomationControlPointItem:
541 start_control_point_grab (item, event);
545 case GainAutomationLineItem:
546 case PanAutomationLineItem:
547 case RedirectAutomationLineItem:
548 start_line_grab_from_line (item, event);
553 case AutomationTrackItem:
554 start_rubberband_select (item, event);
557 /* <CMT Additions> */
558 case ImageFrameHandleStartItem:
559 imageframe_start_handle_op(item, event) ;
562 case ImageFrameHandleEndItem:
563 imageframe_end_handle_op(item, event) ;
566 case MarkerViewHandleStartItem:
567 markerview_item_start_handle_op(item, event) ;
570 case MarkerViewHandleEndItem:
571 markerview_item_end_handle_op(item, event) ;
574 /* </CMT Additions> */
576 /* <CMT Additions> */
578 start_markerview_grab(item, event) ;
581 start_imageframe_grab(item, event) ;
583 /* </CMT Additions> */
599 // start_line_grab_from_regionview (item, event);
602 case GainControlPointItem:
603 start_control_point_grab (item, event);
607 start_line_grab_from_line (item, event);
610 case GainAutomationControlPointItem:
611 case PanAutomationControlPointItem:
612 case RedirectAutomationControlPointItem:
613 start_control_point_grab (item, event);
624 case GainAutomationControlPointItem:
625 case PanAutomationControlPointItem:
626 case RedirectAutomationControlPointItem:
627 start_control_point_grab (item, event);
630 case GainAutomationLineItem:
631 case PanAutomationLineItem:
632 case RedirectAutomationLineItem:
633 start_line_grab_from_line (item, event);
637 // XXX need automation mode to identify which
639 // start_line_grab_from_regionview (item, event);
649 if (event->type == GDK_BUTTON_PRESS) {
650 start_mouse_zoom (item, event);
657 if (item_type == RegionItem) {
658 start_time_fx (item, event);
663 /* handled in release */
672 switch (mouse_mode) {
674 if (event->type == GDK_BUTTON_PRESS) {
677 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
678 start_region_copy_grab (item, event);
680 start_region_grab (item, event);
684 case GainAutomationControlPointItem:
685 case PanAutomationControlPointItem:
686 case RedirectAutomationControlPointItem:
687 start_control_point_grab (item, event);
698 case RegionViewNameHighlight:
699 start_trim (item, event);
704 start_trim (clicked_regionview->get_name_highlight(), event);
715 if (event->type == GDK_BUTTON_PRESS) {
716 /* relax till release */
723 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
724 temporal_zoom_session();
726 temporal_zoom_to_frame (true, event_frame(event));
741 switch (mouse_mode) {
743 //temporal_zoom_to_frame (true, where);
744 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
745 temporal_zoom_to_frame (true, where);
748 temporal_zoom_step (true);
753 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
754 scroll_backward (0.6f);
757 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
758 scroll_tracks_up_line ();
760 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
761 if (clicked_axisview) {
762 if (!current_stepping_trackview) {
763 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
764 current_stepping_trackview = clicked_axisview;
766 gettimeofday (&last_track_height_step_timestamp, 0);
767 current_stepping_trackview->step_height (true);
770 else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
771 temporal_zoom_to_frame (true, where);
778 switch (mouse_mode) {
780 // temporal_zoom_to_frame (false, where);
781 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
782 temporal_zoom_to_frame (false, where);
785 temporal_zoom_step (false);
790 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
791 scroll_forward (0.6f);
794 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
795 scroll_tracks_down_line ();
797 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
798 if (clicked_axisview) {
799 if (!current_stepping_trackview) {
800 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
801 current_stepping_trackview = clicked_axisview;
803 gettimeofday (&last_track_height_step_timestamp, 0);
804 current_stepping_trackview->step_height (false);
806 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
807 temporal_zoom_to_frame (false, where);
822 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
824 nframes_t where = event_frame (event, 0, 0);
826 /* no action if we're recording */
828 if (session && session->actively_recording()) {
832 /* first, see if we're finishing a drag ... */
834 if (drag_info.item) {
835 if (end_grab (item, event)) {
836 /* grab dragged, so do nothing else */
841 button_selection (item, event, item_type);
843 /* edit events get handled here */
845 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
851 case TempoMarkerItem:
852 edit_tempo_marker (item);
855 case MeterMarkerItem:
856 edit_meter_marker (item);
860 if (clicked_regionview->name_active()) {
861 return mouse_rename_region (item, event);
871 /* context menu events get handled here */
873 if (Keyboard::is_context_menu_event (&event->button)) {
875 if (drag_info.item == 0) {
877 /* no matter which button pops up the context menu, tell the menu
878 widget to use button 1 to drive menu selection.
883 case FadeInHandleItem:
885 case FadeOutHandleItem:
886 popup_fade_context_menu (1, event->button.time, item, item_type);
890 popup_track_context_menu (1, event->button.time, item_type, false, where);
894 case RegionViewNameHighlight:
896 popup_track_context_menu (1, event->button.time, item_type, false, where);
900 popup_track_context_menu (1, event->button.time, item_type, true, where);
903 case AutomationTrackItem:
904 popup_track_context_menu (1, event->button.time, item_type, false, where);
908 case RangeMarkerBarItem:
909 case TransportMarkerBarItem:
912 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
916 marker_context_menu (&event->button, item);
919 case TempoMarkerItem:
920 tm_marker_context_menu (&event->button, item);
923 case MeterMarkerItem:
924 tm_marker_context_menu (&event->button, item);
927 case CrossfadeViewItem:
928 popup_track_context_menu (1, event->button.time, item_type, false, where);
931 /* <CMT Additions> */
933 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
935 case ImageFrameTimeAxisItem:
936 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
939 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
941 case MarkerTimeAxisItem:
942 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
944 /* <CMT Additions> */
955 /* delete events get handled here */
957 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
960 case TempoMarkerItem:
961 remove_tempo_marker (item);
964 case MeterMarkerItem:
965 remove_meter_marker (item);
969 remove_marker (*item, event);
973 if (mouse_mode == MouseObject) {
974 remove_clicked_region ();
978 case GainControlPointItem:
979 if (mouse_mode == MouseGain) {
980 remove_gain_control_point (item, event);
984 case GainAutomationControlPointItem:
985 case PanAutomationControlPointItem:
986 case RedirectAutomationControlPointItem:
987 remove_control_point (item, event);
996 switch (event->button.button) {
1000 /* see comments in button_press_handler */
1001 case EditCursorItem:
1002 case PlayheadCursorItem:
1005 case GainAutomationLineItem:
1006 case PanAutomationLineItem:
1007 case RedirectAutomationLineItem:
1008 case StartSelectionTrimItem:
1009 case EndSelectionTrimItem:
1013 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1014 snap_to (where, 0, true);
1016 mouse_add_new_marker (where);
1020 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1023 mouse_add_new_tempo_event (where);
1027 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1035 switch (mouse_mode) {
1037 switch (item_type) {
1038 case AutomationTrackItem:
1039 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->add_automation_event
1053 // Gain only makes sense for audio regions
1054 if ( ! dynamic_cast<AudioRegionView*>(clicked_regionview))
1057 switch (item_type) {
1059 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1063 case AutomationTrackItem:
1064 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1065 add_automation_event (item, event, where, event->button.y);
1074 switch (item_type) {
1076 audition_selected_region ();
1093 switch (mouse_mode) {
1096 switch (item_type) {
1098 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1100 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1103 // Button2 click is unused
1116 // x_style_paste (where, 1.0);
1136 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1142 switch (item_type) {
1143 case GainControlPointItem:
1144 if (mouse_mode == MouseGain) {
1145 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1146 cp->set_visible (true);
1150 at_y = cp->get_y ();
1151 cp->item->i2w (at_x, at_y);
1155 fraction = 1.0 - (cp->get_y() / cp->line.height());
1157 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1158 show_verbose_canvas_cursor ();
1160 if (is_drawable()) {
1161 track_canvas.get_window()->set_cursor (*fader_cursor);
1166 case GainAutomationControlPointItem:
1167 case PanAutomationControlPointItem:
1168 case RedirectAutomationControlPointItem:
1169 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1170 cp->set_visible (true);
1174 at_y = cp->get_y ();
1175 cp->item->i2w (at_x, at_y);
1179 fraction = 1.0 - (cp->get_y() / cp->line.height());
1181 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1182 show_verbose_canvas_cursor ();
1184 if (is_drawable()) {
1185 track_canvas.get_window()->set_cursor (*fader_cursor);
1190 if (mouse_mode == MouseGain) {
1191 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1193 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1194 if (is_drawable()) {
1195 track_canvas.get_window()->set_cursor (*fader_cursor);
1200 case GainAutomationLineItem:
1201 case RedirectAutomationLineItem:
1202 case PanAutomationLineItem:
1204 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1206 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1208 if (is_drawable()) {
1209 track_canvas.get_window()->set_cursor (*fader_cursor);
1213 case RegionViewNameHighlight:
1214 if (is_drawable() && mouse_mode == MouseObject) {
1215 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1219 case StartSelectionTrimItem:
1220 case EndSelectionTrimItem:
1221 /* <CMT Additions> */
1222 case ImageFrameHandleStartItem:
1223 case ImageFrameHandleEndItem:
1224 case MarkerViewHandleStartItem:
1225 case MarkerViewHandleEndItem:
1226 /* </CMT Additions> */
1228 if (is_drawable()) {
1229 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1233 case EditCursorItem:
1234 case PlayheadCursorItem:
1235 if (is_drawable()) {
1236 track_canvas.get_window()->set_cursor (*grabber_cursor);
1240 case RegionViewName:
1242 /* when the name is not an active item, the entire name highlight is for trimming */
1244 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1245 if (mouse_mode == MouseObject && is_drawable()) {
1246 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1252 case AutomationTrackItem:
1253 if (is_drawable()) {
1254 Gdk::Cursor *cursor;
1255 switch (mouse_mode) {
1257 cursor = selector_cursor;
1260 cursor = zoom_cursor;
1263 cursor = cross_hair_cursor;
1267 track_canvas.get_window()->set_cursor (*cursor);
1269 AutomationTimeAxisView* atv;
1270 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1271 clear_entered_track = false;
1272 set_entered_track (atv);
1278 case RangeMarkerBarItem:
1279 case TransportMarkerBarItem:
1282 if (is_drawable()) {
1283 time_canvas.get_window()->set_cursor (*timebar_cursor);
1288 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1291 marker->set_color_rgba (color_map[cEnteredMarker]);
1293 case MeterMarkerItem:
1294 case TempoMarkerItem:
1295 if (is_drawable()) {
1296 time_canvas.get_window()->set_cursor (*timebar_cursor);
1299 case FadeInHandleItem:
1300 case FadeOutHandleItem:
1301 if (mouse_mode == MouseObject) {
1302 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1304 rect->property_fill_color_rgba() = 0;
1305 rect->property_outline_pixels() = 1;
1314 /* second pass to handle entered track status in a comprehensible way.
1317 switch (item_type) {
1319 case GainAutomationLineItem:
1320 case RedirectAutomationLineItem:
1321 case PanAutomationLineItem:
1322 case GainControlPointItem:
1323 case GainAutomationControlPointItem:
1324 case PanAutomationControlPointItem:
1325 case RedirectAutomationControlPointItem:
1326 /* these do not affect the current entered track state */
1327 clear_entered_track = false;
1330 case AutomationTrackItem:
1331 /* handled above already */
1335 set_entered_track (0);
1343 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1352 switch (item_type) {
1353 case GainControlPointItem:
1354 case GainAutomationControlPointItem:
1355 case PanAutomationControlPointItem:
1356 case RedirectAutomationControlPointItem:
1357 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1358 if (cp->line.npoints() > 1) {
1359 if (!cp->selected) {
1360 cp->set_visible (false);
1364 if (is_drawable()) {
1365 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1368 hide_verbose_canvas_cursor ();
1371 case RegionViewNameHighlight:
1372 case StartSelectionTrimItem:
1373 case EndSelectionTrimItem:
1374 case EditCursorItem:
1375 case PlayheadCursorItem:
1376 /* <CMT Additions> */
1377 case ImageFrameHandleStartItem:
1378 case ImageFrameHandleEndItem:
1379 case MarkerViewHandleStartItem:
1380 case MarkerViewHandleEndItem:
1381 /* </CMT Additions> */
1382 if (is_drawable()) {
1383 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1388 case GainAutomationLineItem:
1389 case RedirectAutomationLineItem:
1390 case PanAutomationLineItem:
1391 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1393 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1395 line->property_fill_color_rgba() = al->get_line_color();
1397 if (is_drawable()) {
1398 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1402 case RegionViewName:
1403 /* see enter_handler() for notes */
1404 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1405 if (is_drawable() && mouse_mode == MouseObject) {
1406 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1411 case RangeMarkerBarItem:
1412 case TransportMarkerBarItem:
1416 if (is_drawable()) {
1417 time_canvas.get_window()->set_cursor (*timebar_cursor);
1422 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1425 loc = find_location_from_marker (marker, is_start);
1426 if (loc) location_flags_changed (loc, this);
1428 case MeterMarkerItem:
1429 case TempoMarkerItem:
1431 if (is_drawable()) {
1432 time_canvas.get_window()->set_cursor (*timebar_cursor);
1437 case FadeInHandleItem:
1438 case FadeOutHandleItem:
1439 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1441 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1443 rect->property_fill_color_rgba() = rv->get_fill_color();
1444 rect->property_outline_pixels() = 0;
1449 case AutomationTrackItem:
1450 if (is_drawable()) {
1451 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1452 clear_entered_track = true;
1453 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1465 Editor::left_automation_track ()
1467 if (clear_entered_track) {
1468 set_entered_track (0);
1469 clear_entered_track = false;
1475 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1479 /* We call this so that MOTION_NOTIFY events continue to be
1480 delivered to the canvas. We need to do this because we set
1481 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1482 the density of the events, at the expense of a round-trip
1483 to the server. Given that this will mostly occur on cases
1484 where DISPLAY = :0.0, and given the cost of what the motion
1485 event might do, its a good tradeoff.
1488 track_canvas.get_pointer (x, y);
1490 if (current_stepping_trackview) {
1491 /* don't keep the persistent stepped trackview if the mouse moves */
1492 current_stepping_trackview = 0;
1493 step_timeout.disconnect ();
1496 if (session && session->actively_recording()) {
1497 /* Sorry. no dragging stuff around while we record */
1501 drag_info.item_type = item_type;
1502 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1503 &drag_info.current_pointer_y);
1505 if (!from_autoscroll && drag_info.item) {
1506 /* item != 0 is the best test i can think of for dragging.
1508 if (!drag_info.move_threshold_passed) {
1510 drag_info.move_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1512 // and change the initial grab loc/frame if this drag info wants us to
1514 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1515 drag_info.grab_frame = drag_info.current_pointer_frame;
1516 drag_info.grab_x = drag_info.current_pointer_x;
1517 drag_info.grab_y = drag_info.current_pointer_y;
1518 drag_info.last_pointer_frame = drag_info.grab_frame;
1519 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1524 switch (item_type) {
1525 case PlayheadCursorItem:
1526 case EditCursorItem:
1528 case GainControlPointItem:
1529 case RedirectAutomationControlPointItem:
1530 case GainAutomationControlPointItem:
1531 case PanAutomationControlPointItem:
1532 case TempoMarkerItem:
1533 case MeterMarkerItem:
1534 case RegionViewNameHighlight:
1535 case StartSelectionTrimItem:
1536 case EndSelectionTrimItem:
1539 case RedirectAutomationLineItem:
1540 case GainAutomationLineItem:
1541 case PanAutomationLineItem:
1542 case FadeInHandleItem:
1543 case FadeOutHandleItem:
1544 /* <CMT Additions> */
1545 case ImageFrameHandleStartItem:
1546 case ImageFrameHandleEndItem:
1547 case MarkerViewHandleStartItem:
1548 case MarkerViewHandleEndItem:
1549 /* </CMT Additions> */
1550 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1551 (event->motion.state & Gdk::BUTTON2_MASK))) {
1552 if (!from_autoscroll) {
1553 maybe_autoscroll (event);
1555 (this->*(drag_info.motion_callback)) (item, event);
1564 switch (mouse_mode) {
1569 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1570 (event->motion.state & GDK_BUTTON2_MASK))) {
1571 if (!from_autoscroll) {
1572 maybe_autoscroll (event);
1574 (this->*(drag_info.motion_callback)) (item, event);
1585 track_canvas_motion (event);
1586 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1594 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1596 if (drag_info.item == 0) {
1597 fatal << _("programming error: start_grab called without drag item") << endmsg;
1603 cursor = grabber_cursor;
1606 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1608 if (event->button.button == 2) {
1609 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1610 drag_info.y_constrained = true;
1611 drag_info.x_constrained = false;
1613 drag_info.y_constrained = false;
1614 drag_info.x_constrained = true;
1617 drag_info.x_constrained = false;
1618 drag_info.y_constrained = false;
1621 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1622 drag_info.last_pointer_frame = drag_info.grab_frame;
1623 drag_info.current_pointer_frame = drag_info.grab_frame;
1624 drag_info.current_pointer_x = drag_info.grab_x;
1625 drag_info.current_pointer_y = drag_info.grab_y;
1626 drag_info.cumulative_x_drag = 0;
1627 drag_info.cumulative_y_drag = 0;
1628 drag_info.first_move = true;
1629 drag_info.move_threshold_passed = false;
1630 drag_info.want_move_threshold = false;
1631 drag_info.pointer_frame_offset = 0;
1632 drag_info.brushing = false;
1633 drag_info.copied_location = 0;
1635 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1637 event->button.time);
1639 if (session && session->transport_rolling()) {
1640 drag_info.was_rolling = true;
1642 drag_info.was_rolling = false;
1645 switch (snap_type) {
1646 case SnapToRegionStart:
1647 case SnapToRegionEnd:
1648 case SnapToRegionSync:
1649 case SnapToRegionBoundary:
1650 build_region_boundary_cache ();
1658 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1660 drag_info.item->ungrab (0);
1661 drag_info.item = new_item;
1664 cursor = grabber_cursor;
1667 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1671 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1673 bool did_drag = false;
1675 stop_canvas_autoscroll ();
1677 if (drag_info.item == 0) {
1681 drag_info.item->ungrab (event->button.time);
1683 if (drag_info.finished_callback) {
1684 (this->*(drag_info.finished_callback)) (item, event);
1687 did_drag = !drag_info.first_move;
1689 hide_verbose_canvas_cursor();
1692 drag_info.copy = false;
1693 drag_info.motion_callback = 0;
1694 drag_info.finished_callback = 0;
1695 drag_info.last_trackview = 0;
1696 drag_info.last_frame_position = 0;
1697 drag_info.grab_frame = 0;
1698 drag_info.last_pointer_frame = 0;
1699 drag_info.current_pointer_frame = 0;
1700 drag_info.brushing = false;
1702 if (drag_info.copied_location) {
1703 delete drag_info.copied_location;
1704 drag_info.copied_location = 0;
1711 Editor::set_edit_cursor (GdkEvent* event)
1713 nframes_t pointer_frame = event_frame (event);
1715 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1716 if (snap_type != SnapToEditCursor) {
1717 snap_to (pointer_frame);
1721 edit_cursor->set_position (pointer_frame);
1722 edit_cursor_clock.set (pointer_frame);
1726 Editor::set_playhead_cursor (GdkEvent* event)
1728 nframes_t pointer_frame = event_frame (event);
1730 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1731 snap_to (pointer_frame);
1735 session->request_locate (pointer_frame, session->transport_rolling());
1740 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1742 drag_info.item = item;
1743 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1744 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1748 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1749 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1753 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1755 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1759 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1761 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1763 nframes_t fade_length;
1765 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1766 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1772 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1776 if (pos < (arv->region()->position() + 64)) {
1777 fade_length = 64; // this should be a minimum defined somewhere
1778 } else if (pos > arv->region()->last_frame()) {
1779 fade_length = arv->region()->length();
1781 fade_length = pos - arv->region()->position();
1784 arv->reset_fade_in_shape_width (fade_length);
1786 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1788 drag_info.first_move = false;
1792 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1794 if (drag_info.first_move) return;
1796 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1798 nframes_t fade_length;
1800 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1801 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1807 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1811 if (pos < (arv->region()->position() + 64)) {
1812 fade_length = 64; // this should be a minimum defined somewhere
1814 else if (pos > arv->region()->last_frame()) {
1815 fade_length = arv->region()->length();
1818 fade_length = pos - arv->region()->position();
1821 begin_reversible_command (_("change fade in length"));
1822 XMLNode &before = arv->audio_region()->get_state();
1824 arv->audio_region()->set_fade_in_length (fade_length);
1826 XMLNode &after = arv->audio_region()->get_state();
1827 session->add_command(new MementoCommand<ARDOUR::AudioRegion>(*arv->audio_region().get(), &before, &after));
1828 commit_reversible_command ();
1829 fade_in_drag_motion_callback (item, event);
1833 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1835 drag_info.item = item;
1836 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1837 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1841 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1842 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1846 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1848 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1852 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1854 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1856 nframes_t fade_length;
1858 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1859 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1865 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1869 if (pos > (arv->region()->last_frame() - 64)) {
1870 fade_length = 64; // this should really be a minimum fade defined somewhere
1872 else if (pos < arv->region()->position()) {
1873 fade_length = arv->region()->length();
1876 fade_length = arv->region()->last_frame() - pos;
1879 arv->reset_fade_out_shape_width (fade_length);
1881 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1883 drag_info.first_move = false;
1887 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1889 if (drag_info.first_move) return;
1891 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1893 nframes_t fade_length;
1895 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1896 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1902 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1906 if (pos > (arv->region()->last_frame() - 64)) {
1907 fade_length = 64; // this should really be a minimum fade defined somewhere
1909 else if (pos < arv->region()->position()) {
1910 fade_length = arv->region()->length();
1913 fade_length = arv->region()->last_frame() - pos;
1916 begin_reversible_command (_("change fade out length"));
1917 XMLNode &before = arv->region()->get_state();
1919 arv->audio_region()->set_fade_out_length (fade_length);
1921 XMLNode &after = arv->region()->get_state();
1922 session->add_command(new MementoCommand<ARDOUR::Region>(*arv->region().get(), &before, &after));
1923 commit_reversible_command ();
1925 fade_out_drag_motion_callback (item, event);
1929 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1931 drag_info.item = item;
1932 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1933 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1937 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1938 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1942 Cursor* cursor = (Cursor *) drag_info.data;
1944 if (session && cursor == playhead_cursor) {
1945 if (drag_info.was_rolling) {
1946 session->request_stop ();
1950 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1952 show_verbose_time_cursor (cursor->current_frame, 10);
1956 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1958 Cursor* cursor = (Cursor *) drag_info.data;
1959 nframes_t adjusted_frame;
1961 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1962 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1968 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1969 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1970 snap_to (adjusted_frame);
1974 if (adjusted_frame == drag_info.last_pointer_frame) return;
1976 cursor->set_position (adjusted_frame);
1978 if (cursor == edit_cursor) {
1979 edit_cursor_clock.set (cursor->current_frame);
1982 show_verbose_time_cursor (cursor->current_frame, 10);
1984 drag_info.last_pointer_frame = adjusted_frame;
1985 drag_info.first_move = false;
1989 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1991 if (drag_info.first_move) return;
1993 cursor_drag_motion_callback (item, event);
1995 if (item == &playhead_cursor->canvas_item) {
1997 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1999 } else if (item == &edit_cursor->canvas_item) {
2000 edit_cursor->set_position (edit_cursor->current_frame);
2001 edit_cursor_clock.set (edit_cursor->current_frame);
2006 Editor::update_marker_drag_item (Location *location)
2008 double x1 = frame_to_pixel (location->start());
2009 double x2 = frame_to_pixel (location->end());
2011 if (location->is_mark()) {
2012 marker_drag_line_points.front().set_x(x1);
2013 marker_drag_line_points.back().set_x(x1);
2014 marker_drag_line->property_points() = marker_drag_line_points;
2017 range_marker_drag_rect->property_x1() = x1;
2018 range_marker_drag_rect->property_x2() = x2;
2023 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2027 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2028 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2034 Location *location = find_location_from_marker (marker, is_start);
2036 drag_info.item = item;
2037 drag_info.data = marker;
2038 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2039 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2043 drag_info.copied_location = new Location (*location);
2044 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2046 update_marker_drag_item (location);
2048 if (location->is_mark()) {
2049 marker_drag_line->show();
2050 marker_drag_line->raise_to_top();
2053 range_marker_drag_rect->show();
2054 range_marker_drag_rect->raise_to_top();
2057 if (is_start) show_verbose_time_cursor (location->start(), 10);
2058 else show_verbose_time_cursor (location->end(), 10);
2062 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2065 Marker* marker = (Marker *) drag_info.data;
2066 Location *real_location;
2067 Location *copy_location;
2069 bool move_both = false;
2073 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2074 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2080 nframes_t next = newframe;
2082 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2083 snap_to (newframe, 0, true);
2086 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2090 /* call this to find out if its the start or end */
2092 real_location = find_location_from_marker (marker, is_start);
2094 /* use the copy that we're "dragging" around */
2096 copy_location = drag_info.copied_location;
2098 f_delta = copy_location->end() - copy_location->start();
2100 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2104 if (copy_location->is_mark()) {
2107 copy_location->set_start (newframe);
2111 if (is_start) { // start-of-range marker
2114 copy_location->set_start (newframe);
2115 copy_location->set_end (newframe + f_delta);
2116 } else if (newframe < copy_location->end()) {
2117 copy_location->set_start (newframe);
2119 snap_to (next, 1, true);
2120 copy_location->set_end (next);
2121 copy_location->set_start (newframe);
2124 } else { // end marker
2127 copy_location->set_end (newframe);
2128 copy_location->set_start (newframe - f_delta);
2129 } else if (newframe > copy_location->start()) {
2130 copy_location->set_end (newframe);
2132 } else if (newframe > 0) {
2133 snap_to (next, -1, true);
2134 copy_location->set_start (next);
2135 copy_location->set_end (newframe);
2140 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2141 drag_info.first_move = false;
2143 update_marker_drag_item (copy_location);
2145 LocationMarkers* lm = find_location_markers (real_location);
2146 lm->set_position (copy_location->start(), copy_location->end());
2148 show_verbose_time_cursor (newframe, 10);
2152 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2154 if (drag_info.first_move) {
2155 marker_drag_motion_callback (item, event);
2159 Marker* marker = (Marker *) drag_info.data;
2163 begin_reversible_command ( _("move marker") );
2164 XMLNode &before = session->locations()->get_state();
2166 Location * location = find_location_from_marker (marker, is_start);
2169 if (location->is_mark()) {
2170 location->set_start (drag_info.copied_location->start());
2172 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2176 XMLNode &after = session->locations()->get_state();
2177 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2178 commit_reversible_command ();
2180 marker_drag_line->hide();
2181 range_marker_drag_rect->hide();
2185 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2188 MeterMarker* meter_marker;
2190 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2191 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2195 meter_marker = dynamic_cast<MeterMarker*> (marker);
2197 MetricSection& section (meter_marker->meter());
2199 if (!section.movable()) {
2203 drag_info.item = item;
2204 drag_info.data = marker;
2205 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2206 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2210 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2212 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2216 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2219 MeterMarker* meter_marker;
2221 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2222 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2226 meter_marker = dynamic_cast<MeterMarker*> (marker);
2228 // create a dummy marker for visual representation of moving the copy.
2229 // The actual copying is not done before we reach the finish callback.
2231 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2232 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2233 *new MeterSection(meter_marker->meter()));
2235 drag_info.item = &new_marker->the_item();
2236 drag_info.copy = true;
2237 drag_info.data = new_marker;
2238 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2239 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2243 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2245 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2249 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2251 MeterMarker* marker = (MeterMarker *) drag_info.data;
2252 nframes_t adjusted_frame;
2254 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2255 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2261 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2262 snap_to (adjusted_frame);
2265 if (adjusted_frame == drag_info.last_pointer_frame) return;
2267 marker->set_position (adjusted_frame);
2270 drag_info.last_pointer_frame = adjusted_frame;
2271 drag_info.first_move = false;
2273 show_verbose_time_cursor (adjusted_frame, 10);
2277 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2279 if (drag_info.first_move) return;
2281 meter_marker_drag_motion_callback (drag_info.item, event);
2283 MeterMarker* marker = (MeterMarker *) drag_info.data;
2286 TempoMap& map (session->tempo_map());
2287 map.bbt_time (drag_info.last_pointer_frame, when);
2289 if (drag_info.copy == true) {
2290 begin_reversible_command (_("copy meter mark"));
2291 XMLNode &before = map.get_state();
2292 map.add_meter (marker->meter(), when);
2293 XMLNode &after = map.get_state();
2294 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2295 commit_reversible_command ();
2297 // delete the dummy marker we used for visual representation of copying.
2298 // a new visual marker will show up automatically.
2301 begin_reversible_command (_("move meter mark"));
2302 XMLNode &before = map.get_state();
2303 map.move_meter (marker->meter(), when);
2304 XMLNode &after = map.get_state();
2305 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2306 commit_reversible_command ();
2311 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2314 TempoMarker* tempo_marker;
2316 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2317 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2321 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2322 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2326 MetricSection& section (tempo_marker->tempo());
2328 if (!section.movable()) {
2332 drag_info.item = item;
2333 drag_info.data = marker;
2334 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2335 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2339 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2340 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2344 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2347 TempoMarker* tempo_marker;
2349 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2350 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2354 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2355 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2359 // create a dummy marker for visual representation of moving the copy.
2360 // The actual copying is not done before we reach the finish callback.
2362 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2363 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2364 *new TempoSection(tempo_marker->tempo()));
2366 drag_info.item = &new_marker->the_item();
2367 drag_info.copy = true;
2368 drag_info.data = new_marker;
2369 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2370 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2374 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2376 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2380 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2382 TempoMarker* marker = (TempoMarker *) drag_info.data;
2383 nframes_t adjusted_frame;
2385 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2386 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2392 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2393 snap_to (adjusted_frame);
2396 if (adjusted_frame == drag_info.last_pointer_frame) return;
2398 /* OK, we've moved far enough to make it worth actually move the thing. */
2400 marker->set_position (adjusted_frame);
2402 show_verbose_time_cursor (adjusted_frame, 10);
2404 drag_info.last_pointer_frame = adjusted_frame;
2405 drag_info.first_move = false;
2409 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2411 if (drag_info.first_move) return;
2413 tempo_marker_drag_motion_callback (drag_info.item, event);
2415 TempoMarker* marker = (TempoMarker *) drag_info.data;
2418 TempoMap& map (session->tempo_map());
2419 map.bbt_time (drag_info.last_pointer_frame, when);
2421 if (drag_info.copy == true) {
2422 begin_reversible_command (_("copy tempo mark"));
2423 XMLNode &before = map.get_state();
2424 map.add_tempo (marker->tempo(), when);
2425 XMLNode &after = map.get_state();
2426 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2427 commit_reversible_command ();
2429 // delete the dummy marker we used for visual representation of copying.
2430 // a new visual marker will show up automatically.
2433 begin_reversible_command (_("move tempo mark"));
2434 XMLNode &before = map.get_state();
2435 map.move_tempo (marker->tempo(), when);
2436 XMLNode &after = map.get_state();
2437 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2438 commit_reversible_command ();
2443 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2445 ControlPoint* control_point;
2447 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2448 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2452 // We shouldn't remove the first or last gain point
2453 if (control_point->line.is_last_point(*control_point) ||
2454 control_point->line.is_first_point(*control_point)) {
2458 control_point->line.remove_point (*control_point);
2462 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2464 ControlPoint* control_point;
2466 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2467 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2471 control_point->line.remove_point (*control_point);
2475 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2477 ControlPoint* control_point;
2479 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2480 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2484 drag_info.item = item;
2485 drag_info.data = control_point;
2486 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2487 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2489 start_grab (event, fader_cursor);
2491 control_point->line.start_drag (control_point, 0);
2493 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2494 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2495 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2497 show_verbose_canvas_cursor ();
2501 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2503 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2505 double cx = drag_info.current_pointer_x;
2506 double cy = drag_info.current_pointer_y;
2508 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2509 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2511 if (drag_info.x_constrained) {
2512 cx = drag_info.grab_x;
2514 if (drag_info.y_constrained) {
2515 cy = drag_info.grab_y;
2518 cp->line.parent_group().w2i (cx, cy);
2522 cy = min ((double) cp->line.height(), cy);
2524 //translate cx to frames
2525 nframes_t cx_frames = unit_to_frame (cx);
2527 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2528 snap_to (cx_frames);
2531 float fraction = 1.0 - (cy / cp->line.height());
2535 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2541 cp->line.point_drag (*cp, cx_frames , fraction, push);
2543 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2545 drag_info.first_move = false;
2549 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2551 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2553 if (drag_info.first_move) {
2557 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2558 reset_point_selection ();
2562 control_point_drag_motion_callback (item, event);
2564 cp->line.end_drag (cp);
2568 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2570 switch (mouse_mode) {
2572 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2573 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2581 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2585 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2586 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2590 start_line_grab (al, event);
2594 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2598 nframes_t frame_within_region;
2600 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2604 cx = event->button.x;
2605 cy = event->button.y;
2606 line->parent_group().w2i (cx, cy);
2607 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2609 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2610 current_line_drag_info.after)) {
2611 /* no adjacent points */
2615 drag_info.item = &line->grab_item();
2616 drag_info.data = line;
2617 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2618 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2620 start_grab (event, fader_cursor);
2622 double fraction = 1.0 - (cy / line->height());
2624 line->start_drag (0, fraction);
2626 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2627 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2628 show_verbose_canvas_cursor ();
2632 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2634 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2635 double cx = drag_info.current_pointer_x;
2636 double cy = drag_info.current_pointer_y;
2638 line->parent_group().w2i (cx, cy);
2641 fraction = 1.0 - (cy / line->height());
2645 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2651 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2653 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2657 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2659 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2660 line_drag_motion_callback (item, event);
2665 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2667 if (selection->regions.empty() || clicked_regionview == 0) {
2671 drag_info.copy = false;
2672 drag_info.item = item;
2673 drag_info.data = clicked_regionview;
2674 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2675 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2680 TimeAxisView* tvp = clicked_axisview;
2681 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2683 if (tv && tv->is_track()) {
2684 speed = tv->get_diskstream()->speed();
2687 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2688 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2689 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2690 // we want a move threshold
2691 drag_info.want_move_threshold = true;
2693 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2695 begin_reversible_command (_("move region(s)"));
2699 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2701 if (selection->regions.empty() || clicked_regionview == 0) {
2705 drag_info.copy = true;
2706 drag_info.item = item;
2707 drag_info.data = clicked_regionview;
2711 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2712 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2715 if (rtv && rtv->is_track()) {
2716 speed = rtv->get_diskstream()->speed();
2719 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2720 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2721 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2722 // we want a move threshold
2723 drag_info.want_move_threshold = true;
2724 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2725 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2729 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2731 if (selection->regions.empty() || clicked_regionview == 0) {
2735 drag_info.copy = false;
2736 drag_info.item = item;
2737 drag_info.data = clicked_regionview;
2738 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2739 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2744 TimeAxisView* tvp = clicked_axisview;
2745 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2747 if (tv && tv->is_track()) {
2748 speed = tv->get_diskstream()->speed();
2751 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2752 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2753 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2754 // we want a move threshold
2755 drag_info.want_move_threshold = true;
2756 drag_info.brushing = true;
2758 begin_reversible_command (_("Drag region brush"));
2762 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2766 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2767 nframes_t pending_region_position = 0;
2768 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2769 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2770 bool clamp_y_axis = false;
2771 vector<int32_t> height_list(512) ;
2772 vector<int32_t>::iterator j;
2774 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2776 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2778 drag_info.want_move_threshold = false; // don't copy again
2780 /* this is committed in the grab finished callback. */
2782 begin_reversible_command (_("Drag region copy"));
2784 /* duplicate the region(s) */
2786 vector<RegionView*> new_regionviews;
2788 set<Playlist*> affected_playlists;
2789 pair<set<Playlist*>::iterator,bool> insert_result;
2791 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2796 Playlist* to_playlist = rv->region()->playlist();
2797 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2799 insert_result = affected_playlists.insert (to_playlist);
2800 if (insert_result.second) {
2801 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
2804 latest_regionview = 0;
2806 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2808 /* create a new region with the same name. */
2810 boost::shared_ptr<Region> newregion;
2812 newregion = RegionFactory::create (rv->region());
2813 assert(newregion != 0);
2815 /* if the original region was locked, we don't care */
2817 newregion->set_locked (false);
2819 to_playlist->add_region (newregion, (nframes_t) (rv->region()->position() * rtv->get_diskstream()->speed()));
2823 if (latest_regionview) {
2824 new_regionviews.push_back (latest_regionview);
2828 if (new_regionviews.empty()) {
2832 /* reset selection to new regionviews */
2834 selection->set (new_regionviews);
2836 /* reset drag_info data to reflect the fact that we are dragging the copies */
2838 drag_info.data = new_regionviews.front();
2839 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2842 /* Which trackview is this ? */
2844 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2845 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2847 /* The region motion is only processed if the pointer is over
2851 if (!tv || !tv->is_track()) {
2852 /* To make sure we hide the verbose canvas cursor when the mouse is
2853 not held over a track.
2855 hide_verbose_canvas_cursor ();
2859 original_pointer_order = drag_info.last_trackview->order;
2861 /************************************************************
2863 ************************************************************/
2865 if (drag_info.brushing) {
2866 clamp_y_axis = true;
2871 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2873 int32_t children = 0, numtracks = 0;
2874 // XXX hard coding track limit, oh my, so very very bad
2875 bitset <1024> tracks (0x00);
2876 /* get a bitmask representing the visible tracks */
2878 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2879 TimeAxisView *tracklist_timeview;
2880 tracklist_timeview = (*i);
2881 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2882 list<TimeAxisView*> children_list;
2884 /* zeroes are audio tracks. ones are other types. */
2886 if (!rtv2->hidden()) {
2888 if (visible_y_high < rtv2->order) {
2889 visible_y_high = rtv2->order;
2891 if (visible_y_low > rtv2->order) {
2892 visible_y_low = rtv2->order;
2895 if (!rtv2->is_track()) {
2896 tracks = tracks |= (0x01 << rtv2->order);
2899 height_list[rtv2->order] = (*i)->height;
2901 if ((children_list = rtv2->get_child_list()).size() > 0) {
2902 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2903 tracks = tracks |= (0x01 << (rtv2->order + children));
2904 height_list[rtv2->order + children] = (*j)->height;
2912 /* find the actual span according to the canvas */
2914 canvas_pointer_y_span = pointer_y_span;
2915 if (drag_info.last_trackview->order >= tv->order) {
2917 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2918 if (height_list[y] == 0 ) {
2919 canvas_pointer_y_span--;
2924 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2925 if ( height_list[y] == 0 ) {
2926 canvas_pointer_y_span++;
2931 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2932 RegionView* rv2 = (*i);
2933 double ix1, ix2, iy1, iy2;
2936 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2937 rv2->get_canvas_group()->i2w (ix1, iy1);
2938 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2939 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2941 if (rtv2->order != original_pointer_order) {
2942 /* this isn't the pointer track */
2944 if (canvas_pointer_y_span > 0) {
2946 /* moving up the canvas */
2947 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
2949 int32_t visible_tracks = 0;
2950 while (visible_tracks < canvas_pointer_y_span ) {
2953 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2954 /* we're passing through a hidden track */
2959 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2960 clamp_y_axis = true;
2964 clamp_y_axis = true;
2967 } else if (canvas_pointer_y_span < 0) {
2969 /*moving down the canvas*/
2971 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2974 int32_t visible_tracks = 0;
2976 while (visible_tracks > canvas_pointer_y_span ) {
2979 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2983 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2984 clamp_y_axis = true;
2989 clamp_y_axis = true;
2995 /* this is the pointer's track */
2996 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2997 clamp_y_axis = true;
2998 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2999 clamp_y_axis = true;
3007 } else if (drag_info.last_trackview == tv) {
3008 clamp_y_axis = true;
3012 if (!clamp_y_axis) {
3013 drag_info.last_trackview = tv;
3016 /************************************************************
3018 ************************************************************/
3020 /* compute the amount of pointer motion in frames, and where
3021 the region would be if we moved it by that much.
3024 if (drag_info.move_threshold_passed) {
3026 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3028 nframes_t sync_frame;
3029 nframes_t sync_offset;
3032 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3034 sync_offset = rv->region()->sync_offset (sync_dir);
3035 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3037 /* we snap if the snap modifier is not enabled.
3040 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3041 snap_to (sync_frame);
3044 if (sync_frame - sync_offset <= sync_frame) {
3045 pending_region_position = sync_frame - (sync_dir*sync_offset);
3047 pending_region_position = 0;
3051 pending_region_position = 0;
3054 if (pending_region_position > max_frames - rv->region()->length()) {
3055 pending_region_position = drag_info.last_frame_position;
3058 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3060 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3062 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3063 to make it appear at the new location.
3066 if (pending_region_position > drag_info.last_frame_position) {
3067 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3069 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3072 drag_info.last_frame_position = pending_region_position;
3079 /* threshold not passed */
3084 /*************************************************************
3086 ************************************************************/
3088 if (x_delta == 0 && (pointer_y_span == 0)) {
3089 /* haven't reached next snap point, and we're not switching
3090 trackviews. nothing to do.
3096 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3098 RegionView* rv2 = (*i);
3100 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3102 double ix1, ix2, iy1, iy2;
3103 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3104 rv2->get_canvas_group()->i2w (ix1, iy1);
3113 /*************************************************************
3115 ************************************************************/
3117 pair<set<Playlist*>::iterator,bool> insert_result;
3118 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3120 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3122 RegionView* rv = (*i);
3123 double ix1, ix2, iy1, iy2;
3124 int32_t temp_pointer_y_span = pointer_y_span;
3126 /* get item BBox, which will be relative to parent. so we have
3127 to query on a child, then convert to world coordinates using
3131 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3132 rv->get_canvas_group()->i2w (ix1, iy1);
3133 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3134 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3135 RouteTimeAxisView* temp_rtv;
3137 if ((pointer_y_span != 0) && !clamp_y_axis) {
3140 for (j = height_list.begin(); j!= height_list.end(); j++) {
3141 if (x == canvas_rtv->order) {
3142 /* we found the track the region is on */
3143 if (x != original_pointer_order) {
3144 /*this isn't from the same track we're dragging from */
3145 temp_pointer_y_span = canvas_pointer_y_span;
3147 while (temp_pointer_y_span > 0) {
3148 /* we're moving up canvas-wise,
3149 so we need to find the next track height
3151 if (j != height_list.begin()) {
3154 if (x != original_pointer_order) {
3155 /* we're not from the dragged track, so ignore hidden tracks. */
3157 temp_pointer_y_span++;
3161 temp_pointer_y_span--;
3163 while (temp_pointer_y_span < 0) {
3165 if (x != original_pointer_order) {
3167 temp_pointer_y_span--;
3171 if (j != height_list.end()) {
3174 temp_pointer_y_span++;
3176 /* find out where we'll be when we move and set height accordingly */
3178 tvp2 = trackview_by_y_position (iy1 + y_delta);
3179 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3180 rv->set_height (temp_rtv->height);
3182 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3183 personally, i think this can confuse things, but never mind.
3186 //const GdkColor& col (temp_rtv->view->get_region_color());
3187 //rv->set_color (const_cast<GdkColor&>(col));
3194 /* prevent the regionview from being moved to before
3195 the zero position on the canvas.
3200 if (-x_delta > ix1) {
3203 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3204 x_delta = max_frames - rv->region()->last_frame();
3207 if (drag_info.first_move) {
3209 /* hide any dependent views */
3211 // rv->get_time_axis_view().hide_dependent_views (*rv);
3213 /* this is subtle. raising the regionview itself won't help,
3214 because raise_to_top() just puts the item on the top of
3215 its parent's stack. so, we need to put the trackview canvas_display group
3216 on the top, since its parent is the whole canvas.
3219 rv->get_canvas_group()->raise_to_top();
3220 rv->get_time_axis_view().canvas_display->raise_to_top();
3221 cursor_group->raise_to_top();
3223 /* freeze the playlists from notifying till
3227 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3228 if (rtv && rtv->is_track()) {
3229 Playlist* pl = dynamic_cast<Playlist*>(rtv->get_diskstream()->playlist());
3231 /* only freeze and capture state once */
3233 insert_result = motion_frozen_playlists.insert (pl);
3234 if (insert_result.second) {
3236 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
3242 if (drag_info.brushing) {
3243 mouse_brush_insert_region (rv, pending_region_position);
3245 rv->move (x_delta, y_delta);
3249 if (drag_info.first_move) {
3250 cursor_group->raise_to_top();
3253 drag_info.first_move = false;
3255 if (x_delta != 0 && !drag_info.brushing) {
3256 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3262 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3265 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3266 pair<set<Playlist*>::iterator,bool> insert_result;
3267 bool nocommit = true;
3269 RouteTimeAxisView* atv;
3270 bool regionview_y_movement;
3271 bool regionview_x_movement;
3273 /* first_move is set to false if the regionview has been moved in the
3277 if (drag_info.first_move) {
3284 /* The regionview has been moved at some stage during the grab so we need
3285 to account for any mouse movement between this event and the last one.
3288 region_drag_motion_callback (item, event);
3290 if (drag_info.brushing) {
3291 /* all changes were made during motion event handlers */
3295 /* adjust for track speed */
3298 atv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3299 if (atv && atv->get_diskstream()) {
3300 speed = atv->get_diskstream()->speed();
3303 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3304 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3306 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3307 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3309 if (regionview_y_movement) {
3311 /* motion between tracks */
3313 list<RegionView*> new_selection;
3315 /* moved to a different audio track. */
3317 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3319 RegionView* rv2 = (*i);
3321 /* the region that used to be in the old playlist is not
3322 moved to the new one - we make a copy of it. as a result,
3323 any existing editor for the region should no longer be
3327 if (!drag_info.copy) {
3328 rv2->hide_region_editor();
3330 new_selection.push_back (rv2);
3334 /* first, freeze the target tracks */
3336 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3338 Playlist* from_playlist;
3339 Playlist* to_playlist;
3341 double ix1, ix2, iy1, iy2;
3343 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3344 (*i)->get_canvas_group()->i2w (ix1, iy1);
3345 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3346 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3348 from_playlist = (*i)->region()->playlist();
3349 to_playlist = atv2->playlist();
3351 /* the from_playlist was frozen in the "first_move" case
3352 of the motion handler. the insert can fail,
3353 but that doesn't matter. it just means
3354 we already have the playlist in the list.
3357 motion_frozen_playlists.insert (from_playlist);
3359 /* only freeze the to_playlist once */
3361 insert_result = motion_frozen_playlists.insert(to_playlist);
3362 if (insert_result.second) {
3363 to_playlist->freeze();
3364 session->add_command(new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3369 /* now do it again with the actual operations */
3371 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3373 Playlist* from_playlist;
3374 Playlist* to_playlist;
3376 double ix1, ix2, iy1, iy2;
3378 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3379 (*i)->get_canvas_group()->i2w (ix1, iy1);
3380 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3381 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3383 from_playlist = (*i)->region()->playlist();
3384 to_playlist = atv2->playlist();
3386 latest_regionview = 0;
3388 where = (nframes_t) (unit_to_frame (ix1) * speed);
3389 boost::shared_ptr<Region> new_region (RegionFactory::create ((*i)->region()));
3391 from_playlist->remove_region (((*i)->region()));
3393 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3394 to_playlist->add_region (new_region, where);
3397 if (latest_regionview) {
3398 selection->add (latest_regionview);
3404 /* motion within a single track */
3406 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3410 if (rv->region()->locked()) {
3414 if (regionview_x_movement) {
3415 double ownspeed = 1.0;
3416 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3418 if (atv && atv->get_diskstream()) {
3419 ownspeed = atv->get_diskstream()->speed();
3422 /* base the new region position on the current position of the regionview.*/
3424 double ix1, ix2, iy1, iy2;
3426 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3427 rv->get_canvas_group()->i2w (ix1, iy1);
3428 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3432 where = rv->region()->position();
3435 rv->get_time_axis_view().reveal_dependent_views (*rv);
3437 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3439 rv->region()->set_position (where, (void *) this);
3444 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3446 session->add_command (new MementoCommand<Playlist>(*(*p), 0, & (*p)->get_state()));
3449 motion_frozen_playlists.clear ();
3452 commit_reversible_command ();
3457 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3459 /* Either add to or set the set the region selection, unless
3460 this is an alignment click (control used)
3463 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3464 TimeAxisView* tv = &rv.get_time_axis_view();
3465 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
3467 if (atv && atv->is_track()) {
3468 speed = atv->get_diskstream()->speed();
3471 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3473 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3475 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3477 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3481 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3487 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3498 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3499 case AudioClock::BBT:
3500 session->bbt_time (frame, bbt);
3501 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3504 case AudioClock::SMPTE:
3505 session->smpte_time (frame, smpte);
3506 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3509 case AudioClock::MinSec:
3510 /* XXX fix this to compute min/sec properly */
3511 session->smpte_time (frame, smpte);
3512 secs = smpte.seconds + ((float) smpte.frames / Config->get_smpte_frames_per_second());
3513 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3517 snprintf (buf, sizeof(buf), "%u", frame);
3521 if (xpos >= 0 && ypos >=0) {
3522 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3525 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3527 show_verbose_canvas_cursor ();
3531 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3538 Meter meter_at_start(session->tempo_map().meter_at(start));
3544 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3545 case AudioClock::BBT:
3546 session->bbt_time (start, sbbt);
3547 session->bbt_time (end, ebbt);
3550 /* XXX this computation won't work well if the
3551 user makes a selection that spans any meter changes.
3554 ebbt.bars -= sbbt.bars;
3555 if (ebbt.beats >= sbbt.beats) {
3556 ebbt.beats -= sbbt.beats;
3559 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3561 if (ebbt.ticks >= sbbt.ticks) {
3562 ebbt.ticks -= sbbt.ticks;
3565 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3568 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3571 case AudioClock::SMPTE:
3572 session->smpte_duration (end - start, smpte);
3573 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3576 case AudioClock::MinSec:
3577 /* XXX fix this to compute min/sec properly */
3578 session->smpte_duration (end - start, smpte);
3579 secs = smpte.seconds + ((float) smpte.frames / Config->get_smpte_frames_per_second());
3580 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3584 snprintf (buf, sizeof(buf), "%u", end - start);
3588 if (xpos >= 0 && ypos >=0) {
3589 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3592 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3594 show_verbose_canvas_cursor ();
3598 Editor::collect_new_region_view (RegionView* rv)
3600 latest_regionview = rv;
3604 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3606 if (clicked_regionview == 0) {
3610 /* lets try to create new Region for the selection */
3612 vector<boost::shared_ptr<AudioRegion> > new_regions;
3613 create_region_from_selection (new_regions);
3615 if (new_regions.empty()) {
3619 /* XXX fix me one day to use all new regions */
3621 boost::shared_ptr<Region> region (new_regions.front());
3623 /* add it to the current stream/playlist.
3625 tricky: the streamview for the track will add a new regionview. we will
3626 catch the signal it sends when it creates the regionview to
3627 set the regionview we want to then drag.
3630 latest_regionview = 0;
3631 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3633 /* A selection grab currently creates two undo/redo operations, one for
3634 creating the new region and another for moving it.
3637 begin_reversible_command (_("selection grab"));
3639 Playlist* playlist = clicked_axisview->playlist();
3641 XMLNode *before = &(playlist->get_state());
3642 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3643 XMLNode *after = &(playlist->get_state());
3644 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3646 commit_reversible_command ();
3650 if (latest_regionview == 0) {
3651 /* something went wrong */
3655 /* we need to deselect all other regionviews, and select this one
3656 i'm ignoring undo stuff, because the region creation will take care of it */
3657 selection->set (latest_regionview);
3659 drag_info.item = latest_regionview->get_canvas_group();
3660 drag_info.data = latest_regionview;
3661 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3662 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3666 drag_info.last_trackview = clicked_axisview;
3667 drag_info.last_frame_position = latest_regionview->region()->position();
3668 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3670 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3674 Editor::cancel_selection ()
3676 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3677 (*i)->hide_selection ();
3679 begin_reversible_command (_("cancel selection"));
3680 selection->clear ();
3681 clicked_selection = 0;
3682 commit_reversible_command ();
3686 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3688 nframes_t start = 0;
3695 drag_info.item = item;
3696 drag_info.motion_callback = &Editor::drag_selection;
3697 drag_info.finished_callback = &Editor::end_selection_op;
3702 case CreateSelection:
3703 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3704 drag_info.copy = true;
3706 drag_info.copy = false;
3708 start_grab (event, selector_cursor);
3711 case SelectionStartTrim:
3712 if (clicked_axisview) {
3713 clicked_axisview->order_selection_trims (item, true);
3715 start_grab (event, trimmer_cursor);
3716 start = selection->time[clicked_selection].start;
3717 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3720 case SelectionEndTrim:
3721 if (clicked_axisview) {
3722 clicked_axisview->order_selection_trims (item, false);
3724 start_grab (event, trimmer_cursor);
3725 end = selection->time[clicked_selection].end;
3726 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3730 start = selection->time[clicked_selection].start;
3732 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3736 if (selection_op == SelectionMove) {
3737 show_verbose_time_cursor(start, 10);
3739 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3744 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3746 nframes_t start = 0;
3749 nframes_t pending_position;
3751 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3752 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3755 pending_position = 0;
3758 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3759 snap_to (pending_position);
3762 /* only alter selection if the current frame is
3763 different from the last frame position (adjusted)
3766 if (pending_position == drag_info.last_pointer_frame) return;
3768 switch (selection_op) {
3769 case CreateSelection:
3771 if (drag_info.first_move) {
3772 snap_to (drag_info.grab_frame);
3775 if (pending_position < drag_info.grab_frame) {
3776 start = pending_position;
3777 end = drag_info.grab_frame;
3779 end = pending_position;
3780 start = drag_info.grab_frame;
3783 /* first drag: Either add to the selection
3784 or create a new selection->
3787 if (drag_info.first_move) {
3789 begin_reversible_command (_("range selection"));
3791 if (drag_info.copy) {
3792 /* adding to the selection */
3793 clicked_selection = selection->add (start, end);
3794 drag_info.copy = false;
3796 /* new selection-> */
3797 clicked_selection = selection->set (clicked_axisview, start, end);
3802 case SelectionStartTrim:
3804 if (drag_info.first_move) {
3805 begin_reversible_command (_("trim selection start"));
3808 start = selection->time[clicked_selection].start;
3809 end = selection->time[clicked_selection].end;
3811 if (pending_position > end) {
3814 start = pending_position;
3818 case SelectionEndTrim:
3820 if (drag_info.first_move) {
3821 begin_reversible_command (_("trim selection end"));
3824 start = selection->time[clicked_selection].start;
3825 end = selection->time[clicked_selection].end;
3827 if (pending_position < start) {
3830 end = pending_position;
3837 if (drag_info.first_move) {
3838 begin_reversible_command (_("move selection"));
3841 start = selection->time[clicked_selection].start;
3842 end = selection->time[clicked_selection].end;
3844 length = end - start;
3846 start = pending_position;
3849 end = start + length;
3854 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3855 start_canvas_autoscroll (1);
3859 selection->replace (clicked_selection, start, end);
3862 drag_info.last_pointer_frame = pending_position;
3863 drag_info.first_move = false;
3865 if (selection_op == SelectionMove) {
3866 show_verbose_time_cursor(start, 10);
3868 show_verbose_time_cursor(pending_position, 10);
3873 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3875 if (!drag_info.first_move) {
3876 drag_selection (item, event);
3877 /* XXX this is not object-oriented programming at all. ick */
3878 if (selection->time.consolidate()) {
3879 selection->TimeChanged ();
3881 commit_reversible_command ();
3883 /* just a click, no pointer movement.*/
3885 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3887 selection->clear_time();
3892 /* XXX what happens if its a music selection? */
3893 session->set_audio_range (selection->time);
3894 stop_canvas_autoscroll ();
3898 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3901 TimeAxisView* tvp = clicked_axisview;
3902 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3904 if (tv && tv->is_track()) {
3905 speed = tv->get_diskstream()->speed();
3908 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3909 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3910 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3912 motion_frozen_playlists.clear();
3914 //drag_info.item = clicked_regionview->get_name_highlight();
3915 drag_info.item = item;
3916 drag_info.motion_callback = &Editor::trim_motion_callback;
3917 drag_info.finished_callback = &Editor::trim_finished_callback;
3919 start_grab (event, trimmer_cursor);
3921 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3922 trim_op = ContentsTrim;
3924 /* These will get overridden for a point trim.*/
3925 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3926 /* closer to start */
3927 trim_op = StartTrim;
3928 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3936 show_verbose_time_cursor(region_start, 10);
3939 show_verbose_time_cursor(region_end, 10);
3942 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3948 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3950 RegionView* rv = clicked_regionview;
3951 nframes_t frame_delta = 0;
3952 bool left_direction;
3953 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3955 /* snap modifier works differently here..
3956 its' current state has to be passed to the
3957 various trim functions in order to work properly
3961 TimeAxisView* tvp = clicked_axisview;
3962 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3963 pair<set<Playlist*>::iterator,bool> insert_result;
3965 if (tv && tv->is_track()) {
3966 speed = tv->get_diskstream()->speed();
3969 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3970 left_direction = true;
3972 left_direction = false;
3976 snap_to (drag_info.current_pointer_frame);
3979 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3983 if (drag_info.first_move) {
3989 trim_type = "Region start trim";
3992 trim_type = "Region end trim";
3995 trim_type = "Region content trim";
3999 begin_reversible_command (trim_type);
4001 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4002 (*i)->region()->freeze ();
4004 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4006 arv->temporarily_hide_envelope ();
4008 Playlist * pl = (*i)->region()->playlist();
4009 insert_result = motion_frozen_playlists.insert (pl);
4010 if (insert_result.second) {
4011 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4016 if (left_direction) {
4017 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4019 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4024 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4027 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4028 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4034 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4037 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4038 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4045 bool swap_direction = false;
4047 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4048 swap_direction = true;
4051 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4052 i != selection->regions.by_layer().end(); ++i)
4054 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4062 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4065 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4068 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4072 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4073 drag_info.first_move = false;
4077 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4079 boost::shared_ptr<Region> region (rv.region());
4081 if (region->locked()) {
4085 nframes_t new_bound;
4088 TimeAxisView* tvp = clicked_axisview;
4089 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4091 if (tv && tv->is_track()) {
4092 speed = tv->get_diskstream()->speed();
4095 if (left_direction) {
4096 if (swap_direction) {
4097 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4099 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4102 if (swap_direction) {
4103 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4105 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4110 snap_to (new_bound);
4112 region->trim_start ((nframes_t) (new_bound * speed), this);
4113 rv.region_changed (StartChanged);
4117 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4119 boost::shared_ptr<Region> region (rv.region());
4121 if (region->locked()) {
4125 nframes_t new_bound;
4128 TimeAxisView* tvp = clicked_axisview;
4129 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4131 if (tv && tv->is_track()) {
4132 speed = tv->get_diskstream()->speed();
4135 if (left_direction) {
4136 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4138 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4142 snap_to (new_bound, (left_direction ? 0 : 1));
4145 region->trim_front ((nframes_t) (new_bound * speed), this);
4147 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4151 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4153 boost::shared_ptr<Region> region (rv.region());
4155 if (region->locked()) {
4159 nframes_t new_bound;
4162 TimeAxisView* tvp = clicked_axisview;
4163 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4165 if (tv && tv->is_track()) {
4166 speed = tv->get_diskstream()->speed();
4169 if (left_direction) {
4170 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4172 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4176 snap_to (new_bound);
4178 region->trim_end ((nframes_t) (new_bound * speed), this);
4179 rv.region_changed (LengthChanged);
4183 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4185 if (!drag_info.first_move) {
4186 trim_motion_callback (item, event);
4188 if (!clicked_regionview->get_selected()) {
4189 thaw_region_after_trim (*clicked_regionview);
4192 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4193 i != selection->regions.by_layer().end(); ++i)
4195 thaw_region_after_trim (**i);
4199 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4201 session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
4204 motion_frozen_playlists.clear ();
4206 commit_reversible_command();
4208 /* no mouse movement */
4214 Editor::point_trim (GdkEvent* event)
4216 RegionView* rv = clicked_regionview;
4217 nframes_t new_bound = drag_info.current_pointer_frame;
4219 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4220 snap_to (new_bound);
4223 /* Choose action dependant on which button was pressed */
4224 switch (event->button.button) {
4226 trim_op = StartTrim;
4227 begin_reversible_command (_("Start point trim"));
4229 if (rv->get_selected()) {
4231 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4232 i != selection->regions.by_layer().end(); ++i)
4234 if (!(*i)->region()->locked()) {
4235 Playlist *pl = (*i)->region()->playlist();
4236 XMLNode &before = pl->get_state();
4237 (*i)->region()->trim_front (new_bound, this);
4238 XMLNode &after = pl->get_state();
4239 session->add_command(new MementoCommand<Playlist>(*pl, &before, &after));
4245 if (!rv->region()->locked()) {
4246 Playlist *pl = rv->region()->playlist();
4247 XMLNode &before = pl->get_state();
4248 rv->region()->trim_front (new_bound, this);
4249 XMLNode &after = pl->get_state();
4250 session->add_command(new MementoCommand<Playlist>(*pl, &before, &after));
4254 commit_reversible_command();
4259 begin_reversible_command (_("End point trim"));
4261 if (rv->get_selected()) {
4263 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4265 if (!(*i)->region()->locked()) {
4266 Playlist *pl = (*i)->region()->playlist();
4267 XMLNode &before = pl->get_state();
4268 (*i)->region()->trim_end (new_bound, this);
4269 XMLNode &after = pl->get_state();
4270 session->add_command(new MementoCommand<Playlist>(*pl, &before, &after));
4276 if (!rv->region()->locked()) {
4277 Playlist *pl = rv->region()->playlist();
4278 XMLNode &before = pl->get_state();
4279 rv->region()->trim_end (new_bound, this);
4280 XMLNode &after = pl->get_state();
4281 session->add_command (new MementoCommand<Playlist>(*pl, &before, &after));
4285 commit_reversible_command();
4294 Editor::thaw_region_after_trim (RegionView& rv)
4296 boost::shared_ptr<Region> region (rv.region());
4298 if (region->locked()) {
4302 region->thaw (_("trimmed region"));
4303 XMLNode &after = region->playlist()->get_state();
4304 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4306 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4308 arv->unhide_envelope ();
4312 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4317 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4318 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4322 Location* location = find_location_from_marker (marker, is_start);
4323 location->set_hidden (true, this);
4328 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4334 drag_info.item = item;
4335 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4336 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4338 range_marker_op = op;
4340 if (!temp_location) {
4341 temp_location = new Location;
4345 case CreateRangeMarker:
4346 case CreateTransportMarker:
4348 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4349 drag_info.copy = true;
4351 drag_info.copy = false;
4353 start_grab (event, selector_cursor);
4357 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4362 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4364 nframes_t start = 0;
4366 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4368 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4369 snap_to (drag_info.current_pointer_frame);
4372 /* only alter selection if the current frame is
4373 different from the last frame position.
4376 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4378 switch (range_marker_op) {
4379 case CreateRangeMarker:
4380 case CreateTransportMarker:
4381 if (drag_info.first_move) {
4382 snap_to (drag_info.grab_frame);
4385 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4386 start = drag_info.current_pointer_frame;
4387 end = drag_info.grab_frame;
4389 end = drag_info.current_pointer_frame;
4390 start = drag_info.grab_frame;
4393 /* first drag: Either add to the selection
4394 or create a new selection.
4397 if (drag_info.first_move) {
4399 temp_location->set (start, end);
4403 update_marker_drag_item (temp_location);
4404 range_marker_drag_rect->show();
4405 range_marker_drag_rect->raise_to_top();
4411 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4412 start_canvas_autoscroll (1);
4416 temp_location->set (start, end);
4418 double x1 = frame_to_pixel (start);
4419 double x2 = frame_to_pixel (end);
4420 crect->property_x1() = x1;
4421 crect->property_x2() = x2;
4423 update_marker_drag_item (temp_location);
4426 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4427 drag_info.first_move = false;
4429 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4434 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4436 Location * newloc = 0;
4438 if (!drag_info.first_move) {
4439 drag_range_markerbar_op (item, event);
4441 switch (range_marker_op) {
4442 case CreateRangeMarker:
4444 begin_reversible_command (_("new range marker"));
4445 XMLNode &before = session->locations()->get_state();
4446 newloc = new Location(temp_location->start(), temp_location->end(), "unnamed", Location::IsRangeMarker);
4447 session->locations()->add (newloc, true);
4448 XMLNode &after = session->locations()->get_state();
4449 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4450 commit_reversible_command ();
4452 range_bar_drag_rect->hide();
4453 range_marker_drag_rect->hide();
4457 case CreateTransportMarker:
4458 // popup menu to pick loop or punch
4459 new_transport_marker_context_menu (&event->button, item);
4464 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4466 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4471 start = session->locations()->first_mark_before (drag_info.grab_frame);
4472 end = session->locations()->first_mark_after (drag_info.grab_frame);
4474 if (end == max_frames) {
4475 end = session->current_end_frame ();
4479 start = session->current_start_frame ();
4482 switch (mouse_mode) {
4484 /* find the two markers on either side and then make the selection from it */
4485 cerr << "select between " << start << " .. " << end << endl;
4486 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4490 /* find the two markers on either side of the click and make the range out of it */
4491 selection->set (0, start, end);
4500 stop_canvas_autoscroll ();
4506 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4508 drag_info.item = item;
4509 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4510 drag_info.finished_callback = &Editor::end_mouse_zoom;
4512 start_grab (event, zoom_cursor);
4514 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4518 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4523 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4524 snap_to (drag_info.current_pointer_frame);
4526 if (drag_info.first_move) {
4527 snap_to (drag_info.grab_frame);
4531 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4533 /* base start and end on initial click position */
4534 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4535 start = drag_info.current_pointer_frame;
4536 end = drag_info.grab_frame;
4538 end = drag_info.current_pointer_frame;
4539 start = drag_info.grab_frame;
4544 if (drag_info.first_move) {
4546 zoom_rect->raise_to_top();
4549 reposition_zoom_rect(start, end);
4551 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4552 drag_info.first_move = false;
4554 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4559 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4561 if (!drag_info.first_move) {
4562 drag_mouse_zoom (item, event);
4564 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4565 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4567 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4570 temporal_zoom_to_frame (false, drag_info.grab_frame);
4572 temporal_zoom_step (false);
4573 center_screen (drag_info.grab_frame);
4581 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4583 double x1 = frame_to_pixel (start);
4584 double x2 = frame_to_pixel (end);
4585 double y2 = canvas_height - 2;
4587 zoom_rect->property_x1() = x1;
4588 zoom_rect->property_y1() = 1.0;
4589 zoom_rect->property_x2() = x2;
4590 zoom_rect->property_y2() = y2;
4594 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4596 drag_info.item = item;
4597 drag_info.motion_callback = &Editor::drag_rubberband_select;
4598 drag_info.finished_callback = &Editor::end_rubberband_select;
4600 start_grab (event, cross_hair_cursor);
4602 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4606 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4613 /* use a bigger drag threshold than the default */
4615 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4619 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4620 // snap_to (drag_info.current_pointer_frame);
4622 // if (drag_info.first_move) {
4623 // snap_to (drag_info.grab_frame);
4628 /* base start and end on initial click position */
4629 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4630 start = drag_info.current_pointer_frame;
4631 end = drag_info.grab_frame;
4633 end = drag_info.current_pointer_frame;
4634 start = drag_info.grab_frame;
4637 if (drag_info.current_pointer_y < drag_info.grab_y) {
4638 y1 = drag_info.current_pointer_y;
4639 y2 = drag_info.grab_y;
4642 y2 = drag_info.current_pointer_y;
4643 y1 = drag_info.grab_y;
4647 if (start != end || y1 != y2) {
4649 double x1 = frame_to_pixel (start);
4650 double x2 = frame_to_pixel (end);
4652 rubberband_rect->property_x1() = x1;
4653 rubberband_rect->property_y1() = y1;
4654 rubberband_rect->property_x2() = x2;
4655 rubberband_rect->property_y2() = y2;
4657 rubberband_rect->show();
4658 rubberband_rect->raise_to_top();
4660 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4661 drag_info.first_move = false;
4663 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4668 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4670 if (!drag_info.first_move) {
4672 drag_rubberband_select (item, event);
4675 if (drag_info.current_pointer_y < drag_info.grab_y) {
4676 y1 = drag_info.current_pointer_y;
4677 y2 = drag_info.grab_y;
4680 y2 = drag_info.current_pointer_y;
4681 y1 = drag_info.grab_y;
4685 Selection::Operation op = Keyboard::selection_type (event->button.state);
4688 begin_reversible_command (_("select regions"));
4690 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4691 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4693 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4697 commit_reversible_command ();
4701 selection->clear_regions();
4702 selection->clear_points ();
4703 selection->clear_lines ();
4706 rubberband_rect->hide();
4711 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4713 using namespace Gtkmm2ext;
4715 ArdourPrompter prompter (false);
4717 prompter.set_prompt (_("Name for region:"));
4718 prompter.set_initial_text (clicked_regionview->region()->name());
4719 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4720 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4721 prompter.show_all ();
4722 switch (prompter.run ()) {
4723 case Gtk::RESPONSE_ACCEPT:
4725 prompter.get_result(str);
4727 clicked_regionview->region()->set_name (str);
4735 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4737 drag_info.item = item;
4738 drag_info.motion_callback = &Editor::time_fx_motion;
4739 drag_info.finished_callback = &Editor::end_time_fx;
4743 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4747 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4749 RegionView* rv = clicked_regionview;
4751 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4752 snap_to (drag_info.current_pointer_frame);
4755 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4759 if (drag_info.current_pointer_frame > rv->region()->position()) {
4760 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4763 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4764 drag_info.first_move = false;
4766 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4770 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4772 clicked_regionview->get_time_axis_view().hide_timestretch ();
4774 if (drag_info.first_move) {
4778 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4779 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4781 begin_reversible_command (_("timestretch"));
4783 if (run_timestretch (selection->regions, percentage) == 0) {
4784 session->commit_reversible_command ();
4789 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4791 /* no brushing without a useful snap setting */
4794 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4797 switch (snap_mode) {
4799 return; /* can't work because it allows region to be placed anywhere */
4804 switch (snap_type) {
4807 case SnapToEditCursor:
4814 /* don't brush a copy over the original */
4816 if (pos == rv->region()->position()) {
4820 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4822 if (atv == 0 || !atv->is_track()) {
4826 Playlist* playlist = atv->playlist();
4827 double speed = atv->get_diskstream()->speed();
4829 XMLNode &before = playlist->get_state();
4830 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4831 XMLNode &after = playlist->get_state();
4832 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
4834 // playlist is frozen, so we have to update manually
4836 playlist->StateChanged (Change (~0)); /* EMIT SIGNAL */
4840 Editor::track_height_step_timeout ()
4843 struct timeval delta;
4845 gettimeofday (&now, 0);
4846 timersub (&now, &last_track_height_step_timestamp, &delta);
4848 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4849 current_stepping_trackview = 0;