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 (mouse_mode != MouseRange)) ||
307 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
312 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
314 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
316 /* almost no selection action on modified button-2 or button-3 events */
318 if (item_type != RegionItem && event->button.button != 2) {
324 Selection::Operation op = Keyboard::selection_type (event->button.state);
325 bool press = (event->type == GDK_BUTTON_PRESS);
327 begin_reversible_command (_("select on click"));
331 if (mouse_mode != MouseRange) {
332 commit = set_selected_regionview_from_click (press, op, true);
333 } else if (event->type == GDK_BUTTON_PRESS) {
334 commit = set_selected_track_from_click (press, op, false);
338 case RegionViewNameHighlight:
340 if (mouse_mode != MouseRange) {
341 commit = set_selected_regionview_from_click (press, op, true);
342 } else if (event->type == GDK_BUTTON_PRESS) {
343 commit = set_selected_track_from_click (press, op, false);
347 case FadeInHandleItem:
349 case FadeOutHandleItem:
351 if (mouse_mode != MouseRange) {
352 commit = set_selected_regionview_from_click (press, op, true);
353 } else if (event->type == GDK_BUTTON_PRESS) {
354 commit = set_selected_track_from_click (press, op, false);
358 case GainAutomationControlPointItem:
359 case PanAutomationControlPointItem:
360 case RedirectAutomationControlPointItem:
361 if (mouse_mode != MouseRange) {
362 commit = set_selected_control_point_from_click (op, false);
367 /* for context click or range selection, select track */
368 if (event->button.button == 3) {
369 commit = set_selected_track_from_click (press, op, true);
370 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
371 commit = set_selected_track_from_click (press, op, false);
375 case AutomationTrackItem:
376 commit = set_selected_track_from_click (press, 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 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1180 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1181 cp->set_visible (true);
1185 at_y = cp->get_y ();
1186 cp->item->i2w (at_x, at_y);
1190 fraction = 1.0 - (cp->get_y() / cp->line.height());
1192 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1193 show_verbose_canvas_cursor ();
1195 if (is_drawable()) {
1196 track_canvas.get_window()->set_cursor (*fader_cursor);
1202 if (mouse_mode == MouseGain) {
1203 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1205 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1206 if (is_drawable()) {
1207 track_canvas.get_window()->set_cursor (*fader_cursor);
1212 case GainAutomationLineItem:
1213 case RedirectAutomationLineItem:
1214 case PanAutomationLineItem:
1215 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1217 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1219 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1221 if (is_drawable()) {
1222 track_canvas.get_window()->set_cursor (*fader_cursor);
1227 case RegionViewNameHighlight:
1228 if (is_drawable() && mouse_mode == MouseObject) {
1229 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1233 case StartSelectionTrimItem:
1234 case EndSelectionTrimItem:
1235 /* <CMT Additions> */
1236 case ImageFrameHandleStartItem:
1237 case ImageFrameHandleEndItem:
1238 case MarkerViewHandleStartItem:
1239 case MarkerViewHandleEndItem:
1240 /* </CMT Additions> */
1242 if (is_drawable()) {
1243 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1247 case EditCursorItem:
1248 case PlayheadCursorItem:
1249 if (is_drawable()) {
1250 track_canvas.get_window()->set_cursor (*grabber_cursor);
1254 case RegionViewName:
1256 /* when the name is not an active item, the entire name highlight is for trimming */
1258 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1259 if (mouse_mode == MouseObject && is_drawable()) {
1260 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1266 case AutomationTrackItem:
1267 if (is_drawable()) {
1268 Gdk::Cursor *cursor;
1269 switch (mouse_mode) {
1271 cursor = selector_cursor;
1274 cursor = zoom_cursor;
1277 cursor = cross_hair_cursor;
1281 track_canvas.get_window()->set_cursor (*cursor);
1283 AutomationTimeAxisView* atv;
1284 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1285 clear_entered_track = false;
1286 set_entered_track (atv);
1292 case RangeMarkerBarItem:
1293 case TransportMarkerBarItem:
1296 if (is_drawable()) {
1297 time_canvas.get_window()->set_cursor (*timebar_cursor);
1302 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1305 marker->set_color_rgba (color_map[cEnteredMarker]);
1307 case MeterMarkerItem:
1308 case TempoMarkerItem:
1309 if (is_drawable()) {
1310 time_canvas.get_window()->set_cursor (*timebar_cursor);
1313 case FadeInHandleItem:
1314 case FadeOutHandleItem:
1315 if (mouse_mode == MouseObject) {
1316 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1318 rect->property_fill_color_rgba() = 0;
1319 rect->property_outline_pixels() = 1;
1328 /* second pass to handle entered track status in a comprehensible way.
1331 switch (item_type) {
1333 case GainAutomationLineItem:
1334 case RedirectAutomationLineItem:
1335 case PanAutomationLineItem:
1336 case GainControlPointItem:
1337 case GainAutomationControlPointItem:
1338 case PanAutomationControlPointItem:
1339 case RedirectAutomationControlPointItem:
1340 /* these do not affect the current entered track state */
1341 clear_entered_track = false;
1344 case AutomationTrackItem:
1345 /* handled above already */
1349 set_entered_track (0);
1357 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1366 switch (item_type) {
1367 case GainControlPointItem:
1368 case GainAutomationControlPointItem:
1369 case PanAutomationControlPointItem:
1370 case RedirectAutomationControlPointItem:
1371 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1372 if (cp->line.npoints() > 1) {
1373 if (!cp->selected) {
1374 cp->set_visible (false);
1378 if (is_drawable()) {
1379 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1382 hide_verbose_canvas_cursor ();
1385 case RegionViewNameHighlight:
1386 case StartSelectionTrimItem:
1387 case EndSelectionTrimItem:
1388 case EditCursorItem:
1389 case PlayheadCursorItem:
1390 /* <CMT Additions> */
1391 case ImageFrameHandleStartItem:
1392 case ImageFrameHandleEndItem:
1393 case MarkerViewHandleStartItem:
1394 case MarkerViewHandleEndItem:
1395 /* </CMT Additions> */
1396 if (is_drawable()) {
1397 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1402 case GainAutomationLineItem:
1403 case RedirectAutomationLineItem:
1404 case PanAutomationLineItem:
1405 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1407 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1409 line->property_fill_color_rgba() = al->get_line_color();
1411 if (is_drawable()) {
1412 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1416 case RegionViewName:
1417 /* see enter_handler() for notes */
1418 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1419 if (is_drawable() && mouse_mode == MouseObject) {
1420 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1425 case RangeMarkerBarItem:
1426 case TransportMarkerBarItem:
1430 if (is_drawable()) {
1431 time_canvas.get_window()->set_cursor (*timebar_cursor);
1436 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1439 loc = find_location_from_marker (marker, is_start);
1440 if (loc) location_flags_changed (loc, this);
1442 case MeterMarkerItem:
1443 case TempoMarkerItem:
1445 if (is_drawable()) {
1446 time_canvas.get_window()->set_cursor (*timebar_cursor);
1451 case FadeInHandleItem:
1452 case FadeOutHandleItem:
1453 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1455 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1457 rect->property_fill_color_rgba() = rv->get_fill_color();
1458 rect->property_outline_pixels() = 0;
1463 case AutomationTrackItem:
1464 if (is_drawable()) {
1465 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1466 clear_entered_track = true;
1467 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1479 Editor::left_automation_track ()
1481 if (clear_entered_track) {
1482 set_entered_track (0);
1483 clear_entered_track = false;
1489 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1493 /* We call this so that MOTION_NOTIFY events continue to be
1494 delivered to the canvas. We need to do this because we set
1495 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1496 the density of the events, at the expense of a round-trip
1497 to the server. Given that this will mostly occur on cases
1498 where DISPLAY = :0.0, and given the cost of what the motion
1499 event might do, its a good tradeoff.
1502 track_canvas.get_pointer (x, y);
1504 if (current_stepping_trackview) {
1505 /* don't keep the persistent stepped trackview if the mouse moves */
1506 current_stepping_trackview = 0;
1507 step_timeout.disconnect ();
1510 if (session && session->actively_recording()) {
1511 /* Sorry. no dragging stuff around while we record */
1515 drag_info.item_type = item_type;
1516 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1517 &drag_info.current_pointer_y);
1519 if (!from_autoscroll && drag_info.item) {
1520 /* item != 0 is the best test i can think of for dragging.
1522 if (!drag_info.move_threshold_passed) {
1524 drag_info.move_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1526 // and change the initial grab loc/frame if this drag info wants us to
1528 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1529 drag_info.grab_frame = drag_info.current_pointer_frame;
1530 drag_info.grab_x = drag_info.current_pointer_x;
1531 drag_info.grab_y = drag_info.current_pointer_y;
1532 drag_info.last_pointer_frame = drag_info.grab_frame;
1533 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1538 switch (item_type) {
1539 case PlayheadCursorItem:
1540 case EditCursorItem:
1542 case GainControlPointItem:
1543 case RedirectAutomationControlPointItem:
1544 case GainAutomationControlPointItem:
1545 case PanAutomationControlPointItem:
1546 case TempoMarkerItem:
1547 case MeterMarkerItem:
1548 case RegionViewNameHighlight:
1549 case StartSelectionTrimItem:
1550 case EndSelectionTrimItem:
1553 case RedirectAutomationLineItem:
1554 case GainAutomationLineItem:
1555 case PanAutomationLineItem:
1556 case FadeInHandleItem:
1557 case FadeOutHandleItem:
1558 /* <CMT Additions> */
1559 case ImageFrameHandleStartItem:
1560 case ImageFrameHandleEndItem:
1561 case MarkerViewHandleStartItem:
1562 case MarkerViewHandleEndItem:
1563 /* </CMT Additions> */
1564 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1565 (event->motion.state & Gdk::BUTTON2_MASK))) {
1566 if (!from_autoscroll) {
1567 maybe_autoscroll (event);
1569 (this->*(drag_info.motion_callback)) (item, event);
1578 switch (mouse_mode) {
1583 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1584 (event->motion.state & GDK_BUTTON2_MASK))) {
1585 if (!from_autoscroll) {
1586 maybe_autoscroll (event);
1588 (this->*(drag_info.motion_callback)) (item, event);
1599 track_canvas_motion (event);
1600 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1608 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1610 if (drag_info.item == 0) {
1611 fatal << _("programming error: start_grab called without drag item") << endmsg;
1617 cursor = grabber_cursor;
1620 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1622 if (event->button.button == 2) {
1623 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1624 drag_info.y_constrained = true;
1625 drag_info.x_constrained = false;
1627 drag_info.y_constrained = false;
1628 drag_info.x_constrained = true;
1631 drag_info.x_constrained = false;
1632 drag_info.y_constrained = false;
1635 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1636 drag_info.last_pointer_frame = drag_info.grab_frame;
1637 drag_info.current_pointer_frame = drag_info.grab_frame;
1638 drag_info.current_pointer_x = drag_info.grab_x;
1639 drag_info.current_pointer_y = drag_info.grab_y;
1640 drag_info.cumulative_x_drag = 0;
1641 drag_info.cumulative_y_drag = 0;
1642 drag_info.first_move = true;
1643 drag_info.move_threshold_passed = false;
1644 drag_info.want_move_threshold = false;
1645 drag_info.pointer_frame_offset = 0;
1646 drag_info.brushing = false;
1647 drag_info.copied_location = 0;
1649 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1651 event->button.time);
1653 if (session && session->transport_rolling()) {
1654 drag_info.was_rolling = true;
1656 drag_info.was_rolling = false;
1659 switch (snap_type) {
1660 case SnapToRegionStart:
1661 case SnapToRegionEnd:
1662 case SnapToRegionSync:
1663 case SnapToRegionBoundary:
1664 build_region_boundary_cache ();
1672 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1674 drag_info.item->ungrab (0);
1675 drag_info.item = new_item;
1678 cursor = grabber_cursor;
1681 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1685 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1687 bool did_drag = false;
1689 stop_canvas_autoscroll ();
1691 if (drag_info.item == 0) {
1695 drag_info.item->ungrab (event->button.time);
1697 if (drag_info.finished_callback) {
1698 (this->*(drag_info.finished_callback)) (item, event);
1701 did_drag = !drag_info.first_move;
1703 hide_verbose_canvas_cursor();
1706 drag_info.copy = false;
1707 drag_info.motion_callback = 0;
1708 drag_info.finished_callback = 0;
1709 drag_info.last_trackview = 0;
1710 drag_info.last_frame_position = 0;
1711 drag_info.grab_frame = 0;
1712 drag_info.last_pointer_frame = 0;
1713 drag_info.current_pointer_frame = 0;
1714 drag_info.brushing = false;
1716 if (drag_info.copied_location) {
1717 delete drag_info.copied_location;
1718 drag_info.copied_location = 0;
1725 Editor::set_edit_cursor (GdkEvent* event)
1727 nframes_t pointer_frame = event_frame (event);
1729 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1730 if (snap_type != SnapToEditCursor) {
1731 snap_to (pointer_frame);
1735 edit_cursor->set_position (pointer_frame);
1736 edit_cursor_clock.set (pointer_frame);
1740 Editor::set_playhead_cursor (GdkEvent* event)
1742 nframes_t pointer_frame = event_frame (event);
1744 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1745 snap_to (pointer_frame);
1749 session->request_locate (pointer_frame, session->transport_rolling());
1754 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1756 drag_info.item = item;
1757 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1758 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1762 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1763 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1767 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1769 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1773 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1775 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1777 nframes_t fade_length;
1779 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1780 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1786 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1790 if (pos < (arv->region()->position() + 64)) {
1791 fade_length = 64; // this should be a minimum defined somewhere
1792 } else if (pos > arv->region()->last_frame()) {
1793 fade_length = arv->region()->length();
1795 fade_length = pos - arv->region()->position();
1797 /* mapover the region selection */
1799 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1801 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1807 tmp->reset_fade_in_shape_width (fade_length);
1810 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1812 drag_info.first_move = false;
1816 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1818 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1820 nframes_t fade_length;
1822 if (drag_info.first_move) return;
1824 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1825 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1830 if (pos < (arv->region()->position() + 64)) {
1831 fade_length = 64; // this should be a minimum defined somewhere
1832 } else if (pos > arv->region()->last_frame()) {
1833 fade_length = arv->region()->length();
1835 fade_length = pos - arv->region()->position();
1838 begin_reversible_command (_("change fade in length"));
1840 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1842 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1848 AutomationList& alist = tmp->audio_region()->fade_in();
1849 XMLNode &before = alist.get_state();
1851 tmp->audio_region()->set_fade_in_length (fade_length);
1853 XMLNode &after = alist.get_state();
1854 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1857 commit_reversible_command ();
1861 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1863 drag_info.item = item;
1864 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1865 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1869 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1870 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1874 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1876 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1880 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1882 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1884 nframes_t fade_length;
1886 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1887 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1893 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1897 if (pos > (arv->region()->last_frame() - 64)) {
1898 fade_length = 64; // this should really be a minimum fade defined somewhere
1900 else if (pos < arv->region()->position()) {
1901 fade_length = arv->region()->length();
1904 fade_length = arv->region()->last_frame() - pos;
1907 /* mapover the region selection */
1909 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1911 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1917 tmp->reset_fade_out_shape_width (fade_length);
1920 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1922 drag_info.first_move = false;
1926 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1928 if (drag_info.first_move) return;
1930 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1932 nframes_t fade_length;
1934 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1935 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1941 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1945 if (pos > (arv->region()->last_frame() - 64)) {
1946 fade_length = 64; // this should really be a minimum fade defined somewhere
1948 else if (pos < arv->region()->position()) {
1949 fade_length = arv->region()->length();
1952 fade_length = arv->region()->last_frame() - pos;
1955 begin_reversible_command (_("change fade out length"));
1957 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1959 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1965 AutomationList& alist = tmp->audio_region()->fade_out();
1966 XMLNode &before = alist.get_state();
1968 tmp->audio_region()->set_fade_out_length (fade_length);
1970 XMLNode &after = alist.get_state();
1971 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1974 commit_reversible_command ();
1978 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1980 drag_info.item = item;
1981 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1982 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1986 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1987 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1991 Cursor* cursor = (Cursor *) drag_info.data;
1993 if (cursor == playhead_cursor) {
1994 _dragging_playhead = true;
1996 if (session && drag_info.was_rolling) {
1997 session->request_stop ();
2001 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2003 show_verbose_time_cursor (cursor->current_frame, 10);
2007 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2009 Cursor* cursor = (Cursor *) drag_info.data;
2010 nframes_t adjusted_frame;
2012 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2013 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2019 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2020 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
2021 snap_to (adjusted_frame);
2025 if (adjusted_frame == drag_info.last_pointer_frame) return;
2027 cursor->set_position (adjusted_frame);
2029 if (cursor == edit_cursor) {
2030 edit_cursor_clock.set (cursor->current_frame);
2032 UpdateAllTransportClocks (cursor->current_frame);
2035 show_verbose_time_cursor (cursor->current_frame, 10);
2037 drag_info.last_pointer_frame = adjusted_frame;
2038 drag_info.first_move = false;
2042 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2044 if (drag_info.first_move) return;
2046 cursor_drag_motion_callback (item, event);
2048 _dragging_playhead = false;
2050 if (item == &playhead_cursor->canvas_item) {
2052 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2054 } else if (item == &edit_cursor->canvas_item) {
2055 edit_cursor->set_position (edit_cursor->current_frame);
2056 edit_cursor_clock.set (edit_cursor->current_frame);
2061 Editor::update_marker_drag_item (Location *location)
2063 double x1 = frame_to_pixel (location->start());
2064 double x2 = frame_to_pixel (location->end());
2066 if (location->is_mark()) {
2067 marker_drag_line_points.front().set_x(x1);
2068 marker_drag_line_points.back().set_x(x1);
2069 marker_drag_line->property_points() = marker_drag_line_points;
2072 range_marker_drag_rect->property_x1() = x1;
2073 range_marker_drag_rect->property_x2() = x2;
2078 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2082 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2083 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2089 Location *location = find_location_from_marker (marker, is_start);
2091 drag_info.item = item;
2092 drag_info.data = marker;
2093 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2094 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2098 drag_info.copied_location = new Location (*location);
2099 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2101 update_marker_drag_item (location);
2103 if (location->is_mark()) {
2104 marker_drag_line->show();
2105 marker_drag_line->raise_to_top();
2108 range_marker_drag_rect->show();
2109 range_marker_drag_rect->raise_to_top();
2112 if (is_start) show_verbose_time_cursor (location->start(), 10);
2113 else show_verbose_time_cursor (location->end(), 10);
2117 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2120 Marker* marker = (Marker *) drag_info.data;
2121 Location *real_location;
2122 Location *copy_location;
2124 bool move_both = false;
2128 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2129 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2135 nframes_t next = newframe;
2137 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2138 snap_to (newframe, 0, true);
2141 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2145 /* call this to find out if its the start or end */
2147 real_location = find_location_from_marker (marker, is_start);
2149 /* use the copy that we're "dragging" around */
2151 copy_location = drag_info.copied_location;
2153 f_delta = copy_location->end() - copy_location->start();
2155 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2159 if (copy_location->is_mark()) {
2162 copy_location->set_start (newframe);
2166 if (is_start) { // start-of-range marker
2169 copy_location->set_start (newframe);
2170 copy_location->set_end (newframe + f_delta);
2171 } else if (newframe < copy_location->end()) {
2172 copy_location->set_start (newframe);
2174 snap_to (next, 1, true);
2175 copy_location->set_end (next);
2176 copy_location->set_start (newframe);
2179 } else { // end marker
2182 copy_location->set_end (newframe);
2183 copy_location->set_start (newframe - f_delta);
2184 } else if (newframe > copy_location->start()) {
2185 copy_location->set_end (newframe);
2187 } else if (newframe > 0) {
2188 snap_to (next, -1, true);
2189 copy_location->set_start (next);
2190 copy_location->set_end (newframe);
2195 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2196 drag_info.first_move = false;
2198 update_marker_drag_item (copy_location);
2200 LocationMarkers* lm = find_location_markers (real_location);
2201 lm->set_position (copy_location->start(), copy_location->end());
2203 show_verbose_time_cursor (newframe, 10);
2207 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2209 if (drag_info.first_move) {
2210 marker_drag_motion_callback (item, event);
2214 Marker* marker = (Marker *) drag_info.data;
2218 begin_reversible_command ( _("move marker") );
2219 XMLNode &before = session->locations()->get_state();
2221 Location * location = find_location_from_marker (marker, is_start);
2224 if (location->is_mark()) {
2225 location->set_start (drag_info.copied_location->start());
2227 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2231 XMLNode &after = session->locations()->get_state();
2232 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2233 commit_reversible_command ();
2235 marker_drag_line->hide();
2236 range_marker_drag_rect->hide();
2240 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2243 MeterMarker* meter_marker;
2245 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2246 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2250 meter_marker = dynamic_cast<MeterMarker*> (marker);
2252 MetricSection& section (meter_marker->meter());
2254 if (!section.movable()) {
2258 drag_info.item = item;
2259 drag_info.data = marker;
2260 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2261 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2265 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2267 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2271 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2274 MeterMarker* meter_marker;
2276 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2277 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2281 meter_marker = dynamic_cast<MeterMarker*> (marker);
2283 // create a dummy marker for visual representation of moving the copy.
2284 // The actual copying is not done before we reach the finish callback.
2286 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2287 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2288 *new MeterSection(meter_marker->meter()));
2290 drag_info.item = &new_marker->the_item();
2291 drag_info.copy = true;
2292 drag_info.data = new_marker;
2293 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2294 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2298 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2300 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2304 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2306 MeterMarker* marker = (MeterMarker *) drag_info.data;
2307 nframes_t adjusted_frame;
2309 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2310 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2316 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2317 snap_to (adjusted_frame);
2320 if (adjusted_frame == drag_info.last_pointer_frame) return;
2322 marker->set_position (adjusted_frame);
2325 drag_info.last_pointer_frame = adjusted_frame;
2326 drag_info.first_move = false;
2328 show_verbose_time_cursor (adjusted_frame, 10);
2332 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2334 if (drag_info.first_move) return;
2336 meter_marker_drag_motion_callback (drag_info.item, event);
2338 MeterMarker* marker = (MeterMarker *) drag_info.data;
2341 TempoMap& map (session->tempo_map());
2342 map.bbt_time (drag_info.last_pointer_frame, when);
2344 if (drag_info.copy == true) {
2345 begin_reversible_command (_("copy meter mark"));
2346 XMLNode &before = map.get_state();
2347 map.add_meter (marker->meter(), when);
2348 XMLNode &after = map.get_state();
2349 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2350 commit_reversible_command ();
2352 // delete the dummy marker we used for visual representation of copying.
2353 // a new visual marker will show up automatically.
2356 begin_reversible_command (_("move meter mark"));
2357 XMLNode &before = map.get_state();
2358 map.move_meter (marker->meter(), when);
2359 XMLNode &after = map.get_state();
2360 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2361 commit_reversible_command ();
2366 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2369 TempoMarker* tempo_marker;
2371 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2372 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2376 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2377 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2381 MetricSection& section (tempo_marker->tempo());
2383 if (!section.movable()) {
2387 drag_info.item = item;
2388 drag_info.data = marker;
2389 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2390 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2394 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2395 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2399 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2402 TempoMarker* tempo_marker;
2404 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2405 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2409 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2410 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2414 // create a dummy marker for visual representation of moving the copy.
2415 // The actual copying is not done before we reach the finish callback.
2417 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2418 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2419 *new TempoSection(tempo_marker->tempo()));
2421 drag_info.item = &new_marker->the_item();
2422 drag_info.copy = true;
2423 drag_info.data = new_marker;
2424 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2425 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2429 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2431 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2435 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2437 TempoMarker* marker = (TempoMarker *) drag_info.data;
2438 nframes_t adjusted_frame;
2440 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2441 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2447 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2448 snap_to (adjusted_frame);
2451 if (adjusted_frame == drag_info.last_pointer_frame) return;
2453 /* OK, we've moved far enough to make it worth actually move the thing. */
2455 marker->set_position (adjusted_frame);
2457 show_verbose_time_cursor (adjusted_frame, 10);
2459 drag_info.last_pointer_frame = adjusted_frame;
2460 drag_info.first_move = false;
2464 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2466 if (drag_info.first_move) return;
2468 tempo_marker_drag_motion_callback (drag_info.item, event);
2470 TempoMarker* marker = (TempoMarker *) drag_info.data;
2473 TempoMap& map (session->tempo_map());
2474 map.bbt_time (drag_info.last_pointer_frame, when);
2476 if (drag_info.copy == true) {
2477 begin_reversible_command (_("copy tempo mark"));
2478 XMLNode &before = map.get_state();
2479 map.add_tempo (marker->tempo(), when);
2480 XMLNode &after = map.get_state();
2481 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2482 commit_reversible_command ();
2484 // delete the dummy marker we used for visual representation of copying.
2485 // a new visual marker will show up automatically.
2488 begin_reversible_command (_("move tempo mark"));
2489 XMLNode &before = map.get_state();
2490 map.move_tempo (marker->tempo(), when);
2491 XMLNode &after = map.get_state();
2492 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2493 commit_reversible_command ();
2498 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2500 ControlPoint* control_point;
2502 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2503 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2507 // We shouldn't remove the first or last gain point
2508 if (control_point->line.is_last_point(*control_point) ||
2509 control_point->line.is_first_point(*control_point)) {
2513 control_point->line.remove_point (*control_point);
2517 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2519 ControlPoint* control_point;
2521 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2522 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2526 control_point->line.remove_point (*control_point);
2530 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2532 ControlPoint* control_point;
2534 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2535 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2539 drag_info.item = item;
2540 drag_info.data = control_point;
2541 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2542 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2544 start_grab (event, fader_cursor);
2546 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2548 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2549 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2550 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2552 show_verbose_canvas_cursor ();
2556 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2558 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2560 double cx = drag_info.current_pointer_x;
2561 double cy = drag_info.current_pointer_y;
2563 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2564 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2566 if (drag_info.x_constrained) {
2567 cx = drag_info.grab_x;
2569 if (drag_info.y_constrained) {
2570 cy = drag_info.grab_y;
2573 cp->line.parent_group().w2i (cx, cy);
2577 cy = min ((double) cp->line.height(), cy);
2579 //translate cx to frames
2580 nframes_t cx_frames = unit_to_frame (cx);
2582 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2583 snap_to (cx_frames);
2586 float fraction = 1.0 - (cy / cp->line.height());
2590 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2596 cp->line.point_drag (*cp, cx_frames , fraction, push);
2598 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2600 drag_info.first_move = false;
2604 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2606 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2608 if (drag_info.first_move) {
2612 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2613 reset_point_selection ();
2617 control_point_drag_motion_callback (item, event);
2619 cp->line.end_drag (cp);
2623 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2625 switch (mouse_mode) {
2627 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2628 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2636 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2640 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2641 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2645 start_line_grab (al, event);
2649 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2653 nframes_t frame_within_region;
2655 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2659 cx = event->button.x;
2660 cy = event->button.y;
2661 line->parent_group().w2i (cx, cy);
2662 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2664 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2665 current_line_drag_info.after)) {
2666 /* no adjacent points */
2670 drag_info.item = &line->grab_item();
2671 drag_info.data = line;
2672 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2673 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2675 start_grab (event, fader_cursor);
2677 double fraction = 1.0 - (cy / line->height());
2679 line->start_drag (0, drag_info.grab_frame, fraction);
2681 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2682 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2683 show_verbose_canvas_cursor ();
2687 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2689 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2690 double cx = drag_info.current_pointer_x;
2691 double cy = drag_info.current_pointer_y;
2693 line->parent_group().w2i (cx, cy);
2696 fraction = 1.0 - (cy / line->height());
2700 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2706 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2708 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2712 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2714 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2715 line_drag_motion_callback (item, event);
2720 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2722 if (selection->regions.empty() || clicked_regionview == 0) {
2726 drag_info.copy = false;
2727 drag_info.item = item;
2728 drag_info.data = clicked_regionview;
2729 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2730 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2735 TimeAxisView* tvp = clicked_trackview;
2736 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2738 if (tv && tv->is_audio_track()) {
2739 speed = tv->get_diskstream()->speed();
2742 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2743 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2744 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2745 // we want a move threshold
2746 drag_info.want_move_threshold = true;
2748 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2750 begin_reversible_command (_("move region(s)"));
2754 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2756 if (selection->regions.empty() || clicked_regionview == 0) {
2760 drag_info.copy = true;
2761 drag_info.item = item;
2762 drag_info.data = clicked_regionview;
2766 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2767 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2770 if (atv && atv->is_audio_track()) {
2771 speed = atv->get_diskstream()->speed();
2774 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2775 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2776 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2777 // we want a move threshold
2778 drag_info.want_move_threshold = true;
2779 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2780 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2781 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2785 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2787 if (selection->regions.empty() || clicked_regionview == 0) {
2791 drag_info.copy = false;
2792 drag_info.item = item;
2793 drag_info.data = clicked_regionview;
2794 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2795 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2800 TimeAxisView* tvp = clicked_trackview;
2801 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2803 if (tv && tv->is_audio_track()) {
2804 speed = tv->get_diskstream()->speed();
2807 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2808 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2809 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2810 // we want a move threshold
2811 drag_info.want_move_threshold = true;
2812 drag_info.brushing = true;
2814 begin_reversible_command (_("Drag region brush"));
2818 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2822 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2823 nframes_t pending_region_position = 0;
2824 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2825 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2826 bool clamp_y_axis = false;
2827 vector<int32_t> height_list(512) ;
2828 vector<int32_t>::iterator j;
2830 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2832 drag_info.want_move_threshold = false; // don't copy again
2834 /* this is committed in the grab finished callback. */
2836 begin_reversible_command (_("Drag region copy"));
2838 /* duplicate the region(s) */
2840 vector<RegionView*> new_regionviews;
2842 set<boost::shared_ptr<Playlist> > affected_playlists;
2843 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2845 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2850 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
2851 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2853 insert_result = affected_playlists.insert (to_playlist);
2854 if (insert_result.second) {
2855 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
2858 latest_regionview = 0;
2860 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2862 /* create a new region with the same name. */
2864 // FIXME: ew. need a (virtual) Region::duplicate() or something?
2866 boost::shared_ptr<Region> newregion;
2867 boost::shared_ptr<Region> ar;
2869 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
2870 newregion = RegionFactory::create (ar);
2872 assert(newregion != 0);
2874 /* if the original region was locked, we don't care */
2876 newregion->set_locked (false);
2878 to_playlist->add_region (newregion, (nframes_t) (rv->region()->position() * atv->get_diskstream()->speed()));
2882 if (latest_regionview) {
2883 new_regionviews.push_back (latest_regionview);
2889 if (new_regionviews.empty()) {
2893 /* reset selection to new regionviews */
2895 selection->set (new_regionviews);
2897 /* reset drag_info data to reflect the fact that we are dragging the copies */
2899 drag_info.data = new_regionviews.front();
2900 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2903 /* Which trackview is this ? */
2905 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2906 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2908 /* The region motion is only processed if the pointer is over
2912 if (!tv || !tv->is_audio_track()) {
2913 /* To make sure we hide the verbose canvas cursor when the mouse is
2914 not held over and audiotrack.
2916 hide_verbose_canvas_cursor ();
2920 original_pointer_order = drag_info.last_trackview->order;
2922 /************************************************************
2924 ************************************************************/
2926 if (drag_info.brushing) {
2927 clamp_y_axis = true;
2932 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2934 int32_t children = 0, numtracks = 0;
2935 // XXX hard coding track limit, oh my, so very very bad
2936 bitset <1024> tracks (0x00);
2937 /* get a bitmask representing the visible tracks */
2939 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2940 TimeAxisView *tracklist_timeview;
2941 tracklist_timeview = (*i);
2942 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2943 list<TimeAxisView*> children_list;
2945 /* zeroes are audio tracks. ones are other types. */
2947 if (!atv2->hidden()) {
2949 if (visible_y_high < atv2->order) {
2950 visible_y_high = atv2->order;
2952 if (visible_y_low > atv2->order) {
2953 visible_y_low = atv2->order;
2956 if (!atv2->is_audio_track()) {
2957 tracks = tracks |= (0x01 << atv2->order);
2960 height_list[atv2->order] = (*i)->height;
2962 if ((children_list = atv2->get_child_list()).size() > 0) {
2963 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2964 tracks = tracks |= (0x01 << (atv2->order + children));
2965 height_list[atv2->order + children] = (*j)->height;
2973 /* find the actual span according to the canvas */
2975 canvas_pointer_y_span = pointer_y_span;
2976 if (drag_info.last_trackview->order >= tv->order) {
2978 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2979 if (height_list[y] == 0 ) {
2980 canvas_pointer_y_span--;
2985 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2986 if ( height_list[y] == 0 ) {
2987 canvas_pointer_y_span++;
2992 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2993 RegionView* rv2 = (*i);
2994 double ix1, ix2, iy1, iy2;
2997 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2998 rv2->get_canvas_group()->i2w (ix1, iy1);
2999 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3000 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3002 if (atv2->order != original_pointer_order) {
3003 /* this isn't the pointer track */
3005 if (canvas_pointer_y_span > 0) {
3007 /* moving up the canvas */
3008 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3010 int32_t visible_tracks = 0;
3011 while (visible_tracks < canvas_pointer_y_span ) {
3014 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3015 /* we're passing through a hidden track */
3020 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3021 clamp_y_axis = true;
3025 clamp_y_axis = true;
3028 } else if (canvas_pointer_y_span < 0) {
3030 /*moving down the canvas*/
3032 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3035 int32_t visible_tracks = 0;
3037 while (visible_tracks > canvas_pointer_y_span ) {
3040 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3044 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3045 clamp_y_axis = true;
3050 clamp_y_axis = true;
3056 /* this is the pointer's track */
3057 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3058 clamp_y_axis = true;
3059 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3060 clamp_y_axis = true;
3068 } else if (drag_info.last_trackview == tv) {
3069 clamp_y_axis = true;
3073 if (!clamp_y_axis) {
3074 drag_info.last_trackview = tv;
3077 /************************************************************
3079 ************************************************************/
3081 /* compute the amount of pointer motion in frames, and where
3082 the region would be if we moved it by that much.
3085 if (drag_info.move_threshold_passed) {
3087 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3089 nframes_t sync_frame;
3090 nframes_t sync_offset;
3093 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3095 sync_offset = rv->region()->sync_offset (sync_dir);
3096 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3098 /* we snap if the snap modifier is not enabled.
3101 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3102 snap_to (sync_frame);
3105 if (sync_frame - sync_offset <= sync_frame) {
3106 pending_region_position = sync_frame - (sync_dir*sync_offset);
3108 pending_region_position = 0;
3112 pending_region_position = 0;
3115 if (pending_region_position > max_frames - rv->region()->length()) {
3116 pending_region_position = drag_info.last_frame_position;
3119 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3121 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3123 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3124 to make it appear at the new location.
3127 if (pending_region_position > drag_info.last_frame_position) {
3128 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3130 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3133 drag_info.last_frame_position = pending_region_position;
3140 /* threshold not passed */
3145 /*************************************************************
3147 ************************************************************/
3149 if (x_delta == 0 && (pointer_y_span == 0)) {
3150 /* haven't reached next snap point, and we're not switching
3151 trackviews. nothing to do.
3157 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3159 RegionView* rv2 = (*i);
3161 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3163 double ix1, ix2, iy1, iy2;
3164 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3165 rv2->get_canvas_group()->i2w (ix1, iy1);
3174 /*************************************************************
3176 ************************************************************/
3178 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3179 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3181 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3183 RegionView* rv = (*i);
3184 double ix1, ix2, iy1, iy2;
3185 int32_t temp_pointer_y_span = pointer_y_span;
3187 /* get item BBox, which will be relative to parent. so we have
3188 to query on a child, then convert to world coordinates using
3192 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3193 rv->get_canvas_group()->i2w (ix1, iy1);
3194 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3195 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3196 AudioTimeAxisView* temp_atv;
3198 if ((pointer_y_span != 0) && !clamp_y_axis) {
3201 for (j = height_list.begin(); j!= height_list.end(); j++) {
3202 if (x == canvas_atv->order) {
3203 /* we found the track the region is on */
3204 if (x != original_pointer_order) {
3205 /*this isn't from the same track we're dragging from */
3206 temp_pointer_y_span = canvas_pointer_y_span;
3208 while (temp_pointer_y_span > 0) {
3209 /* we're moving up canvas-wise,
3210 so we need to find the next track height
3212 if (j != height_list.begin()) {
3215 if (x != original_pointer_order) {
3216 /* we're not from the dragged track, so ignore hidden tracks. */
3218 temp_pointer_y_span++;
3222 temp_pointer_y_span--;
3224 while (temp_pointer_y_span < 0) {
3226 if (x != original_pointer_order) {
3228 temp_pointer_y_span--;
3232 if (j != height_list.end()) {
3235 temp_pointer_y_span++;
3237 /* find out where we'll be when we move and set height accordingly */
3239 tvp2 = trackview_by_y_position (iy1 + y_delta);
3240 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3241 rv->set_height (temp_atv->height);
3243 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3244 personally, i think this can confuse things, but never mind.
3247 //const GdkColor& col (temp_atv->view->get_region_color());
3248 //rv->set_color (const_cast<GdkColor&>(col));
3255 /* prevent the regionview from being moved to before
3256 the zero position on the canvas.
3261 if (-x_delta > ix1) {
3264 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3265 x_delta = max_frames - rv->region()->last_frame();
3268 if (drag_info.first_move) {
3270 /* hide any dependent views */
3272 rv->get_time_axis_view().hide_dependent_views (*rv);
3274 /* this is subtle. raising the regionview itself won't help,
3275 because raise_to_top() just puts the item on the top of
3276 its parent's stack. so, we need to put the trackview canvas_display group
3277 on the top, since its parent is the whole canvas.
3280 rv->get_canvas_group()->raise_to_top();
3281 rv->get_time_axis_view().canvas_display->raise_to_top();
3282 cursor_group->raise_to_top();
3284 /* freeze the playlists from notifying till
3288 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3289 if (atv && atv->is_audio_track()) {
3290 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>(atv->get_diskstream()->playlist());
3292 /* only freeze and capture state once */
3294 insert_result = motion_frozen_playlists.insert (pl);
3295 if (insert_result.second) {
3297 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
3301 rv->region()->set_opaque(false);
3304 if (drag_info.brushing) {
3305 mouse_brush_insert_region (rv, pending_region_position);
3307 rv->move (x_delta, y_delta);
3311 if (drag_info.first_move) {
3312 cursor_group->raise_to_top();
3315 drag_info.first_move = false;
3317 if (x_delta != 0 && !drag_info.brushing) {
3318 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3324 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3327 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3328 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3329 bool nocommit = true;
3331 RouteTimeAxisView* atv;
3332 bool regionview_y_movement;
3333 bool regionview_x_movement;
3335 /* first_move is set to false if the regionview has been moved in the
3339 if (drag_info.first_move) {
3346 /* The regionview has been moved at some stage during the grab so we need
3347 to account for any mouse movement between this event and the last one.
3350 region_drag_motion_callback (item, event);
3352 if (drag_info.brushing) {
3353 /* all changes were made during motion event handlers */
3357 /* adjust for track speed */
3360 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3361 if (atv && atv->get_diskstream()) {
3362 speed = atv->get_diskstream()->speed();
3365 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3366 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3368 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3369 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3371 if (regionview_y_movement) {
3373 /* motion between tracks */
3375 list<RegionView*> new_selection;
3377 /* moved to a different audio track. */
3379 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3381 RegionView* rv2 = (*i);
3383 /* the region that used to be in the old playlist is not
3384 moved to the new one - we make a copy of it. as a result,
3385 any existing editor for the region should no longer be
3389 if (!drag_info.copy) {
3390 rv2->hide_region_editor();
3392 new_selection.push_back (rv2);
3396 /* first, freeze the target tracks */
3398 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3400 boost::shared_ptr<Playlist> from_playlist;
3401 boost::shared_ptr<Playlist> to_playlist;
3403 double ix1, ix2, iy1, iy2;
3405 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3406 (*i)->get_canvas_group()->i2w (ix1, iy1);
3407 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3408 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3410 (*i)->region()->set_opaque (true);
3412 from_playlist = (*i)->region()->playlist();
3413 to_playlist = atv2->playlist();
3415 /* the from_playlist was frozen in the "first_move" case
3416 of the motion handler. the insert can fail,
3417 but that doesn't matter. it just means
3418 we already have the playlist in the list.
3421 motion_frozen_playlists.insert (from_playlist);
3423 /* only freeze the to_playlist once */
3425 insert_result = motion_frozen_playlists.insert(to_playlist);
3426 if (insert_result.second) {
3427 to_playlist->freeze();
3428 session->add_command(new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3433 /* now do it again with the actual operations */
3435 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3437 boost::shared_ptr<Playlist> from_playlist;
3438 boost::shared_ptr<Playlist> to_playlist;
3440 double ix1, ix2, iy1, iy2;
3442 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3443 (*i)->get_canvas_group()->i2w (ix1, iy1);
3444 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3445 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3447 from_playlist = (*i)->region()->playlist();
3448 to_playlist = atv2->playlist();
3450 latest_regionview = 0;
3452 where = (nframes_t) (unit_to_frame (ix1) * speed);
3453 boost::shared_ptr<Region> new_region (RegionFactory::create ((*i)->region()));
3455 from_playlist->remove_region (((*i)->region()));
3457 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3458 to_playlist->add_region (new_region, where);
3461 if (latest_regionview) {
3462 selection->add (latest_regionview);
3468 /* motion within a single track */
3470 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3474 if (rv->region()->locked()) {
3478 if (regionview_x_movement) {
3479 double ownspeed = 1.0;
3480 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3482 if (atv && atv->get_diskstream()) {
3483 ownspeed = atv->get_diskstream()->speed();
3486 /* base the new region position on the current position of the regionview.*/
3488 double ix1, ix2, iy1, iy2;
3490 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3491 rv->get_canvas_group()->i2w (ix1, iy1);
3492 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3496 where = rv->region()->position();
3499 rv->get_time_axis_view().reveal_dependent_views (*rv);
3501 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3503 rv->region()->set_position (where, (void *) this);
3504 rv->region()->set_opaque (true);
3509 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3511 session->add_command (new MementoCommand<Playlist>(*((*p).get()), 0, & (*p)->get_state()));
3514 motion_frozen_playlists.clear ();
3517 commit_reversible_command ();
3522 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3524 /* Either add to or set the set the region selection, unless
3525 this is an alignment click (control used)
3528 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3529 TimeAxisView* tv = &rv.get_time_axis_view();
3530 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3532 if (atv && atv->is_audio_track()) {
3533 speed = atv->get_diskstream()->speed();
3536 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3538 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3540 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3542 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3546 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3552 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3558 nframes_t frame_rate;
3565 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3566 case AudioClock::BBT:
3567 session->bbt_time (frame, bbt);
3568 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3571 case AudioClock::SMPTE:
3572 session->smpte_time (frame, smpte);
3573 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3576 case AudioClock::MinSec:
3577 /* XXX this is copied from show_verbose_duration_cursor() */
3578 frame_rate = session->frame_rate();
3579 hours = frame / (frame_rate * 3600);
3580 frame = frame % (frame_rate * 3600);
3581 mins = frame / (frame_rate * 60);
3582 frame = frame % (frame_rate * 60);
3583 secs = (float) frame / (float) frame_rate;
3584 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3588 snprintf (buf, sizeof(buf), "%u", frame);
3592 if (xpos >= 0 && ypos >=0) {
3593 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3596 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3598 show_verbose_canvas_cursor ();
3602 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3609 nframes_t distance, frame_rate;
3611 Meter meter_at_start(session->tempo_map().meter_at(start));
3617 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3618 case AudioClock::BBT:
3619 session->bbt_time (start, sbbt);
3620 session->bbt_time (end, ebbt);
3623 /* XXX this computation won't work well if the
3624 user makes a selection that spans any meter changes.
3627 ebbt.bars -= sbbt.bars;
3628 if (ebbt.beats >= sbbt.beats) {
3629 ebbt.beats -= sbbt.beats;
3632 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3634 if (ebbt.ticks >= sbbt.ticks) {
3635 ebbt.ticks -= sbbt.ticks;
3638 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3641 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3644 case AudioClock::SMPTE:
3645 session->smpte_duration (end - start, smpte);
3646 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3649 case AudioClock::MinSec:
3650 /* XXX this stuff should be elsewhere.. */
3651 distance = end - start;
3652 frame_rate = session->frame_rate();
3653 hours = distance / (frame_rate * 3600);
3654 distance = distance % (frame_rate * 3600);
3655 mins = distance / (frame_rate * 60);
3656 distance = distance % (frame_rate * 60);
3657 secs = (float) distance / (float) frame_rate;
3658 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3662 snprintf (buf, sizeof(buf), "%u", end - start);
3666 if (xpos >= 0 && ypos >=0) {
3667 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3670 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3672 show_verbose_canvas_cursor ();
3676 Editor::collect_new_region_view (RegionView* rv)
3678 latest_regionview = rv;
3682 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3684 if (clicked_regionview == 0) {
3688 /* lets try to create new Region for the selection */
3690 vector<boost::shared_ptr<AudioRegion> > new_regions;
3691 create_region_from_selection (new_regions);
3693 if (new_regions.empty()) {
3697 /* XXX fix me one day to use all new regions */
3699 boost::shared_ptr<Region> region (new_regions.front());
3701 /* add it to the current stream/playlist.
3703 tricky: the streamview for the track will add a new regionview. we will
3704 catch the signal it sends when it creates the regionview to
3705 set the regionview we want to then drag.
3708 latest_regionview = 0;
3709 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3711 /* A selection grab currently creates two undo/redo operations, one for
3712 creating the new region and another for moving it.
3715 begin_reversible_command (_("selection grab"));
3717 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3719 XMLNode *before = &(playlist->get_state());
3720 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3721 XMLNode *after = &(playlist->get_state());
3722 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3724 commit_reversible_command ();
3728 if (latest_regionview == 0) {
3729 /* something went wrong */
3733 /* we need to deselect all other regionviews, and select this one
3734 i'm ignoring undo stuff, because the region creation will take care of it */
3735 selection->set (latest_regionview);
3737 drag_info.item = latest_regionview->get_canvas_group();
3738 drag_info.data = latest_regionview;
3739 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3740 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3744 drag_info.last_trackview = clicked_trackview;
3745 drag_info.last_frame_position = latest_regionview->region()->position();
3746 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3748 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3752 Editor::cancel_selection ()
3754 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3755 (*i)->hide_selection ();
3757 begin_reversible_command (_("cancel selection"));
3758 selection->clear ();
3759 clicked_selection = 0;
3760 commit_reversible_command ();
3764 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3766 nframes_t start = 0;
3773 drag_info.item = item;
3774 drag_info.motion_callback = &Editor::drag_selection;
3775 drag_info.finished_callback = &Editor::end_selection_op;
3780 case CreateSelection:
3781 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3782 drag_info.copy = true;
3784 drag_info.copy = false;
3786 start_grab (event, selector_cursor);
3789 case SelectionStartTrim:
3790 if (clicked_trackview) {
3791 clicked_trackview->order_selection_trims (item, true);
3793 start_grab (event, trimmer_cursor);
3794 start = selection->time[clicked_selection].start;
3795 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3798 case SelectionEndTrim:
3799 if (clicked_trackview) {
3800 clicked_trackview->order_selection_trims (item, false);
3802 start_grab (event, trimmer_cursor);
3803 end = selection->time[clicked_selection].end;
3804 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3808 start = selection->time[clicked_selection].start;
3810 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3814 if (selection_op == SelectionMove) {
3815 show_verbose_time_cursor(start, 10);
3817 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3822 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3824 nframes_t start = 0;
3827 nframes_t pending_position;
3829 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3830 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3833 pending_position = 0;
3836 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3837 snap_to (pending_position);
3840 /* only alter selection if the current frame is
3841 different from the last frame position (adjusted)
3844 if (pending_position == drag_info.last_pointer_frame) return;
3846 switch (selection_op) {
3847 case CreateSelection:
3849 if (drag_info.first_move) {
3850 snap_to (drag_info.grab_frame);
3853 if (pending_position < drag_info.grab_frame) {
3854 start = pending_position;
3855 end = drag_info.grab_frame;
3857 end = pending_position;
3858 start = drag_info.grab_frame;
3861 /* first drag: Either add to the selection
3862 or create a new selection->
3865 if (drag_info.first_move) {
3867 begin_reversible_command (_("range selection"));
3869 if (drag_info.copy) {
3870 /* adding to the selection */
3871 clicked_selection = selection->add (start, end);
3872 drag_info.copy = false;
3874 /* new selection-> */
3875 clicked_selection = selection->set (clicked_trackview, start, end);
3880 case SelectionStartTrim:
3882 if (drag_info.first_move) {
3883 begin_reversible_command (_("trim selection start"));
3886 start = selection->time[clicked_selection].start;
3887 end = selection->time[clicked_selection].end;
3889 if (pending_position > end) {
3892 start = pending_position;
3896 case SelectionEndTrim:
3898 if (drag_info.first_move) {
3899 begin_reversible_command (_("trim selection end"));
3902 start = selection->time[clicked_selection].start;
3903 end = selection->time[clicked_selection].end;
3905 if (pending_position < start) {
3908 end = pending_position;
3915 if (drag_info.first_move) {
3916 begin_reversible_command (_("move selection"));
3919 start = selection->time[clicked_selection].start;
3920 end = selection->time[clicked_selection].end;
3922 length = end - start;
3924 start = pending_position;
3927 end = start + length;
3932 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3933 start_canvas_autoscroll (1);
3937 selection->replace (clicked_selection, start, end);
3940 drag_info.last_pointer_frame = pending_position;
3941 drag_info.first_move = false;
3943 if (selection_op == SelectionMove) {
3944 show_verbose_time_cursor(start, 10);
3946 show_verbose_time_cursor(pending_position, 10);
3951 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3953 if (!drag_info.first_move) {
3954 drag_selection (item, event);
3955 /* XXX this is not object-oriented programming at all. ick */
3956 if (selection->time.consolidate()) {
3957 selection->TimeChanged ();
3959 commit_reversible_command ();
3961 /* just a click, no pointer movement.*/
3963 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3965 selection->clear_time();
3970 /* XXX what happens if its a music selection? */
3971 session->set_audio_range (selection->time);
3972 stop_canvas_autoscroll ();
3976 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3979 TimeAxisView* tvp = clicked_trackview;
3980 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3982 if (tv && tv->is_audio_track()) {
3983 speed = tv->get_diskstream()->speed();
3986 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3987 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3988 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3990 motion_frozen_playlists.clear();
3992 //drag_info.item = clicked_regionview->get_name_highlight();
3993 drag_info.item = item;
3994 drag_info.motion_callback = &Editor::trim_motion_callback;
3995 drag_info.finished_callback = &Editor::trim_finished_callback;
3997 start_grab (event, trimmer_cursor);
3999 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4000 trim_op = ContentsTrim;
4002 /* These will get overridden for a point trim.*/
4003 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4004 /* closer to start */
4005 trim_op = StartTrim;
4006 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4014 show_verbose_time_cursor(region_start, 10);
4017 show_verbose_time_cursor(region_end, 10);
4020 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4026 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4028 RegionView* rv = clicked_regionview;
4029 nframes_t frame_delta = 0;
4030 bool left_direction;
4031 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4033 /* snap modifier works differently here..
4034 its' current state has to be passed to the
4035 various trim functions in order to work properly
4039 TimeAxisView* tvp = clicked_trackview;
4040 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4041 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4043 if (tv && tv->is_audio_track()) {
4044 speed = tv->get_diskstream()->speed();
4047 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4048 left_direction = true;
4050 left_direction = false;
4054 snap_to (drag_info.current_pointer_frame);
4057 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4061 if (drag_info.first_move) {
4067 trim_type = "Region start trim";
4070 trim_type = "Region end trim";
4073 trim_type = "Region content trim";
4077 begin_reversible_command (trim_type);
4079 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4080 (*i)->region()->set_opaque(false);
4081 (*i)->region()->freeze ();
4083 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4085 arv->temporarily_hide_envelope ();
4087 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4088 insert_result = motion_frozen_playlists.insert (pl);
4089 if (insert_result.second) {
4090 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4095 if (left_direction) {
4096 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4098 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4103 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4106 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4107 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4113 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4116 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4117 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4124 bool swap_direction = false;
4126 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4127 swap_direction = true;
4130 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4131 i != selection->regions.by_layer().end(); ++i)
4133 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4141 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4144 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4147 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4151 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4152 drag_info.first_move = false;
4156 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4158 boost::shared_ptr<Region> region (rv.region());
4160 if (region->locked()) {
4164 nframes_t new_bound;
4167 TimeAxisView* tvp = clicked_trackview;
4168 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4170 if (tv && tv->is_audio_track()) {
4171 speed = tv->get_diskstream()->speed();
4174 if (left_direction) {
4175 if (swap_direction) {
4176 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4178 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4181 if (swap_direction) {
4182 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4184 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4189 snap_to (new_bound);
4191 region->trim_start ((nframes_t) (new_bound * speed), this);
4192 rv.region_changed (StartChanged);
4196 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4198 boost::shared_ptr<Region> region (rv.region());
4200 if (region->locked()) {
4204 nframes_t new_bound;
4207 TimeAxisView* tvp = clicked_trackview;
4208 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4210 if (tv && tv->is_audio_track()) {
4211 speed = tv->get_diskstream()->speed();
4214 if (left_direction) {
4215 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4217 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4221 snap_to (new_bound, (left_direction ? 0 : 1));
4224 region->trim_front ((nframes_t) (new_bound * speed), this);
4226 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4230 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4232 boost::shared_ptr<Region> region (rv.region());
4234 if (region->locked()) {
4238 nframes_t new_bound;
4241 TimeAxisView* tvp = clicked_trackview;
4242 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4244 if (tv && tv->is_audio_track()) {
4245 speed = tv->get_diskstream()->speed();
4248 if (left_direction) {
4249 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4251 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4255 snap_to (new_bound);
4257 region->trim_end ((nframes_t) (new_bound * speed), this);
4258 rv.region_changed (LengthChanged);
4262 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4264 if (!drag_info.first_move) {
4265 trim_motion_callback (item, event);
4267 if (!clicked_regionview->get_selected()) {
4268 thaw_region_after_trim (*clicked_regionview);
4271 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4272 i != selection->regions.by_layer().end(); ++i)
4274 thaw_region_after_trim (**i);
4275 (*i)->region()->set_opaque(true);
4279 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4281 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4284 motion_frozen_playlists.clear ();
4286 commit_reversible_command();
4288 /* no mouse movement */
4294 Editor::point_trim (GdkEvent* event)
4296 RegionView* rv = clicked_regionview;
4297 nframes_t new_bound = drag_info.current_pointer_frame;
4299 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4300 snap_to (new_bound);
4303 /* Choose action dependant on which button was pressed */
4304 switch (event->button.button) {
4306 trim_op = StartTrim;
4307 begin_reversible_command (_("Start point trim"));
4309 if (rv->get_selected()) {
4311 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4312 i != selection->regions.by_layer().end(); ++i)
4314 if (!(*i)->region()->locked()) {
4315 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4316 XMLNode &before = pl->get_state();
4317 (*i)->region()->trim_front (new_bound, this);
4318 XMLNode &after = pl->get_state();
4319 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4325 if (!rv->region()->locked()) {
4326 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4327 XMLNode &before = pl->get_state();
4328 rv->region()->trim_front (new_bound, this);
4329 XMLNode &after = pl->get_state();
4330 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4334 commit_reversible_command();
4339 begin_reversible_command (_("End point trim"));
4341 if (rv->get_selected()) {
4343 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4345 if (!(*i)->region()->locked()) {
4346 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4347 XMLNode &before = pl->get_state();
4348 (*i)->region()->trim_end (new_bound, this);
4349 XMLNode &after = pl->get_state();
4350 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4356 if (!rv->region()->locked()) {
4357 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4358 XMLNode &before = pl->get_state();
4359 rv->region()->trim_end (new_bound, this);
4360 XMLNode &after = pl->get_state();
4361 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4365 commit_reversible_command();
4374 Editor::thaw_region_after_trim (RegionView& rv)
4376 boost::shared_ptr<Region> region (rv.region());
4378 if (region->locked()) {
4382 region->thaw (_("trimmed region"));
4383 XMLNode &after = region->playlist()->get_state();
4384 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4386 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4388 arv->unhide_envelope ();
4392 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4397 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4398 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4402 Location* location = find_location_from_marker (marker, is_start);
4403 location->set_hidden (true, this);
4408 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4414 drag_info.item = item;
4415 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4416 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4418 range_marker_op = op;
4420 if (!temp_location) {
4421 temp_location = new Location;
4425 case CreateRangeMarker:
4426 case CreateTransportMarker:
4428 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4429 drag_info.copy = true;
4431 drag_info.copy = false;
4433 start_grab (event, selector_cursor);
4437 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4442 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4444 nframes_t start = 0;
4446 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4448 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4449 snap_to (drag_info.current_pointer_frame);
4452 /* only alter selection if the current frame is
4453 different from the last frame position.
4456 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4458 switch (range_marker_op) {
4459 case CreateRangeMarker:
4460 case CreateTransportMarker:
4461 if (drag_info.first_move) {
4462 snap_to (drag_info.grab_frame);
4465 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4466 start = drag_info.current_pointer_frame;
4467 end = drag_info.grab_frame;
4469 end = drag_info.current_pointer_frame;
4470 start = drag_info.grab_frame;
4473 /* first drag: Either add to the selection
4474 or create a new selection.
4477 if (drag_info.first_move) {
4479 temp_location->set (start, end);
4483 update_marker_drag_item (temp_location);
4484 range_marker_drag_rect->show();
4485 range_marker_drag_rect->raise_to_top();
4491 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4492 start_canvas_autoscroll (1);
4496 temp_location->set (start, end);
4498 double x1 = frame_to_pixel (start);
4499 double x2 = frame_to_pixel (end);
4500 crect->property_x1() = x1;
4501 crect->property_x2() = x2;
4503 update_marker_drag_item (temp_location);
4506 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4507 drag_info.first_move = false;
4509 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4514 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4516 Location * newloc = 0;
4519 if (!drag_info.first_move) {
4520 drag_range_markerbar_op (item, event);
4522 switch (range_marker_op) {
4523 case CreateRangeMarker:
4525 begin_reversible_command (_("new range marker"));
4526 XMLNode &before = session->locations()->get_state();
4527 session->locations()->next_available_name(rangename,"unnamed");
4528 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4529 session->locations()->add (newloc, true);
4530 XMLNode &after = session->locations()->get_state();
4531 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4532 commit_reversible_command ();
4534 range_bar_drag_rect->hide();
4535 range_marker_drag_rect->hide();
4539 case CreateTransportMarker:
4540 // popup menu to pick loop or punch
4541 new_transport_marker_context_menu (&event->button, item);
4546 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4548 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4553 start = session->locations()->first_mark_before (drag_info.grab_frame);
4554 end = session->locations()->first_mark_after (drag_info.grab_frame);
4556 if (end == max_frames) {
4557 end = session->current_end_frame ();
4561 start = session->current_start_frame ();
4564 switch (mouse_mode) {
4566 /* find the two markers on either side and then make the selection from it */
4567 cerr << "select between " << start << " .. " << end << endl;
4568 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4572 /* find the two markers on either side of the click and make the range out of it */
4573 selection->set (0, start, end);
4582 stop_canvas_autoscroll ();
4588 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4590 drag_info.item = item;
4591 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4592 drag_info.finished_callback = &Editor::end_mouse_zoom;
4594 start_grab (event, zoom_cursor);
4596 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4600 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4605 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4606 snap_to (drag_info.current_pointer_frame);
4608 if (drag_info.first_move) {
4609 snap_to (drag_info.grab_frame);
4613 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4615 /* base start and end on initial click position */
4616 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4617 start = drag_info.current_pointer_frame;
4618 end = drag_info.grab_frame;
4620 end = drag_info.current_pointer_frame;
4621 start = drag_info.grab_frame;
4626 if (drag_info.first_move) {
4628 zoom_rect->raise_to_top();
4631 reposition_zoom_rect(start, end);
4633 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4634 drag_info.first_move = false;
4636 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4641 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4643 if (!drag_info.first_move) {
4644 drag_mouse_zoom (item, event);
4646 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4647 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4649 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4652 temporal_zoom_to_frame (false, drag_info.grab_frame);
4654 temporal_zoom_step (false);
4655 center_screen (drag_info.grab_frame);
4663 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4665 double x1 = frame_to_pixel (start);
4666 double x2 = frame_to_pixel (end);
4667 double y2 = full_canvas_height - 1.0;
4669 zoom_rect->property_x1() = x1;
4670 zoom_rect->property_y1() = 1.0;
4671 zoom_rect->property_x2() = x2;
4672 zoom_rect->property_y2() = y2;
4676 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4678 drag_info.item = item;
4679 drag_info.motion_callback = &Editor::drag_rubberband_select;
4680 drag_info.finished_callback = &Editor::end_rubberband_select;
4682 start_grab (event, cross_hair_cursor);
4684 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4688 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4695 /* use a bigger drag threshold than the default */
4697 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4701 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4702 if (drag_info.first_move) {
4703 snap_to (drag_info.grab_frame);
4705 snap_to (drag_info.current_pointer_frame);
4708 /* base start and end on initial click position */
4710 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4711 start = drag_info.current_pointer_frame;
4712 end = drag_info.grab_frame;
4714 end = drag_info.current_pointer_frame;
4715 start = drag_info.grab_frame;
4718 if (drag_info.current_pointer_y < drag_info.grab_y) {
4719 y1 = drag_info.current_pointer_y;
4720 y2 = drag_info.grab_y;
4722 y2 = drag_info.current_pointer_y;
4723 y1 = drag_info.grab_y;
4727 if (start != end || y1 != y2) {
4729 double x1 = frame_to_pixel (start);
4730 double x2 = frame_to_pixel (end);
4732 rubberband_rect->property_x1() = x1;
4733 rubberband_rect->property_y1() = y1;
4734 rubberband_rect->property_x2() = x2;
4735 rubberband_rect->property_y2() = y2;
4737 rubberband_rect->show();
4738 rubberband_rect->raise_to_top();
4740 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4741 drag_info.first_move = false;
4743 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4748 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4750 if (!drag_info.first_move) {
4752 drag_rubberband_select (item, event);
4755 if (drag_info.current_pointer_y < drag_info.grab_y) {
4756 y1 = drag_info.current_pointer_y;
4757 y2 = drag_info.grab_y;
4760 y2 = drag_info.current_pointer_y;
4761 y1 = drag_info.grab_y;
4765 Selection::Operation op = Keyboard::selection_type (event->button.state);
4768 begin_reversible_command (_("rubberband selection"));
4770 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4771 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4773 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4777 commit_reversible_command ();
4781 selection->clear_tracks();
4782 selection->clear_regions();
4783 selection->clear_points ();
4784 selection->clear_lines ();
4787 rubberband_rect->hide();
4792 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4794 using namespace Gtkmm2ext;
4796 ArdourPrompter prompter (false);
4798 prompter.set_prompt (_("Name for region:"));
4799 prompter.set_initial_text (clicked_regionview->region()->name());
4800 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4801 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4802 prompter.show_all ();
4803 switch (prompter.run ()) {
4804 case Gtk::RESPONSE_ACCEPT:
4806 prompter.get_result(str);
4808 clicked_regionview->region()->set_name (str);
4816 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4818 drag_info.item = item;
4819 drag_info.motion_callback = &Editor::time_fx_motion;
4820 drag_info.finished_callback = &Editor::end_time_fx;
4824 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4828 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4830 RegionView* rv = clicked_regionview;
4832 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4833 snap_to (drag_info.current_pointer_frame);
4836 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4840 if (drag_info.current_pointer_frame > rv->region()->position()) {
4841 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4844 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4845 drag_info.first_move = false;
4847 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4851 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4853 clicked_regionview->get_time_axis_view().hide_timestretch ();
4855 if (drag_info.first_move) {
4859 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4860 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4862 begin_reversible_command (_("timestretch"));
4864 if (run_timestretch (selection->regions, percentage) == 0) {
4865 session->commit_reversible_command ();
4870 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4872 /* no brushing without a useful snap setting */
4875 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4878 switch (snap_mode) {
4880 return; /* can't work because it allows region to be placed anywhere */
4885 switch (snap_type) {
4888 case SnapToEditCursor:
4895 /* don't brush a copy over the original */
4897 if (pos == rv->region()->position()) {
4901 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4903 if (atv == 0 || !atv->is_audio_track()) {
4907 boost::shared_ptr<Playlist> playlist = atv->playlist();
4908 double speed = atv->get_diskstream()->speed();
4910 XMLNode &before = playlist->get_state();
4911 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4912 XMLNode &after = playlist->get_state();
4913 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4915 // playlist is frozen, so we have to update manually
4917 playlist->Modified(); /* EMIT SIGNAL */
4921 Editor::track_height_step_timeout ()
4924 struct timeval delta;
4926 gettimeofday (&now, 0);
4927 timersub (&now, &last_track_height_step_timestamp, &delta);
4929 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4930 current_stepping_trackview = 0;