2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <pbd/error.h>
30 #include <gtkmm2ext/utils.h>
31 #include <pbd/memento_command.h>
33 #include "ardour_ui.h"
35 #include "time_axis_view.h"
36 #include "audio_time_axis.h"
37 #include "audio_region_view.h"
39 #include "streamview.h"
40 #include "region_gain_line.h"
41 #include "automation_time_axis.h"
44 #include "selection.h"
47 #include "rgb_macros.h"
49 #include <ardour/types.h>
50 #include <ardour/route.h>
51 #include <ardour/audio_track.h>
52 #include <ardour/audio_diskstream.h>
53 #include <ardour/playlist.h>
54 #include <ardour/audioplaylist.h>
55 #include <ardour/audioregion.h>
56 #include <ardour/dB.h>
57 #include <ardour/utils.h>
58 #include <ardour/region_factory.h>
65 using namespace ARDOUR;
69 using namespace Editing;
72 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
86 switch (event->type) {
87 case GDK_BUTTON_RELEASE:
88 case GDK_BUTTON_PRESS:
89 case GDK_2BUTTON_PRESS:
90 case GDK_3BUTTON_PRESS:
91 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
93 case GDK_MOTION_NOTIFY:
94 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
96 case GDK_ENTER_NOTIFY:
97 case GDK_LEAVE_NOTIFY:
98 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
101 case GDK_KEY_RELEASE:
102 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
105 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
109 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
110 position is negative (as can be the case with motion events in particular),
111 the frame location is always positive.
114 return pixel_to_frame (*pcx);
118 Editor::mouse_mode_toggled (MouseMode m)
120 if (ignore_mouse_mode_toggle) {
126 if (mouse_select_button.get_active()) {
132 if (mouse_move_button.get_active()) {
138 if (mouse_gain_button.get_active()) {
144 if (mouse_zoom_button.get_active()) {
150 if (mouse_timefx_button.get_active()) {
156 if (mouse_audition_button.get_active()) {
167 Editor::set_mouse_mode (MouseMode m, bool force)
169 if (drag_info.item) {
173 if (!force && m == mouse_mode) {
181 if (mouse_mode != MouseRange) {
183 /* in all modes except range, hide the range selection,
184 show the object (region) selection.
187 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
188 (*i)->set_should_show_selection (true);
190 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
191 (*i)->hide_selection ();
197 in range mode,show the range selection.
200 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
201 if ((*i)->get_selected()) {
202 (*i)->show_selection (selection->time);
207 /* XXX the hack of unsetting all other buttongs should go
208 away once GTK2 allows us to use regular radio buttons drawn like
209 normal buttons, rather than my silly GroupedButton hack.
212 ignore_mouse_mode_toggle = true;
214 switch (mouse_mode) {
216 mouse_select_button.set_active (true);
217 current_canvas_cursor = selector_cursor;
221 mouse_move_button.set_active (true);
222 current_canvas_cursor = grabber_cursor;
226 mouse_gain_button.set_active (true);
227 current_canvas_cursor = cross_hair_cursor;
231 mouse_zoom_button.set_active (true);
232 current_canvas_cursor = zoom_cursor;
236 mouse_timefx_button.set_active (true);
237 current_canvas_cursor = time_fx_cursor; // just use playhead
241 mouse_audition_button.set_active (true);
242 current_canvas_cursor = speaker_cursor;
246 ignore_mouse_mode_toggle = false;
249 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
254 Editor::step_mouse_mode (bool next)
256 switch (current_mouse_mode()) {
258 if (next) set_mouse_mode (MouseRange);
259 else set_mouse_mode (MouseTimeFX);
263 if (next) set_mouse_mode (MouseZoom);
264 else set_mouse_mode (MouseObject);
268 if (next) set_mouse_mode (MouseGain);
269 else set_mouse_mode (MouseRange);
273 if (next) set_mouse_mode (MouseTimeFX);
274 else set_mouse_mode (MouseZoom);
278 if (next) set_mouse_mode (MouseAudition);
279 else set_mouse_mode (MouseGain);
283 if (next) set_mouse_mode (MouseObject);
284 else set_mouse_mode (MouseTimeFX);
290 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
294 /* in object/audition/timefx mode, any button press sets
295 the selection if the object can be selected. this is a
296 bit of hack, because we want to avoid this if the
297 mouse operation is a region alignment.
299 note: not dbl-click or triple-click
302 if (((mouse_mode != MouseObject) &&
303 (mouse_mode != MouseAudition || item_type != RegionItem) &&
304 (mouse_mode != MouseTimeFX || item_type != RegionItem)) ||
305 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
310 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
312 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
314 /* no selection action on modified button-2 or button-3 events */
320 Selection::Operation op = Keyboard::selection_type (event->button.state);
321 bool press = (event->type == GDK_BUTTON_PRESS);
323 begin_reversible_command (_("select on click"));
327 commit = set_selected_regionview_from_click (press, op, true);
330 case RegionViewNameHighlight:
332 commit = set_selected_regionview_from_click (press, op, true);
335 case FadeInHandleItem:
337 case FadeOutHandleItem:
339 commit = set_selected_regionview_from_click (press, op, true);
342 case GainAutomationControlPointItem:
343 case PanAutomationControlPointItem:
344 case RedirectAutomationControlPointItem:
345 commit = set_selected_control_point_from_click (op, false);
349 if (event->button.button == 3) {
350 /* for context click, select track */
351 commit = set_selected_track_from_click (press, op, true);
355 case AutomationTrackItem:
356 commit = set_selected_track_from_click (press, op, true);
363 #define SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
364 #ifdef SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
365 /* in range mode, button 1/2/3 press potentially selects a track */
367 if (mouse_mode == MouseRange &&
368 event->type == GDK_BUTTON_PRESS &&
369 event->button.button <= 3) {
374 case AutomationTrackItem:
375 commit = set_selected_track_from_click (true, op, true);
384 commit_reversible_command ();
389 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
391 nframes_t where = event_frame (event, 0, 0);
393 track_canvas.grab_focus();
395 if (session && session->actively_recording()) {
399 button_selection (item, event, item_type);
401 if (drag_info.item == 0 &&
402 (Keyboard::is_delete_event (&event->button) ||
403 Keyboard::is_context_menu_event (&event->button) ||
404 Keyboard::is_edit_event (&event->button))) {
406 /* handled by button release */
410 switch (event->button.button) {
413 if (event->type == GDK_BUTTON_PRESS) {
415 if (drag_info.item) {
416 drag_info.item->ungrab (event->button.time);
419 /* single mouse clicks on any of these item types operate
420 independent of mouse mode, mostly because they are
421 not on the main track canvas or because we want
427 case PlayheadCursorItem:
428 start_cursor_grab (item, event);
432 if (Keyboard::modifier_state_equals (event->button.state,
433 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
434 hide_marker (item, event);
436 start_marker_grab (item, event);
440 case TempoMarkerItem:
441 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
442 start_tempo_marker_copy_grab (item, event);
444 start_tempo_marker_grab (item, event);
448 case MeterMarkerItem:
449 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
450 start_meter_marker_copy_grab (item, event);
452 start_meter_marker_grab (item, event);
462 case RangeMarkerBarItem:
463 start_range_markerbar_op (item, event, CreateRangeMarker);
467 case TransportMarkerBarItem:
468 start_range_markerbar_op (item, event, CreateTransportMarker);
477 switch (mouse_mode) {
480 case StartSelectionTrimItem:
481 start_selection_op (item, event, SelectionStartTrim);
484 case EndSelectionTrimItem:
485 start_selection_op (item, event, SelectionEndTrim);
489 if (Keyboard::modifier_state_contains
490 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
491 // contains and not equals because I can't use alt as a modifier alone.
492 start_selection_grab (item, event);
493 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
494 /* grab selection for moving */
495 start_selection_op (item, event, SelectionMove);
498 /* this was debated, but decided the more common action was to
499 make a new selection */
500 start_selection_op (item, event, CreateSelection);
505 start_selection_op (item, event, CreateSelection);
511 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
512 event->type == GDK_BUTTON_PRESS) {
514 start_rubberband_select (item, event);
516 } else if (event->type == GDK_BUTTON_PRESS) {
519 case FadeInHandleItem:
520 start_fade_in_grab (item, event);
523 case FadeOutHandleItem:
524 start_fade_out_grab (item, event);
528 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
529 start_region_copy_grab (item, event);
530 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
531 start_region_brush_grab (item, event);
533 start_region_grab (item, event);
537 case RegionViewNameHighlight:
538 start_trim (item, event);
543 /* rename happens on edit clicks */
544 start_trim (clicked_regionview->get_name_highlight(), event);
548 case GainAutomationControlPointItem:
549 case PanAutomationControlPointItem:
550 case RedirectAutomationControlPointItem:
551 start_control_point_grab (item, event);
555 case GainAutomationLineItem:
556 case PanAutomationLineItem:
557 case RedirectAutomationLineItem:
558 start_line_grab_from_line (item, event);
563 case AutomationTrackItem:
564 start_rubberband_select (item, event);
567 /* <CMT Additions> */
568 case ImageFrameHandleStartItem:
569 imageframe_start_handle_op(item, event) ;
572 case ImageFrameHandleEndItem:
573 imageframe_end_handle_op(item, event) ;
576 case MarkerViewHandleStartItem:
577 markerview_item_start_handle_op(item, event) ;
580 case MarkerViewHandleEndItem:
581 markerview_item_end_handle_op(item, event) ;
584 /* </CMT Additions> */
586 /* <CMT Additions> */
588 start_markerview_grab(item, event) ;
591 start_imageframe_grab(item, event) ;
593 /* </CMT Additions> */
609 // start_line_grab_from_regionview (item, event);
612 case GainControlPointItem:
613 start_control_point_grab (item, event);
617 start_line_grab_from_line (item, event);
620 case GainAutomationControlPointItem:
621 case PanAutomationControlPointItem:
622 case RedirectAutomationControlPointItem:
623 start_control_point_grab (item, event);
634 case GainAutomationControlPointItem:
635 case PanAutomationControlPointItem:
636 case RedirectAutomationControlPointItem:
637 start_control_point_grab (item, event);
640 case GainAutomationLineItem:
641 case PanAutomationLineItem:
642 case RedirectAutomationLineItem:
643 start_line_grab_from_line (item, event);
647 // XXX need automation mode to identify which
649 // start_line_grab_from_regionview (item, event);
659 if (event->type == GDK_BUTTON_PRESS) {
660 start_mouse_zoom (item, event);
667 if (item_type == RegionItem) {
668 start_time_fx (item, event);
673 /* handled in release */
682 switch (mouse_mode) {
684 if (event->type == GDK_BUTTON_PRESS) {
687 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
688 start_region_copy_grab (item, event);
690 start_region_grab (item, event);
694 case GainAutomationControlPointItem:
695 case PanAutomationControlPointItem:
696 case RedirectAutomationControlPointItem:
697 start_control_point_grab (item, event);
708 case RegionViewNameHighlight:
709 start_trim (item, event);
714 start_trim (clicked_regionview->get_name_highlight(), event);
725 if (event->type == GDK_BUTTON_PRESS) {
726 /* relax till release */
733 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
734 temporal_zoom_session();
736 temporal_zoom_to_frame (true, event_frame(event));
751 switch (mouse_mode) {
753 //temporal_zoom_to_frame (true, where);
754 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
755 temporal_zoom_to_frame (true, where);
758 temporal_zoom_step (true);
763 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
764 scroll_backward (0.6f);
767 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
768 scroll_tracks_up_line ();
770 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
771 if (clicked_trackview) {
772 if (!current_stepping_trackview) {
773 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
774 current_stepping_trackview = clicked_trackview;
776 gettimeofday (&last_track_height_step_timestamp, 0);
777 current_stepping_trackview->step_height (true);
780 else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
781 temporal_zoom_to_frame (true, where);
788 switch (mouse_mode) {
790 // temporal_zoom_to_frame (false, where);
791 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
792 temporal_zoom_to_frame (false, where);
795 temporal_zoom_step (false);
800 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
801 scroll_forward (0.6f);
804 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
805 scroll_tracks_down_line ();
807 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
808 if (clicked_trackview) {
809 if (!current_stepping_trackview) {
810 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
811 current_stepping_trackview = clicked_trackview;
813 gettimeofday (&last_track_height_step_timestamp, 0);
814 current_stepping_trackview->step_height (false);
816 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
817 temporal_zoom_to_frame (false, where);
832 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
834 nframes_t where = event_frame (event, 0, 0);
836 /* no action if we're recording */
838 if (session && session->actively_recording()) {
842 /* first, see if we're finishing a drag ... */
844 if (drag_info.item) {
845 if (end_grab (item, event)) {
846 /* grab dragged, so do nothing else */
851 button_selection (item, event, item_type);
853 /* edit events get handled here */
855 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
861 case TempoMarkerItem:
862 edit_tempo_marker (item);
865 case MeterMarkerItem:
866 edit_meter_marker (item);
870 if (clicked_regionview->name_active()) {
871 return mouse_rename_region (item, event);
881 /* context menu events get handled here */
883 if (Keyboard::is_context_menu_event (&event->button)) {
885 if (drag_info.item == 0) {
887 /* no matter which button pops up the context menu, tell the menu
888 widget to use button 1 to drive menu selection.
893 case FadeInHandleItem:
895 case FadeOutHandleItem:
896 popup_fade_context_menu (1, event->button.time, item, item_type);
900 popup_track_context_menu (1, event->button.time, item_type, false, where);
904 case RegionViewNameHighlight:
906 popup_track_context_menu (1, event->button.time, item_type, false, where);
910 popup_track_context_menu (1, event->button.time, item_type, true, where);
913 case AutomationTrackItem:
914 popup_track_context_menu (1, event->button.time, item_type, false, where);
918 case RangeMarkerBarItem:
919 case TransportMarkerBarItem:
922 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
926 marker_context_menu (&event->button, item);
929 case TempoMarkerItem:
930 tm_marker_context_menu (&event->button, item);
933 case MeterMarkerItem:
934 tm_marker_context_menu (&event->button, item);
937 case CrossfadeViewItem:
938 popup_track_context_menu (1, event->button.time, item_type, false, where);
941 /* <CMT Additions> */
943 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
945 case ImageFrameTimeAxisItem:
946 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
949 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
951 case MarkerTimeAxisItem:
952 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
954 /* <CMT Additions> */
965 /* delete events get handled here */
967 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
970 case TempoMarkerItem:
971 remove_tempo_marker (item);
974 case MeterMarkerItem:
975 remove_meter_marker (item);
979 remove_marker (*item, event);
983 if (mouse_mode == MouseObject) {
984 remove_clicked_region ();
988 case GainControlPointItem:
989 if (mouse_mode == MouseGain) {
990 remove_gain_control_point (item, event);
994 case GainAutomationControlPointItem:
995 case PanAutomationControlPointItem:
996 case RedirectAutomationControlPointItem:
997 remove_control_point (item, event);
1006 switch (event->button.button) {
1009 switch (item_type) {
1010 /* see comments in button_press_handler */
1011 case EditCursorItem:
1012 case PlayheadCursorItem:
1015 case GainAutomationLineItem:
1016 case PanAutomationLineItem:
1017 case RedirectAutomationLineItem:
1018 case StartSelectionTrimItem:
1019 case EndSelectionTrimItem:
1023 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1024 snap_to (where, 0, true);
1026 mouse_add_new_marker (where);
1030 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1033 mouse_add_new_tempo_event (where);
1037 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1045 switch (mouse_mode) {
1047 switch (item_type) {
1048 case AutomationTrackItem:
1049 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1063 // Gain only makes sense for audio regions
1064 if ( ! dynamic_cast<AudioRegionView*>(clicked_regionview))
1067 switch (item_type) {
1069 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1073 case AutomationTrackItem:
1074 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1075 add_automation_event (item, event, where, event->button.y);
1084 switch (item_type) {
1086 audition_selected_region ();
1103 switch (mouse_mode) {
1106 switch (item_type) {
1108 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1110 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1113 // Button2 click is unused
1126 // x_style_paste (where, 1.0);
1146 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1152 switch (item_type) {
1153 case GainControlPointItem:
1154 if (mouse_mode == MouseGain) {
1155 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1156 cp->set_visible (true);
1160 at_y = cp->get_y ();
1161 cp->item->i2w (at_x, at_y);
1165 fraction = 1.0 - (cp->get_y() / cp->line.height());
1167 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1168 show_verbose_canvas_cursor ();
1170 if (is_drawable()) {
1171 track_canvas.get_window()->set_cursor (*fader_cursor);
1176 case GainAutomationControlPointItem:
1177 case PanAutomationControlPointItem:
1178 case RedirectAutomationControlPointItem:
1179 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1180 cp->set_visible (true);
1184 at_y = cp->get_y ();
1185 cp->item->i2w (at_x, at_y);
1189 fraction = 1.0 - (cp->get_y() / cp->line.height());
1191 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1192 show_verbose_canvas_cursor ();
1194 if (is_drawable()) {
1195 track_canvas.get_window()->set_cursor (*fader_cursor);
1200 if (mouse_mode == MouseGain) {
1201 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1203 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1204 if (is_drawable()) {
1205 track_canvas.get_window()->set_cursor (*fader_cursor);
1210 case GainAutomationLineItem:
1211 case RedirectAutomationLineItem:
1212 case PanAutomationLineItem:
1214 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1216 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1218 if (is_drawable()) {
1219 track_canvas.get_window()->set_cursor (*fader_cursor);
1223 case RegionViewNameHighlight:
1224 if (is_drawable() && mouse_mode == MouseObject) {
1225 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1229 case StartSelectionTrimItem:
1230 case EndSelectionTrimItem:
1231 /* <CMT Additions> */
1232 case ImageFrameHandleStartItem:
1233 case ImageFrameHandleEndItem:
1234 case MarkerViewHandleStartItem:
1235 case MarkerViewHandleEndItem:
1236 /* </CMT Additions> */
1238 if (is_drawable()) {
1239 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1243 case EditCursorItem:
1244 case PlayheadCursorItem:
1245 if (is_drawable()) {
1246 track_canvas.get_window()->set_cursor (*grabber_cursor);
1250 case RegionViewName:
1252 /* when the name is not an active item, the entire name highlight is for trimming */
1254 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1255 if (mouse_mode == MouseObject && is_drawable()) {
1256 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1262 case AutomationTrackItem:
1263 if (is_drawable()) {
1264 Gdk::Cursor *cursor;
1265 switch (mouse_mode) {
1267 cursor = selector_cursor;
1270 cursor = zoom_cursor;
1273 cursor = cross_hair_cursor;
1277 track_canvas.get_window()->set_cursor (*cursor);
1279 AutomationTimeAxisView* atv;
1280 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1281 clear_entered_track = false;
1282 set_entered_track (atv);
1288 case RangeMarkerBarItem:
1289 case TransportMarkerBarItem:
1292 if (is_drawable()) {
1293 time_canvas.get_window()->set_cursor (*timebar_cursor);
1298 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1301 marker->set_color_rgba (color_map[cEnteredMarker]);
1303 case MeterMarkerItem:
1304 case TempoMarkerItem:
1305 if (is_drawable()) {
1306 time_canvas.get_window()->set_cursor (*timebar_cursor);
1309 case FadeInHandleItem:
1310 case FadeOutHandleItem:
1311 if (mouse_mode == MouseObject) {
1312 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1314 rect->property_fill_color_rgba() = 0;
1315 rect->property_outline_pixels() = 1;
1324 /* second pass to handle entered track status in a comprehensible way.
1327 switch (item_type) {
1329 case GainAutomationLineItem:
1330 case RedirectAutomationLineItem:
1331 case PanAutomationLineItem:
1332 case GainControlPointItem:
1333 case GainAutomationControlPointItem:
1334 case PanAutomationControlPointItem:
1335 case RedirectAutomationControlPointItem:
1336 /* these do not affect the current entered track state */
1337 clear_entered_track = false;
1340 case AutomationTrackItem:
1341 /* handled above already */
1345 set_entered_track (0);
1353 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1362 switch (item_type) {
1363 case GainControlPointItem:
1364 case GainAutomationControlPointItem:
1365 case PanAutomationControlPointItem:
1366 case RedirectAutomationControlPointItem:
1367 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1368 if (cp->line.npoints() > 1) {
1369 if (!cp->selected) {
1370 cp->set_visible (false);
1374 if (is_drawable()) {
1375 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1378 hide_verbose_canvas_cursor ();
1381 case RegionViewNameHighlight:
1382 case StartSelectionTrimItem:
1383 case EndSelectionTrimItem:
1384 case EditCursorItem:
1385 case PlayheadCursorItem:
1386 /* <CMT Additions> */
1387 case ImageFrameHandleStartItem:
1388 case ImageFrameHandleEndItem:
1389 case MarkerViewHandleStartItem:
1390 case MarkerViewHandleEndItem:
1391 /* </CMT Additions> */
1392 if (is_drawable()) {
1393 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1398 case GainAutomationLineItem:
1399 case RedirectAutomationLineItem:
1400 case PanAutomationLineItem:
1401 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1403 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1405 line->property_fill_color_rgba() = al->get_line_color();
1407 if (is_drawable()) {
1408 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1412 case RegionViewName:
1413 /* see enter_handler() for notes */
1414 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1415 if (is_drawable() && mouse_mode == MouseObject) {
1416 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1421 case RangeMarkerBarItem:
1422 case TransportMarkerBarItem:
1426 if (is_drawable()) {
1427 time_canvas.get_window()->set_cursor (*timebar_cursor);
1432 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1435 loc = find_location_from_marker (marker, is_start);
1436 if (loc) location_flags_changed (loc, this);
1438 case MeterMarkerItem:
1439 case TempoMarkerItem:
1441 if (is_drawable()) {
1442 time_canvas.get_window()->set_cursor (*timebar_cursor);
1447 case FadeInHandleItem:
1448 case FadeOutHandleItem:
1449 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1451 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1453 rect->property_fill_color_rgba() = rv->get_fill_color();
1454 rect->property_outline_pixels() = 0;
1459 case AutomationTrackItem:
1460 if (is_drawable()) {
1461 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1462 clear_entered_track = true;
1463 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1475 Editor::left_automation_track ()
1477 if (clear_entered_track) {
1478 set_entered_track (0);
1479 clear_entered_track = false;
1485 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1489 /* We call this so that MOTION_NOTIFY events continue to be
1490 delivered to the canvas. We need to do this because we set
1491 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1492 the density of the events, at the expense of a round-trip
1493 to the server. Given that this will mostly occur on cases
1494 where DISPLAY = :0.0, and given the cost of what the motion
1495 event might do, its a good tradeoff.
1498 track_canvas.get_pointer (x, y);
1500 if (current_stepping_trackview) {
1501 /* don't keep the persistent stepped trackview if the mouse moves */
1502 current_stepping_trackview = 0;
1503 step_timeout.disconnect ();
1506 if (session && session->actively_recording()) {
1507 /* Sorry. no dragging stuff around while we record */
1511 drag_info.item_type = item_type;
1512 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1513 &drag_info.current_pointer_y);
1515 if (!from_autoscroll && drag_info.item) {
1516 /* item != 0 is the best test i can think of for dragging.
1518 if (!drag_info.move_threshold_passed) {
1520 drag_info.move_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1522 // and change the initial grab loc/frame if this drag info wants us to
1524 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1525 drag_info.grab_frame = drag_info.current_pointer_frame;
1526 drag_info.grab_x = drag_info.current_pointer_x;
1527 drag_info.grab_y = drag_info.current_pointer_y;
1528 drag_info.last_pointer_frame = drag_info.grab_frame;
1529 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1534 switch (item_type) {
1535 case PlayheadCursorItem:
1536 case EditCursorItem:
1538 case GainControlPointItem:
1539 case RedirectAutomationControlPointItem:
1540 case GainAutomationControlPointItem:
1541 case PanAutomationControlPointItem:
1542 case TempoMarkerItem:
1543 case MeterMarkerItem:
1544 case RegionViewNameHighlight:
1545 case StartSelectionTrimItem:
1546 case EndSelectionTrimItem:
1549 case RedirectAutomationLineItem:
1550 case GainAutomationLineItem:
1551 case PanAutomationLineItem:
1552 case FadeInHandleItem:
1553 case FadeOutHandleItem:
1554 /* <CMT Additions> */
1555 case ImageFrameHandleStartItem:
1556 case ImageFrameHandleEndItem:
1557 case MarkerViewHandleStartItem:
1558 case MarkerViewHandleEndItem:
1559 /* </CMT Additions> */
1560 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1561 (event->motion.state & Gdk::BUTTON2_MASK))) {
1562 if (!from_autoscroll) {
1563 maybe_autoscroll (event);
1565 (this->*(drag_info.motion_callback)) (item, event);
1574 switch (mouse_mode) {
1579 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1580 (event->motion.state & GDK_BUTTON2_MASK))) {
1581 if (!from_autoscroll) {
1582 maybe_autoscroll (event);
1584 (this->*(drag_info.motion_callback)) (item, event);
1595 track_canvas_motion (event);
1596 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1604 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1606 if (drag_info.item == 0) {
1607 fatal << _("programming error: start_grab called without drag item") << endmsg;
1613 cursor = grabber_cursor;
1616 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1618 if (event->button.button == 2) {
1619 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1620 drag_info.y_constrained = true;
1621 drag_info.x_constrained = false;
1623 drag_info.y_constrained = false;
1624 drag_info.x_constrained = true;
1627 drag_info.x_constrained = false;
1628 drag_info.y_constrained = false;
1631 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1632 drag_info.last_pointer_frame = drag_info.grab_frame;
1633 drag_info.current_pointer_frame = drag_info.grab_frame;
1634 drag_info.current_pointer_x = drag_info.grab_x;
1635 drag_info.current_pointer_y = drag_info.grab_y;
1636 drag_info.cumulative_x_drag = 0;
1637 drag_info.cumulative_y_drag = 0;
1638 drag_info.first_move = true;
1639 drag_info.move_threshold_passed = false;
1640 drag_info.want_move_threshold = false;
1641 drag_info.pointer_frame_offset = 0;
1642 drag_info.brushing = false;
1643 drag_info.copied_location = 0;
1645 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1647 event->button.time);
1649 if (session && session->transport_rolling()) {
1650 drag_info.was_rolling = true;
1652 drag_info.was_rolling = false;
1655 switch (snap_type) {
1656 case SnapToRegionStart:
1657 case SnapToRegionEnd:
1658 case SnapToRegionSync:
1659 case SnapToRegionBoundary:
1660 build_region_boundary_cache ();
1668 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1670 drag_info.item->ungrab (0);
1671 drag_info.item = new_item;
1674 cursor = grabber_cursor;
1677 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1681 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1683 bool did_drag = false;
1685 stop_canvas_autoscroll ();
1687 if (drag_info.item == 0) {
1691 drag_info.item->ungrab (event->button.time);
1693 if (drag_info.finished_callback) {
1694 (this->*(drag_info.finished_callback)) (item, event);
1697 did_drag = !drag_info.first_move;
1699 hide_verbose_canvas_cursor();
1702 drag_info.copy = false;
1703 drag_info.motion_callback = 0;
1704 drag_info.finished_callback = 0;
1705 drag_info.last_trackview = 0;
1706 drag_info.last_frame_position = 0;
1707 drag_info.grab_frame = 0;
1708 drag_info.last_pointer_frame = 0;
1709 drag_info.current_pointer_frame = 0;
1710 drag_info.brushing = false;
1712 if (drag_info.copied_location) {
1713 delete drag_info.copied_location;
1714 drag_info.copied_location = 0;
1721 Editor::set_edit_cursor (GdkEvent* event)
1723 nframes_t pointer_frame = event_frame (event);
1725 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1726 if (snap_type != SnapToEditCursor) {
1727 snap_to (pointer_frame);
1731 edit_cursor->set_position (pointer_frame);
1732 edit_cursor_clock.set (pointer_frame);
1736 Editor::set_playhead_cursor (GdkEvent* event)
1738 nframes_t pointer_frame = event_frame (event);
1740 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1741 snap_to (pointer_frame);
1745 session->request_locate (pointer_frame, session->transport_rolling());
1750 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1752 drag_info.item = item;
1753 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1754 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1758 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1759 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1763 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1765 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1769 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1771 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1773 nframes_t fade_length;
1775 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1776 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1782 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1786 if (pos < (arv->region()->position() + 64)) {
1787 fade_length = 64; // this should be a minimum defined somewhere
1788 } else if (pos > arv->region()->last_frame()) {
1789 fade_length = arv->region()->length();
1791 fade_length = pos - arv->region()->position();
1793 /* mapover the region selection */
1795 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1797 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1803 tmp->reset_fade_in_shape_width (fade_length);
1806 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1808 drag_info.first_move = false;
1812 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1814 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1816 nframes_t fade_length;
1818 if (drag_info.first_move) return;
1820 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1821 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1826 if (pos < (arv->region()->position() + 64)) {
1827 fade_length = 64; // this should be a minimum defined somewhere
1828 } else if (pos > arv->region()->last_frame()) {
1829 fade_length = arv->region()->length();
1831 fade_length = pos - arv->region()->position();
1834 begin_reversible_command (_("change fade in length"));
1836 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1838 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1844 AutomationList& alist = tmp->audio_region()->fade_in();
1845 XMLNode &before = alist.get_state();
1847 tmp->audio_region()->set_fade_in_length (fade_length);
1849 XMLNode &after = alist.get_state();
1850 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1853 commit_reversible_command ();
1857 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1859 drag_info.item = item;
1860 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1861 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1865 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1866 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1870 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1872 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1876 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1878 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1880 nframes_t fade_length;
1882 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1883 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1889 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1893 if (pos > (arv->region()->last_frame() - 64)) {
1894 fade_length = 64; // this should really be a minimum fade defined somewhere
1896 else if (pos < arv->region()->position()) {
1897 fade_length = arv->region()->length();
1900 fade_length = arv->region()->last_frame() - pos;
1903 /* mapover the region selection */
1905 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1907 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1913 tmp->reset_fade_out_shape_width (fade_length);
1916 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1918 drag_info.first_move = false;
1922 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1924 if (drag_info.first_move) return;
1926 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1928 nframes_t fade_length;
1930 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1931 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1937 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1941 if (pos > (arv->region()->last_frame() - 64)) {
1942 fade_length = 64; // this should really be a minimum fade defined somewhere
1944 else if (pos < arv->region()->position()) {
1945 fade_length = arv->region()->length();
1948 fade_length = arv->region()->last_frame() - pos;
1951 begin_reversible_command (_("change fade out length"));
1953 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1955 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1961 AutomationList& alist = tmp->audio_region()->fade_out();
1962 XMLNode &before = alist.get_state();
1964 tmp->audio_region()->set_fade_out_length (fade_length);
1966 XMLNode &after = alist.get_state();
1967 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1970 commit_reversible_command ();
1974 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1976 drag_info.item = item;
1977 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1978 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1982 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1983 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1987 Cursor* cursor = (Cursor *) drag_info.data;
1989 if (cursor == playhead_cursor) {
1990 _dragging_playhead = true;
1992 if (session && drag_info.was_rolling) {
1993 session->request_stop ();
1997 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1999 show_verbose_time_cursor (cursor->current_frame, 10);
2003 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2005 Cursor* cursor = (Cursor *) drag_info.data;
2006 nframes_t adjusted_frame;
2008 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2009 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2015 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2016 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
2017 snap_to (adjusted_frame);
2021 if (adjusted_frame == drag_info.last_pointer_frame) return;
2023 cursor->set_position (adjusted_frame);
2025 if (cursor == edit_cursor) {
2026 edit_cursor_clock.set (cursor->current_frame);
2028 UpdateAllTransportClocks (cursor->current_frame);
2031 show_verbose_time_cursor (cursor->current_frame, 10);
2033 drag_info.last_pointer_frame = adjusted_frame;
2034 drag_info.first_move = false;
2038 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2040 if (drag_info.first_move) return;
2042 cursor_drag_motion_callback (item, event);
2044 _dragging_playhead = false;
2046 if (item == &playhead_cursor->canvas_item) {
2048 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2050 } else if (item == &edit_cursor->canvas_item) {
2051 edit_cursor->set_position (edit_cursor->current_frame);
2052 edit_cursor_clock.set (edit_cursor->current_frame);
2057 Editor::update_marker_drag_item (Location *location)
2059 double x1 = frame_to_pixel (location->start());
2060 double x2 = frame_to_pixel (location->end());
2062 if (location->is_mark()) {
2063 marker_drag_line_points.front().set_x(x1);
2064 marker_drag_line_points.back().set_x(x1);
2065 marker_drag_line->property_points() = marker_drag_line_points;
2068 range_marker_drag_rect->property_x1() = x1;
2069 range_marker_drag_rect->property_x2() = x2;
2074 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2078 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2079 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2085 Location *location = find_location_from_marker (marker, is_start);
2087 drag_info.item = item;
2088 drag_info.data = marker;
2089 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2090 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2094 drag_info.copied_location = new Location (*location);
2095 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2097 update_marker_drag_item (location);
2099 if (location->is_mark()) {
2100 marker_drag_line->show();
2101 marker_drag_line->raise_to_top();
2104 range_marker_drag_rect->show();
2105 range_marker_drag_rect->raise_to_top();
2108 if (is_start) show_verbose_time_cursor (location->start(), 10);
2109 else show_verbose_time_cursor (location->end(), 10);
2113 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2116 Marker* marker = (Marker *) drag_info.data;
2117 Location *real_location;
2118 Location *copy_location;
2120 bool move_both = false;
2124 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2125 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2131 nframes_t next = newframe;
2133 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2134 snap_to (newframe, 0, true);
2137 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2141 /* call this to find out if its the start or end */
2143 real_location = find_location_from_marker (marker, is_start);
2145 /* use the copy that we're "dragging" around */
2147 copy_location = drag_info.copied_location;
2149 f_delta = copy_location->end() - copy_location->start();
2151 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2155 if (copy_location->is_mark()) {
2158 copy_location->set_start (newframe);
2162 if (is_start) { // start-of-range marker
2165 copy_location->set_start (newframe);
2166 copy_location->set_end (newframe + f_delta);
2167 } else if (newframe < copy_location->end()) {
2168 copy_location->set_start (newframe);
2170 snap_to (next, 1, true);
2171 copy_location->set_end (next);
2172 copy_location->set_start (newframe);
2175 } else { // end marker
2178 copy_location->set_end (newframe);
2179 copy_location->set_start (newframe - f_delta);
2180 } else if (newframe > copy_location->start()) {
2181 copy_location->set_end (newframe);
2183 } else if (newframe > 0) {
2184 snap_to (next, -1, true);
2185 copy_location->set_start (next);
2186 copy_location->set_end (newframe);
2191 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2192 drag_info.first_move = false;
2194 update_marker_drag_item (copy_location);
2196 LocationMarkers* lm = find_location_markers (real_location);
2197 lm->set_position (copy_location->start(), copy_location->end());
2199 show_verbose_time_cursor (newframe, 10);
2203 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2205 if (drag_info.first_move) {
2206 marker_drag_motion_callback (item, event);
2210 Marker* marker = (Marker *) drag_info.data;
2214 begin_reversible_command ( _("move marker") );
2215 XMLNode &before = session->locations()->get_state();
2217 Location * location = find_location_from_marker (marker, is_start);
2220 if (location->is_mark()) {
2221 location->set_start (drag_info.copied_location->start());
2223 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2227 XMLNode &after = session->locations()->get_state();
2228 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2229 commit_reversible_command ();
2231 marker_drag_line->hide();
2232 range_marker_drag_rect->hide();
2236 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2239 MeterMarker* meter_marker;
2241 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2242 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2246 meter_marker = dynamic_cast<MeterMarker*> (marker);
2248 MetricSection& section (meter_marker->meter());
2250 if (!section.movable()) {
2254 drag_info.item = item;
2255 drag_info.data = marker;
2256 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2257 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2261 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2263 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2267 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2270 MeterMarker* meter_marker;
2272 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2273 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2277 meter_marker = dynamic_cast<MeterMarker*> (marker);
2279 // create a dummy marker for visual representation of moving the copy.
2280 // The actual copying is not done before we reach the finish callback.
2282 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2283 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2284 *new MeterSection(meter_marker->meter()));
2286 drag_info.item = &new_marker->the_item();
2287 drag_info.copy = true;
2288 drag_info.data = new_marker;
2289 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2290 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2294 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2296 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2300 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2302 MeterMarker* marker = (MeterMarker *) drag_info.data;
2303 nframes_t adjusted_frame;
2305 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2306 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2312 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2313 snap_to (adjusted_frame);
2316 if (adjusted_frame == drag_info.last_pointer_frame) return;
2318 marker->set_position (adjusted_frame);
2321 drag_info.last_pointer_frame = adjusted_frame;
2322 drag_info.first_move = false;
2324 show_verbose_time_cursor (adjusted_frame, 10);
2328 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2330 if (drag_info.first_move) return;
2332 meter_marker_drag_motion_callback (drag_info.item, event);
2334 MeterMarker* marker = (MeterMarker *) drag_info.data;
2337 TempoMap& map (session->tempo_map());
2338 map.bbt_time (drag_info.last_pointer_frame, when);
2340 if (drag_info.copy == true) {
2341 begin_reversible_command (_("copy meter mark"));
2342 XMLNode &before = map.get_state();
2343 map.add_meter (marker->meter(), when);
2344 XMLNode &after = map.get_state();
2345 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2346 commit_reversible_command ();
2348 // delete the dummy marker we used for visual representation of copying.
2349 // a new visual marker will show up automatically.
2352 begin_reversible_command (_("move meter mark"));
2353 XMLNode &before = map.get_state();
2354 map.move_meter (marker->meter(), when);
2355 XMLNode &after = map.get_state();
2356 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2357 commit_reversible_command ();
2362 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2365 TempoMarker* tempo_marker;
2367 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2368 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2372 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2373 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2377 MetricSection& section (tempo_marker->tempo());
2379 if (!section.movable()) {
2383 drag_info.item = item;
2384 drag_info.data = marker;
2385 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2386 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2390 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2391 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2395 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2398 TempoMarker* tempo_marker;
2400 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2401 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2405 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2406 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2410 // create a dummy marker for visual representation of moving the copy.
2411 // The actual copying is not done before we reach the finish callback.
2413 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2414 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2415 *new TempoSection(tempo_marker->tempo()));
2417 drag_info.item = &new_marker->the_item();
2418 drag_info.copy = true;
2419 drag_info.data = new_marker;
2420 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2421 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2425 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2427 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2431 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2433 TempoMarker* marker = (TempoMarker *) drag_info.data;
2434 nframes_t adjusted_frame;
2436 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2437 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2443 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2444 snap_to (adjusted_frame);
2447 if (adjusted_frame == drag_info.last_pointer_frame) return;
2449 /* OK, we've moved far enough to make it worth actually move the thing. */
2451 marker->set_position (adjusted_frame);
2453 show_verbose_time_cursor (adjusted_frame, 10);
2455 drag_info.last_pointer_frame = adjusted_frame;
2456 drag_info.first_move = false;
2460 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2462 if (drag_info.first_move) return;
2464 tempo_marker_drag_motion_callback (drag_info.item, event);
2466 TempoMarker* marker = (TempoMarker *) drag_info.data;
2469 TempoMap& map (session->tempo_map());
2470 map.bbt_time (drag_info.last_pointer_frame, when);
2472 if (drag_info.copy == true) {
2473 begin_reversible_command (_("copy tempo mark"));
2474 XMLNode &before = map.get_state();
2475 map.add_tempo (marker->tempo(), when);
2476 XMLNode &after = map.get_state();
2477 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2478 commit_reversible_command ();
2480 // delete the dummy marker we used for visual representation of copying.
2481 // a new visual marker will show up automatically.
2484 begin_reversible_command (_("move tempo mark"));
2485 XMLNode &before = map.get_state();
2486 map.move_tempo (marker->tempo(), when);
2487 XMLNode &after = map.get_state();
2488 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2489 commit_reversible_command ();
2494 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2496 ControlPoint* control_point;
2498 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2499 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2503 // We shouldn't remove the first or last gain point
2504 if (control_point->line.is_last_point(*control_point) ||
2505 control_point->line.is_first_point(*control_point)) {
2509 control_point->line.remove_point (*control_point);
2513 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2515 ControlPoint* control_point;
2517 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2518 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2522 control_point->line.remove_point (*control_point);
2526 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2528 ControlPoint* control_point;
2530 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2531 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2535 drag_info.item = item;
2536 drag_info.data = control_point;
2537 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2538 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2540 start_grab (event, fader_cursor);
2542 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2544 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2545 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2546 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2548 show_verbose_canvas_cursor ();
2552 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2554 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2556 double cx = drag_info.current_pointer_x;
2557 double cy = drag_info.current_pointer_y;
2559 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2560 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2562 if (drag_info.x_constrained) {
2563 cx = drag_info.grab_x;
2565 if (drag_info.y_constrained) {
2566 cy = drag_info.grab_y;
2569 cp->line.parent_group().w2i (cx, cy);
2573 cy = min ((double) cp->line.height(), cy);
2575 //translate cx to frames
2576 nframes_t cx_frames = unit_to_frame (cx);
2578 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2579 snap_to (cx_frames);
2582 float fraction = 1.0 - (cy / cp->line.height());
2586 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2592 cp->line.point_drag (*cp, cx_frames , fraction, push);
2594 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2596 drag_info.first_move = false;
2600 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2602 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2604 if (drag_info.first_move) {
2608 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2609 reset_point_selection ();
2613 control_point_drag_motion_callback (item, event);
2615 cp->line.end_drag (cp);
2619 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2621 switch (mouse_mode) {
2623 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2624 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2632 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2636 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2637 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2641 start_line_grab (al, event);
2645 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2649 nframes_t frame_within_region;
2651 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2655 cx = event->button.x;
2656 cy = event->button.y;
2657 line->parent_group().w2i (cx, cy);
2658 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2660 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2661 current_line_drag_info.after)) {
2662 /* no adjacent points */
2666 drag_info.item = &line->grab_item();
2667 drag_info.data = line;
2668 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2669 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2671 start_grab (event, fader_cursor);
2673 double fraction = 1.0 - (cy / line->height());
2675 line->start_drag (0, drag_info.grab_frame, fraction);
2677 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2678 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2679 show_verbose_canvas_cursor ();
2683 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2685 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2686 double cx = drag_info.current_pointer_x;
2687 double cy = drag_info.current_pointer_y;
2689 line->parent_group().w2i (cx, cy);
2692 fraction = 1.0 - (cy / line->height());
2696 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2702 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2704 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2708 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2710 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2711 line_drag_motion_callback (item, event);
2716 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2718 if (selection->regions.empty() || clicked_regionview == 0) {
2722 drag_info.copy = false;
2723 drag_info.item = item;
2724 drag_info.data = clicked_regionview;
2725 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2726 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2731 TimeAxisView* tvp = clicked_trackview;
2732 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2734 if (tv && tv->is_audio_track()) {
2735 speed = tv->get_diskstream()->speed();
2738 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2739 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2740 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2741 // we want a move threshold
2742 drag_info.want_move_threshold = true;
2744 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2746 begin_reversible_command (_("move region(s)"));
2750 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2752 if (selection->regions.empty() || clicked_regionview == 0) {
2756 drag_info.copy = true;
2757 drag_info.item = item;
2758 drag_info.data = clicked_regionview;
2762 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2763 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2766 if (atv && atv->is_audio_track()) {
2767 speed = atv->get_diskstream()->speed();
2770 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2771 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2772 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2773 // we want a move threshold
2774 drag_info.want_move_threshold = true;
2775 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2776 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2777 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2781 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2783 if (selection->regions.empty() || clicked_regionview == 0) {
2787 drag_info.copy = false;
2788 drag_info.item = item;
2789 drag_info.data = clicked_regionview;
2790 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2791 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2796 TimeAxisView* tvp = clicked_trackview;
2797 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2799 if (tv && tv->is_audio_track()) {
2800 speed = tv->get_diskstream()->speed();
2803 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2804 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2805 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2806 // we want a move threshold
2807 drag_info.want_move_threshold = true;
2808 drag_info.brushing = true;
2810 begin_reversible_command (_("Drag region brush"));
2814 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2818 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2819 nframes_t pending_region_position = 0;
2820 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2821 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2822 bool clamp_y_axis = false;
2823 vector<int32_t> height_list(512) ;
2824 vector<int32_t>::iterator j;
2826 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2828 drag_info.want_move_threshold = false; // don't copy again
2830 /* this is committed in the grab finished callback. */
2832 begin_reversible_command (_("Drag region copy"));
2834 /* duplicate the region(s) */
2836 vector<RegionView*> new_regionviews;
2838 set<boost::shared_ptr<Playlist> > affected_playlists;
2839 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2841 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2846 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
2847 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2849 insert_result = affected_playlists.insert (to_playlist);
2850 if (insert_result.second) {
2851 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
2854 latest_regionview = 0;
2856 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2858 /* create a new region with the same name. */
2860 // FIXME: ew. need a (virtual) Region::duplicate() or something?
2862 boost::shared_ptr<Region> newregion;
2863 boost::shared_ptr<Region> ar;
2865 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
2866 newregion = RegionFactory::create (ar);
2868 assert(newregion != 0);
2870 /* if the original region was locked, we don't care */
2872 newregion->set_locked (false);
2874 to_playlist->add_region (newregion, (nframes_t) (rv->region()->position() * atv->get_diskstream()->speed()));
2878 if (latest_regionview) {
2879 new_regionviews.push_back (latest_regionview);
2885 if (new_regionviews.empty()) {
2889 /* reset selection to new regionviews */
2891 selection->set (new_regionviews);
2893 /* reset drag_info data to reflect the fact that we are dragging the copies */
2895 drag_info.data = new_regionviews.front();
2896 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2899 /* Which trackview is this ? */
2901 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2902 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2904 /* The region motion is only processed if the pointer is over
2908 if (!tv || !tv->is_audio_track()) {
2909 /* To make sure we hide the verbose canvas cursor when the mouse is
2910 not held over and audiotrack.
2912 hide_verbose_canvas_cursor ();
2916 original_pointer_order = drag_info.last_trackview->order;
2918 /************************************************************
2920 ************************************************************/
2922 if (drag_info.brushing) {
2923 clamp_y_axis = true;
2928 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2930 int32_t children = 0, numtracks = 0;
2931 // XXX hard coding track limit, oh my, so very very bad
2932 bitset <1024> tracks (0x00);
2933 /* get a bitmask representing the visible tracks */
2935 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2936 TimeAxisView *tracklist_timeview;
2937 tracklist_timeview = (*i);
2938 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2939 list<TimeAxisView*> children_list;
2941 /* zeroes are audio tracks. ones are other types. */
2943 if (!atv2->hidden()) {
2945 if (visible_y_high < atv2->order) {
2946 visible_y_high = atv2->order;
2948 if (visible_y_low > atv2->order) {
2949 visible_y_low = atv2->order;
2952 if (!atv2->is_audio_track()) {
2953 tracks = tracks |= (0x01 << atv2->order);
2956 height_list[atv2->order] = (*i)->height;
2958 if ((children_list = atv2->get_child_list()).size() > 0) {
2959 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2960 tracks = tracks |= (0x01 << (atv2->order + children));
2961 height_list[atv2->order + children] = (*j)->height;
2969 /* find the actual span according to the canvas */
2971 canvas_pointer_y_span = pointer_y_span;
2972 if (drag_info.last_trackview->order >= tv->order) {
2974 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2975 if (height_list[y] == 0 ) {
2976 canvas_pointer_y_span--;
2981 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2982 if ( height_list[y] == 0 ) {
2983 canvas_pointer_y_span++;
2988 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2989 RegionView* rv2 = (*i);
2990 double ix1, ix2, iy1, iy2;
2993 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2994 rv2->get_canvas_group()->i2w (ix1, iy1);
2995 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2996 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2998 if (atv2->order != original_pointer_order) {
2999 /* this isn't the pointer track */
3001 if (canvas_pointer_y_span > 0) {
3003 /* moving up the canvas */
3004 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3006 int32_t visible_tracks = 0;
3007 while (visible_tracks < canvas_pointer_y_span ) {
3010 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3011 /* we're passing through a hidden track */
3016 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3017 clamp_y_axis = true;
3021 clamp_y_axis = true;
3024 } else if (canvas_pointer_y_span < 0) {
3026 /*moving down the canvas*/
3028 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3031 int32_t visible_tracks = 0;
3033 while (visible_tracks > canvas_pointer_y_span ) {
3036 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3040 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3041 clamp_y_axis = true;
3046 clamp_y_axis = true;
3052 /* this is the pointer's track */
3053 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3054 clamp_y_axis = true;
3055 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3056 clamp_y_axis = true;
3064 } else if (drag_info.last_trackview == tv) {
3065 clamp_y_axis = true;
3069 if (!clamp_y_axis) {
3070 drag_info.last_trackview = tv;
3073 /************************************************************
3075 ************************************************************/
3077 /* compute the amount of pointer motion in frames, and where
3078 the region would be if we moved it by that much.
3081 if (drag_info.move_threshold_passed) {
3083 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3085 nframes_t sync_frame;
3086 nframes_t sync_offset;
3089 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3091 sync_offset = rv->region()->sync_offset (sync_dir);
3092 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3094 /* we snap if the snap modifier is not enabled.
3097 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3098 snap_to (sync_frame);
3101 if (sync_frame - sync_offset <= sync_frame) {
3102 pending_region_position = sync_frame - (sync_dir*sync_offset);
3104 pending_region_position = 0;
3108 pending_region_position = 0;
3111 if (pending_region_position > max_frames - rv->region()->length()) {
3112 pending_region_position = drag_info.last_frame_position;
3115 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3117 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3119 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3120 to make it appear at the new location.
3123 if (pending_region_position > drag_info.last_frame_position) {
3124 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3126 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3129 drag_info.last_frame_position = pending_region_position;
3136 /* threshold not passed */
3141 /*************************************************************
3143 ************************************************************/
3145 if (x_delta == 0 && (pointer_y_span == 0)) {
3146 /* haven't reached next snap point, and we're not switching
3147 trackviews. nothing to do.
3153 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3155 RegionView* rv2 = (*i);
3157 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3159 double ix1, ix2, iy1, iy2;
3160 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3161 rv2->get_canvas_group()->i2w (ix1, iy1);
3170 /*************************************************************
3172 ************************************************************/
3174 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3175 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3177 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3179 RegionView* rv = (*i);
3180 double ix1, ix2, iy1, iy2;
3181 int32_t temp_pointer_y_span = pointer_y_span;
3183 /* get item BBox, which will be relative to parent. so we have
3184 to query on a child, then convert to world coordinates using
3188 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3189 rv->get_canvas_group()->i2w (ix1, iy1);
3190 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3191 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3192 AudioTimeAxisView* temp_atv;
3194 if ((pointer_y_span != 0) && !clamp_y_axis) {
3197 for (j = height_list.begin(); j!= height_list.end(); j++) {
3198 if (x == canvas_atv->order) {
3199 /* we found the track the region is on */
3200 if (x != original_pointer_order) {
3201 /*this isn't from the same track we're dragging from */
3202 temp_pointer_y_span = canvas_pointer_y_span;
3204 while (temp_pointer_y_span > 0) {
3205 /* we're moving up canvas-wise,
3206 so we need to find the next track height
3208 if (j != height_list.begin()) {
3211 if (x != original_pointer_order) {
3212 /* we're not from the dragged track, so ignore hidden tracks. */
3214 temp_pointer_y_span++;
3218 temp_pointer_y_span--;
3220 while (temp_pointer_y_span < 0) {
3222 if (x != original_pointer_order) {
3224 temp_pointer_y_span--;
3228 if (j != height_list.end()) {
3231 temp_pointer_y_span++;
3233 /* find out where we'll be when we move and set height accordingly */
3235 tvp2 = trackview_by_y_position (iy1 + y_delta);
3236 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3237 rv->set_height (temp_atv->height);
3239 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3240 personally, i think this can confuse things, but never mind.
3243 //const GdkColor& col (temp_atv->view->get_region_color());
3244 //rv->set_color (const_cast<GdkColor&>(col));
3251 /* prevent the regionview from being moved to before
3252 the zero position on the canvas.
3257 if (-x_delta > ix1) {
3260 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3261 x_delta = max_frames - rv->region()->last_frame();
3264 if (drag_info.first_move) {
3266 /* hide any dependent views */
3268 rv->get_time_axis_view().hide_dependent_views (*rv);
3270 /* this is subtle. raising the regionview itself won't help,
3271 because raise_to_top() just puts the item on the top of
3272 its parent's stack. so, we need to put the trackview canvas_display group
3273 on the top, since its parent is the whole canvas.
3276 rv->get_canvas_group()->raise_to_top();
3277 rv->get_time_axis_view().canvas_display->raise_to_top();
3278 cursor_group->raise_to_top();
3280 /* freeze the playlists from notifying till
3284 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3285 if (atv && atv->is_audio_track()) {
3286 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>(atv->get_diskstream()->playlist());
3288 /* only freeze and capture state once */
3290 insert_result = motion_frozen_playlists.insert (pl);
3291 if (insert_result.second) {
3293 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
3297 rv->region()->set_opaque(false);
3300 if (drag_info.brushing) {
3301 mouse_brush_insert_region (rv, pending_region_position);
3303 rv->move (x_delta, y_delta);
3307 if (drag_info.first_move) {
3308 cursor_group->raise_to_top();
3311 drag_info.first_move = false;
3313 if (x_delta != 0 && !drag_info.brushing) {
3314 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3320 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3323 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3324 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3325 bool nocommit = true;
3327 RouteTimeAxisView* atv;
3328 bool regionview_y_movement;
3329 bool regionview_x_movement;
3331 /* first_move is set to false if the regionview has been moved in the
3335 if (drag_info.first_move) {
3342 /* The regionview has been moved at some stage during the grab so we need
3343 to account for any mouse movement between this event and the last one.
3346 region_drag_motion_callback (item, event);
3348 if (drag_info.brushing) {
3349 /* all changes were made during motion event handlers */
3353 /* adjust for track speed */
3356 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3357 if (atv && atv->get_diskstream()) {
3358 speed = atv->get_diskstream()->speed();
3361 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3362 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3364 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3365 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3367 if (regionview_y_movement) {
3369 /* motion between tracks */
3371 list<RegionView*> new_selection;
3373 /* moved to a different audio track. */
3375 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3377 RegionView* rv2 = (*i);
3379 /* the region that used to be in the old playlist is not
3380 moved to the new one - we make a copy of it. as a result,
3381 any existing editor for the region should no longer be
3385 if (!drag_info.copy) {
3386 rv2->hide_region_editor();
3388 new_selection.push_back (rv2);
3392 /* first, freeze the target tracks */
3394 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3396 boost::shared_ptr<Playlist> from_playlist;
3397 boost::shared_ptr<Playlist> to_playlist;
3399 double ix1, ix2, iy1, iy2;
3401 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3402 (*i)->get_canvas_group()->i2w (ix1, iy1);
3403 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3404 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3406 (*i)->region()->set_opaque (true);
3408 from_playlist = (*i)->region()->playlist();
3409 to_playlist = atv2->playlist();
3411 /* the from_playlist was frozen in the "first_move" case
3412 of the motion handler. the insert can fail,
3413 but that doesn't matter. it just means
3414 we already have the playlist in the list.
3417 motion_frozen_playlists.insert (from_playlist);
3419 /* only freeze the to_playlist once */
3421 insert_result = motion_frozen_playlists.insert(to_playlist);
3422 if (insert_result.second) {
3423 to_playlist->freeze();
3424 session->add_command(new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3429 /* now do it again with the actual operations */
3431 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3433 boost::shared_ptr<Playlist> from_playlist;
3434 boost::shared_ptr<Playlist> to_playlist;
3436 double ix1, ix2, iy1, iy2;
3438 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3439 (*i)->get_canvas_group()->i2w (ix1, iy1);
3440 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3441 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3443 from_playlist = (*i)->region()->playlist();
3444 to_playlist = atv2->playlist();
3446 latest_regionview = 0;
3448 where = (nframes_t) (unit_to_frame (ix1) * speed);
3449 boost::shared_ptr<Region> new_region (RegionFactory::create ((*i)->region()));
3451 from_playlist->remove_region (((*i)->region()));
3453 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3454 to_playlist->add_region (new_region, where);
3457 if (latest_regionview) {
3458 selection->add (latest_regionview);
3464 /* motion within a single track */
3466 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3470 if (rv->region()->locked()) {
3474 if (regionview_x_movement) {
3475 double ownspeed = 1.0;
3476 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3478 if (atv && atv->get_diskstream()) {
3479 ownspeed = atv->get_diskstream()->speed();
3482 /* base the new region position on the current position of the regionview.*/
3484 double ix1, ix2, iy1, iy2;
3486 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3487 rv->get_canvas_group()->i2w (ix1, iy1);
3488 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3492 where = rv->region()->position();
3495 rv->get_time_axis_view().reveal_dependent_views (*rv);
3497 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3499 rv->region()->set_position (where, (void *) this);
3500 rv->region()->set_opaque (true);
3505 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3507 session->add_command (new MementoCommand<Playlist>(*((*p).get()), 0, & (*p)->get_state()));
3510 motion_frozen_playlists.clear ();
3513 commit_reversible_command ();
3518 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3520 /* Either add to or set the set the region selection, unless
3521 this is an alignment click (control used)
3524 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3525 TimeAxisView* tv = &rv.get_time_axis_view();
3526 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3528 if (atv && atv->is_audio_track()) {
3529 speed = atv->get_diskstream()->speed();
3532 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3534 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3536 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3538 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3542 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3548 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3554 nframes_t frame_rate;
3561 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3562 case AudioClock::BBT:
3563 session->bbt_time (frame, bbt);
3564 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3567 case AudioClock::SMPTE:
3568 session->smpte_time (frame, smpte);
3569 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3572 case AudioClock::MinSec:
3573 /* XXX this is copied from show_verbose_duration_cursor() */
3574 frame_rate = session->frame_rate();
3575 hours = frame / (frame_rate * 3600);
3576 frame = frame % (frame_rate * 3600);
3577 mins = frame / (frame_rate * 60);
3578 frame = frame % (frame_rate * 60);
3579 secs = (float) frame / (float) frame_rate;
3580 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3584 snprintf (buf, sizeof(buf), "%u", frame);
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::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3605 nframes_t distance, frame_rate;
3607 Meter meter_at_start(session->tempo_map().meter_at(start));
3613 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3614 case AudioClock::BBT:
3615 session->bbt_time (start, sbbt);
3616 session->bbt_time (end, ebbt);
3619 /* XXX this computation won't work well if the
3620 user makes a selection that spans any meter changes.
3623 ebbt.bars -= sbbt.bars;
3624 if (ebbt.beats >= sbbt.beats) {
3625 ebbt.beats -= sbbt.beats;
3628 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3630 if (ebbt.ticks >= sbbt.ticks) {
3631 ebbt.ticks -= sbbt.ticks;
3634 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3637 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3640 case AudioClock::SMPTE:
3641 session->smpte_duration (end - start, smpte);
3642 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3645 case AudioClock::MinSec:
3646 /* XXX this stuff should be elsewhere.. */
3647 distance = end - start;
3648 frame_rate = session->frame_rate();
3649 hours = distance / (frame_rate * 3600);
3650 distance = distance % (frame_rate * 3600);
3651 mins = distance / (frame_rate * 60);
3652 distance = distance % (frame_rate * 60);
3653 secs = (float) distance / (float) frame_rate;
3654 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3658 snprintf (buf, sizeof(buf), "%u", end - start);
3662 if (xpos >= 0 && ypos >=0) {
3663 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3666 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3668 show_verbose_canvas_cursor ();
3672 Editor::collect_new_region_view (RegionView* rv)
3674 latest_regionview = rv;
3678 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3680 if (clicked_regionview == 0) {
3684 /* lets try to create new Region for the selection */
3686 vector<boost::shared_ptr<AudioRegion> > new_regions;
3687 create_region_from_selection (new_regions);
3689 if (new_regions.empty()) {
3693 /* XXX fix me one day to use all new regions */
3695 boost::shared_ptr<Region> region (new_regions.front());
3697 /* add it to the current stream/playlist.
3699 tricky: the streamview for the track will add a new regionview. we will
3700 catch the signal it sends when it creates the regionview to
3701 set the regionview we want to then drag.
3704 latest_regionview = 0;
3705 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3707 /* A selection grab currently creates two undo/redo operations, one for
3708 creating the new region and another for moving it.
3711 begin_reversible_command (_("selection grab"));
3713 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3715 XMLNode *before = &(playlist->get_state());
3716 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3717 XMLNode *after = &(playlist->get_state());
3718 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3720 commit_reversible_command ();
3724 if (latest_regionview == 0) {
3725 /* something went wrong */
3729 /* we need to deselect all other regionviews, and select this one
3730 i'm ignoring undo stuff, because the region creation will take care of it */
3731 selection->set (latest_regionview);
3733 drag_info.item = latest_regionview->get_canvas_group();
3734 drag_info.data = latest_regionview;
3735 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3736 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3740 drag_info.last_trackview = clicked_trackview;
3741 drag_info.last_frame_position = latest_regionview->region()->position();
3742 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3744 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3748 Editor::cancel_selection ()
3750 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3751 (*i)->hide_selection ();
3753 begin_reversible_command (_("cancel selection"));
3754 selection->clear ();
3755 clicked_selection = 0;
3756 commit_reversible_command ();
3760 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3762 nframes_t start = 0;
3769 drag_info.item = item;
3770 drag_info.motion_callback = &Editor::drag_selection;
3771 drag_info.finished_callback = &Editor::end_selection_op;
3776 case CreateSelection:
3777 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3778 drag_info.copy = true;
3780 drag_info.copy = false;
3782 start_grab (event, selector_cursor);
3785 case SelectionStartTrim:
3786 if (clicked_trackview) {
3787 clicked_trackview->order_selection_trims (item, true);
3789 start_grab (event, trimmer_cursor);
3790 start = selection->time[clicked_selection].start;
3791 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3794 case SelectionEndTrim:
3795 if (clicked_trackview) {
3796 clicked_trackview->order_selection_trims (item, false);
3798 start_grab (event, trimmer_cursor);
3799 end = selection->time[clicked_selection].end;
3800 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3804 start = selection->time[clicked_selection].start;
3806 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3810 if (selection_op == SelectionMove) {
3811 show_verbose_time_cursor(start, 10);
3813 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3818 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3820 nframes_t start = 0;
3823 nframes_t pending_position;
3825 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3826 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3829 pending_position = 0;
3832 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3833 snap_to (pending_position);
3836 /* only alter selection if the current frame is
3837 different from the last frame position (adjusted)
3840 if (pending_position == drag_info.last_pointer_frame) return;
3842 switch (selection_op) {
3843 case CreateSelection:
3845 if (drag_info.first_move) {
3846 snap_to (drag_info.grab_frame);
3849 if (pending_position < drag_info.grab_frame) {
3850 start = pending_position;
3851 end = drag_info.grab_frame;
3853 end = pending_position;
3854 start = drag_info.grab_frame;
3857 /* first drag: Either add to the selection
3858 or create a new selection->
3861 if (drag_info.first_move) {
3863 begin_reversible_command (_("range selection"));
3865 if (drag_info.copy) {
3866 /* adding to the selection */
3867 clicked_selection = selection->add (start, end);
3868 drag_info.copy = false;
3870 /* new selection-> */
3871 clicked_selection = selection->set (clicked_trackview, start, end);
3876 case SelectionStartTrim:
3878 if (drag_info.first_move) {
3879 begin_reversible_command (_("trim selection start"));
3882 start = selection->time[clicked_selection].start;
3883 end = selection->time[clicked_selection].end;
3885 if (pending_position > end) {
3888 start = pending_position;
3892 case SelectionEndTrim:
3894 if (drag_info.first_move) {
3895 begin_reversible_command (_("trim selection end"));
3898 start = selection->time[clicked_selection].start;
3899 end = selection->time[clicked_selection].end;
3901 if (pending_position < start) {
3904 end = pending_position;
3911 if (drag_info.first_move) {
3912 begin_reversible_command (_("move selection"));
3915 start = selection->time[clicked_selection].start;
3916 end = selection->time[clicked_selection].end;
3918 length = end - start;
3920 start = pending_position;
3923 end = start + length;
3928 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3929 start_canvas_autoscroll (1);
3933 selection->replace (clicked_selection, start, end);
3936 drag_info.last_pointer_frame = pending_position;
3937 drag_info.first_move = false;
3939 if (selection_op == SelectionMove) {
3940 show_verbose_time_cursor(start, 10);
3942 show_verbose_time_cursor(pending_position, 10);
3947 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3949 if (!drag_info.first_move) {
3950 drag_selection (item, event);
3951 /* XXX this is not object-oriented programming at all. ick */
3952 if (selection->time.consolidate()) {
3953 selection->TimeChanged ();
3955 commit_reversible_command ();
3957 /* just a click, no pointer movement.*/
3959 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3961 selection->clear_time();
3966 /* XXX what happens if its a music selection? */
3967 session->set_audio_range (selection->time);
3968 stop_canvas_autoscroll ();
3972 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3975 TimeAxisView* tvp = clicked_trackview;
3976 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3978 if (tv && tv->is_audio_track()) {
3979 speed = tv->get_diskstream()->speed();
3982 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3983 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3984 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3986 motion_frozen_playlists.clear();
3988 //drag_info.item = clicked_regionview->get_name_highlight();
3989 drag_info.item = item;
3990 drag_info.motion_callback = &Editor::trim_motion_callback;
3991 drag_info.finished_callback = &Editor::trim_finished_callback;
3993 start_grab (event, trimmer_cursor);
3995 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3996 trim_op = ContentsTrim;
3998 /* These will get overridden for a point trim.*/
3999 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4000 /* closer to start */
4001 trim_op = StartTrim;
4002 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4010 show_verbose_time_cursor(region_start, 10);
4013 show_verbose_time_cursor(region_end, 10);
4016 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4022 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4024 RegionView* rv = clicked_regionview;
4025 nframes_t frame_delta = 0;
4026 bool left_direction;
4027 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4029 /* snap modifier works differently here..
4030 its' current state has to be passed to the
4031 various trim functions in order to work properly
4035 TimeAxisView* tvp = clicked_trackview;
4036 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4037 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4039 if (tv && tv->is_audio_track()) {
4040 speed = tv->get_diskstream()->speed();
4043 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4044 left_direction = true;
4046 left_direction = false;
4050 snap_to (drag_info.current_pointer_frame);
4053 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4057 if (drag_info.first_move) {
4063 trim_type = "Region start trim";
4066 trim_type = "Region end trim";
4069 trim_type = "Region content trim";
4073 begin_reversible_command (trim_type);
4075 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4076 (*i)->region()->set_opaque(false);
4077 (*i)->region()->freeze ();
4079 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4081 arv->temporarily_hide_envelope ();
4083 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4084 insert_result = motion_frozen_playlists.insert (pl);
4085 if (insert_result.second) {
4086 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4091 if (left_direction) {
4092 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4094 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4099 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4102 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4103 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4109 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4112 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4113 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4120 bool swap_direction = false;
4122 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4123 swap_direction = true;
4126 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4127 i != selection->regions.by_layer().end(); ++i)
4129 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4137 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4140 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4143 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4147 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4148 drag_info.first_move = false;
4152 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4154 boost::shared_ptr<Region> region (rv.region());
4156 if (region->locked()) {
4160 nframes_t new_bound;
4163 TimeAxisView* tvp = clicked_trackview;
4164 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4166 if (tv && tv->is_audio_track()) {
4167 speed = tv->get_diskstream()->speed();
4170 if (left_direction) {
4171 if (swap_direction) {
4172 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4174 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4177 if (swap_direction) {
4178 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4180 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4185 snap_to (new_bound);
4187 region->trim_start ((nframes_t) (new_bound * speed), this);
4188 rv.region_changed (StartChanged);
4192 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4194 boost::shared_ptr<Region> region (rv.region());
4196 if (region->locked()) {
4200 nframes_t new_bound;
4203 TimeAxisView* tvp = clicked_trackview;
4204 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4206 if (tv && tv->is_audio_track()) {
4207 speed = tv->get_diskstream()->speed();
4210 if (left_direction) {
4211 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4213 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4217 snap_to (new_bound, (left_direction ? 0 : 1));
4220 region->trim_front ((nframes_t) (new_bound * speed), this);
4222 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4226 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4228 boost::shared_ptr<Region> region (rv.region());
4230 if (region->locked()) {
4234 nframes_t new_bound;
4237 TimeAxisView* tvp = clicked_trackview;
4238 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4240 if (tv && tv->is_audio_track()) {
4241 speed = tv->get_diskstream()->speed();
4244 if (left_direction) {
4245 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4247 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4251 snap_to (new_bound);
4253 region->trim_end ((nframes_t) (new_bound * speed), this);
4254 rv.region_changed (LengthChanged);
4258 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4260 if (!drag_info.first_move) {
4261 trim_motion_callback (item, event);
4263 if (!clicked_regionview->get_selected()) {
4264 thaw_region_after_trim (*clicked_regionview);
4267 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4268 i != selection->regions.by_layer().end(); ++i)
4270 thaw_region_after_trim (**i);
4271 (*i)->region()->set_opaque(true);
4275 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4277 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4280 motion_frozen_playlists.clear ();
4282 commit_reversible_command();
4284 /* no mouse movement */
4290 Editor::point_trim (GdkEvent* event)
4292 RegionView* rv = clicked_regionview;
4293 nframes_t new_bound = drag_info.current_pointer_frame;
4295 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4296 snap_to (new_bound);
4299 /* Choose action dependant on which button was pressed */
4300 switch (event->button.button) {
4302 trim_op = StartTrim;
4303 begin_reversible_command (_("Start point trim"));
4305 if (rv->get_selected()) {
4307 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4308 i != selection->regions.by_layer().end(); ++i)
4310 if (!(*i)->region()->locked()) {
4311 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4312 XMLNode &before = pl->get_state();
4313 (*i)->region()->trim_front (new_bound, this);
4314 XMLNode &after = pl->get_state();
4315 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4321 if (!rv->region()->locked()) {
4322 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4323 XMLNode &before = pl->get_state();
4324 rv->region()->trim_front (new_bound, this);
4325 XMLNode &after = pl->get_state();
4326 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4330 commit_reversible_command();
4335 begin_reversible_command (_("End point trim"));
4337 if (rv->get_selected()) {
4339 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4341 if (!(*i)->region()->locked()) {
4342 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4343 XMLNode &before = pl->get_state();
4344 (*i)->region()->trim_end (new_bound, this);
4345 XMLNode &after = pl->get_state();
4346 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4352 if (!rv->region()->locked()) {
4353 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4354 XMLNode &before = pl->get_state();
4355 rv->region()->trim_end (new_bound, this);
4356 XMLNode &after = pl->get_state();
4357 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4361 commit_reversible_command();
4370 Editor::thaw_region_after_trim (RegionView& rv)
4372 boost::shared_ptr<Region> region (rv.region());
4374 if (region->locked()) {
4378 region->thaw (_("trimmed region"));
4379 XMLNode &after = region->playlist()->get_state();
4380 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4382 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4384 arv->unhide_envelope ();
4388 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4393 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4394 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4398 Location* location = find_location_from_marker (marker, is_start);
4399 location->set_hidden (true, this);
4404 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4410 drag_info.item = item;
4411 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4412 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4414 range_marker_op = op;
4416 if (!temp_location) {
4417 temp_location = new Location;
4421 case CreateRangeMarker:
4422 case CreateTransportMarker:
4424 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4425 drag_info.copy = true;
4427 drag_info.copy = false;
4429 start_grab (event, selector_cursor);
4433 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4438 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4440 nframes_t start = 0;
4442 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4444 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4445 snap_to (drag_info.current_pointer_frame);
4448 /* only alter selection if the current frame is
4449 different from the last frame position.
4452 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4454 switch (range_marker_op) {
4455 case CreateRangeMarker:
4456 case CreateTransportMarker:
4457 if (drag_info.first_move) {
4458 snap_to (drag_info.grab_frame);
4461 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4462 start = drag_info.current_pointer_frame;
4463 end = drag_info.grab_frame;
4465 end = drag_info.current_pointer_frame;
4466 start = drag_info.grab_frame;
4469 /* first drag: Either add to the selection
4470 or create a new selection.
4473 if (drag_info.first_move) {
4475 temp_location->set (start, end);
4479 update_marker_drag_item (temp_location);
4480 range_marker_drag_rect->show();
4481 range_marker_drag_rect->raise_to_top();
4487 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4488 start_canvas_autoscroll (1);
4492 temp_location->set (start, end);
4494 double x1 = frame_to_pixel (start);
4495 double x2 = frame_to_pixel (end);
4496 crect->property_x1() = x1;
4497 crect->property_x2() = x2;
4499 update_marker_drag_item (temp_location);
4502 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4503 drag_info.first_move = false;
4505 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4510 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4512 Location * newloc = 0;
4515 if (!drag_info.first_move) {
4516 drag_range_markerbar_op (item, event);
4518 switch (range_marker_op) {
4519 case CreateRangeMarker:
4521 begin_reversible_command (_("new range marker"));
4522 XMLNode &before = session->locations()->get_state();
4523 session->locations()->next_available_name(rangename,"unnamed");
4524 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4525 session->locations()->add (newloc, true);
4526 XMLNode &after = session->locations()->get_state();
4527 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4528 commit_reversible_command ();
4530 range_bar_drag_rect->hide();
4531 range_marker_drag_rect->hide();
4535 case CreateTransportMarker:
4536 // popup menu to pick loop or punch
4537 new_transport_marker_context_menu (&event->button, item);
4542 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4544 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4549 start = session->locations()->first_mark_before (drag_info.grab_frame);
4550 end = session->locations()->first_mark_after (drag_info.grab_frame);
4552 if (end == max_frames) {
4553 end = session->current_end_frame ();
4557 start = session->current_start_frame ();
4560 switch (mouse_mode) {
4562 /* find the two markers on either side and then make the selection from it */
4563 cerr << "select between " << start << " .. " << end << endl;
4564 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4568 /* find the two markers on either side of the click and make the range out of it */
4569 selection->set (0, start, end);
4578 stop_canvas_autoscroll ();
4584 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4586 drag_info.item = item;
4587 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4588 drag_info.finished_callback = &Editor::end_mouse_zoom;
4590 start_grab (event, zoom_cursor);
4592 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4596 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4601 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4602 snap_to (drag_info.current_pointer_frame);
4604 if (drag_info.first_move) {
4605 snap_to (drag_info.grab_frame);
4609 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4611 /* base start and end on initial click position */
4612 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4613 start = drag_info.current_pointer_frame;
4614 end = drag_info.grab_frame;
4616 end = drag_info.current_pointer_frame;
4617 start = drag_info.grab_frame;
4622 if (drag_info.first_move) {
4624 zoom_rect->raise_to_top();
4627 reposition_zoom_rect(start, end);
4629 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4630 drag_info.first_move = false;
4632 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4637 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4639 if (!drag_info.first_move) {
4640 drag_mouse_zoom (item, event);
4642 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4643 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4645 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4648 temporal_zoom_to_frame (false, drag_info.grab_frame);
4650 temporal_zoom_step (false);
4651 center_screen (drag_info.grab_frame);
4659 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4661 double x1 = frame_to_pixel (start);
4662 double x2 = frame_to_pixel (end);
4663 double y2 = full_canvas_height - 1.0;
4665 zoom_rect->property_x1() = x1;
4666 zoom_rect->property_y1() = 1.0;
4667 zoom_rect->property_x2() = x2;
4668 zoom_rect->property_y2() = y2;
4672 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4674 drag_info.item = item;
4675 drag_info.motion_callback = &Editor::drag_rubberband_select;
4676 drag_info.finished_callback = &Editor::end_rubberband_select;
4678 start_grab (event, cross_hair_cursor);
4680 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4684 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4691 /* use a bigger drag threshold than the default */
4693 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4697 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4698 if (drag_info.first_move) {
4699 snap_to (drag_info.grab_frame);
4701 snap_to (drag_info.current_pointer_frame);
4704 /* base start and end on initial click position */
4706 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4707 start = drag_info.current_pointer_frame;
4708 end = drag_info.grab_frame;
4710 end = drag_info.current_pointer_frame;
4711 start = drag_info.grab_frame;
4714 if (drag_info.current_pointer_y < drag_info.grab_y) {
4715 y1 = drag_info.current_pointer_y;
4716 y2 = drag_info.grab_y;
4718 y2 = drag_info.current_pointer_y;
4719 y1 = drag_info.grab_y;
4723 if (start != end || y1 != y2) {
4725 double x1 = frame_to_pixel (start);
4726 double x2 = frame_to_pixel (end);
4728 rubberband_rect->property_x1() = x1;
4729 rubberband_rect->property_y1() = y1;
4730 rubberband_rect->property_x2() = x2;
4731 rubberband_rect->property_y2() = y2;
4733 rubberband_rect->show();
4734 rubberband_rect->raise_to_top();
4736 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4737 drag_info.first_move = false;
4739 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4744 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4746 if (!drag_info.first_move) {
4748 drag_rubberband_select (item, event);
4751 if (drag_info.current_pointer_y < drag_info.grab_y) {
4752 y1 = drag_info.current_pointer_y;
4753 y2 = drag_info.grab_y;
4756 y2 = drag_info.current_pointer_y;
4757 y1 = drag_info.grab_y;
4761 Selection::Operation op = Keyboard::selection_type (event->button.state);
4764 begin_reversible_command (_("rubberband selection"));
4766 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4767 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4769 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4773 commit_reversible_command ();
4777 selection->clear_tracks();
4778 selection->clear_regions();
4779 selection->clear_points ();
4780 selection->clear_lines ();
4783 rubberband_rect->hide();
4788 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4790 using namespace Gtkmm2ext;
4792 ArdourPrompter prompter (false);
4794 prompter.set_prompt (_("Name for region:"));
4795 prompter.set_initial_text (clicked_regionview->region()->name());
4796 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4797 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4798 prompter.show_all ();
4799 switch (prompter.run ()) {
4800 case Gtk::RESPONSE_ACCEPT:
4802 prompter.get_result(str);
4804 clicked_regionview->region()->set_name (str);
4812 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4814 drag_info.item = item;
4815 drag_info.motion_callback = &Editor::time_fx_motion;
4816 drag_info.finished_callback = &Editor::end_time_fx;
4820 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4824 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4826 RegionView* rv = clicked_regionview;
4828 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4829 snap_to (drag_info.current_pointer_frame);
4832 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4836 if (drag_info.current_pointer_frame > rv->region()->position()) {
4837 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4840 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4841 drag_info.first_move = false;
4843 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4847 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4849 clicked_regionview->get_time_axis_view().hide_timestretch ();
4851 if (drag_info.first_move) {
4855 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4856 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4858 begin_reversible_command (_("timestretch"));
4860 if (run_timestretch (selection->regions, percentage) == 0) {
4861 session->commit_reversible_command ();
4866 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4868 /* no brushing without a useful snap setting */
4871 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4874 switch (snap_mode) {
4876 return; /* can't work because it allows region to be placed anywhere */
4881 switch (snap_type) {
4884 case SnapToEditCursor:
4891 /* don't brush a copy over the original */
4893 if (pos == rv->region()->position()) {
4897 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4899 if (atv == 0 || !atv->is_audio_track()) {
4903 boost::shared_ptr<Playlist> playlist = atv->playlist();
4904 double speed = atv->get_diskstream()->speed();
4906 XMLNode &before = playlist->get_state();
4907 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4908 XMLNode &after = playlist->get_state();
4909 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4911 // playlist is frozen, so we have to update manually
4913 playlist->Modified(); /* EMIT SIGNAL */
4917 Editor::track_height_step_timeout ()
4920 struct timeval delta;
4922 gettimeofday (&now, 0);
4923 timersub (&now, &last_track_height_step_timestamp, &delta);
4925 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4926 current_stepping_trackview = 0;