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 /* no selection action on modified button-2 or button-3 events */
322 Selection::Operation op = Keyboard::selection_type (event->button.state);
323 bool press = (event->type == GDK_BUTTON_PRESS);
325 begin_reversible_command (_("select on click"));
329 if (mouse_mode != MouseRange) {
330 commit = set_selected_regionview_from_click (press, op, true);
331 } else if (event->type == GDK_BUTTON_PRESS) {
332 commit = set_selected_track_from_click (press, op, false);
336 case RegionViewNameHighlight:
338 if (mouse_mode != MouseRange) {
339 commit = set_selected_regionview_from_click (press, op, true);
340 } else if (event->type == GDK_BUTTON_PRESS) {
341 commit = set_selected_track_from_click (press, op, false);
345 case FadeInHandleItem:
347 case FadeOutHandleItem:
349 if (mouse_mode != MouseRange) {
350 commit = set_selected_regionview_from_click (press, op, true);
351 } else if (event->type == GDK_BUTTON_PRESS) {
352 commit = set_selected_track_from_click (press, op, false);
356 case GainAutomationControlPointItem:
357 case PanAutomationControlPointItem:
358 case RedirectAutomationControlPointItem:
359 if (mouse_mode != MouseRange) {
360 commit = set_selected_control_point_from_click (op, false);
365 /* for context click or range selection, select track */
366 if (event->button.button == 3) {
367 commit = set_selected_track_from_click (press, op, true);
368 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
369 commit = set_selected_track_from_click (press, op, false);
373 case AutomationTrackItem:
374 commit = set_selected_track_from_click (press, op, true);
382 commit_reversible_command ();
387 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
389 nframes_t where = event_frame (event, 0, 0);
391 track_canvas.grab_focus();
393 if (session && session->actively_recording()) {
397 button_selection (item, event, item_type);
399 if (drag_info.item == 0 &&
400 (Keyboard::is_delete_event (&event->button) ||
401 Keyboard::is_context_menu_event (&event->button) ||
402 Keyboard::is_edit_event (&event->button))) {
404 /* handled by button release */
408 switch (event->button.button) {
411 if (event->type == GDK_BUTTON_PRESS) {
413 if (drag_info.item) {
414 drag_info.item->ungrab (event->button.time);
417 /* single mouse clicks on any of these item types operate
418 independent of mouse mode, mostly because they are
419 not on the main track canvas or because we want
425 case PlayheadCursorItem:
426 start_cursor_grab (item, event);
430 if (Keyboard::modifier_state_equals (event->button.state,
431 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
432 hide_marker (item, event);
434 start_marker_grab (item, event);
438 case TempoMarkerItem:
439 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
440 start_tempo_marker_copy_grab (item, event);
442 start_tempo_marker_grab (item, event);
446 case MeterMarkerItem:
447 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
448 start_meter_marker_copy_grab (item, event);
450 start_meter_marker_grab (item, event);
460 case RangeMarkerBarItem:
461 start_range_markerbar_op (item, event, CreateRangeMarker);
465 case TransportMarkerBarItem:
466 start_range_markerbar_op (item, event, CreateTransportMarker);
475 switch (mouse_mode) {
478 case StartSelectionTrimItem:
479 start_selection_op (item, event, SelectionStartTrim);
482 case EndSelectionTrimItem:
483 start_selection_op (item, event, SelectionEndTrim);
487 if (Keyboard::modifier_state_contains
488 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
489 // contains and not equals because I can't use alt as a modifier alone.
490 start_selection_grab (item, event);
491 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
492 /* grab selection for moving */
493 start_selection_op (item, event, SelectionMove);
496 /* this was debated, but decided the more common action was to
497 make a new selection */
498 start_selection_op (item, event, CreateSelection);
503 start_selection_op (item, event, CreateSelection);
509 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
510 event->type == GDK_BUTTON_PRESS) {
512 start_rubberband_select (item, event);
514 } else if (event->type == GDK_BUTTON_PRESS) {
517 case FadeInHandleItem:
518 start_fade_in_grab (item, event);
521 case FadeOutHandleItem:
522 start_fade_out_grab (item, event);
526 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
527 start_region_copy_grab (item, event);
528 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
529 start_region_brush_grab (item, event);
531 start_region_grab (item, event);
535 case RegionViewNameHighlight:
536 start_trim (item, event);
541 /* rename happens on edit clicks */
542 start_trim (clicked_regionview->get_name_highlight(), event);
546 case GainAutomationControlPointItem:
547 case PanAutomationControlPointItem:
548 case RedirectAutomationControlPointItem:
549 start_control_point_grab (item, event);
553 case GainAutomationLineItem:
554 case PanAutomationLineItem:
555 case RedirectAutomationLineItem:
556 start_line_grab_from_line (item, event);
561 case AutomationTrackItem:
562 start_rubberband_select (item, event);
565 /* <CMT Additions> */
566 case ImageFrameHandleStartItem:
567 imageframe_start_handle_op(item, event) ;
570 case ImageFrameHandleEndItem:
571 imageframe_end_handle_op(item, event) ;
574 case MarkerViewHandleStartItem:
575 markerview_item_start_handle_op(item, event) ;
578 case MarkerViewHandleEndItem:
579 markerview_item_end_handle_op(item, event) ;
582 /* </CMT Additions> */
584 /* <CMT Additions> */
586 start_markerview_grab(item, event) ;
589 start_imageframe_grab(item, event) ;
591 /* </CMT Additions> */
607 // start_line_grab_from_regionview (item, event);
610 case GainControlPointItem:
611 start_control_point_grab (item, event);
615 start_line_grab_from_line (item, event);
618 case GainAutomationControlPointItem:
619 case PanAutomationControlPointItem:
620 case RedirectAutomationControlPointItem:
621 start_control_point_grab (item, event);
632 case GainAutomationControlPointItem:
633 case PanAutomationControlPointItem:
634 case RedirectAutomationControlPointItem:
635 start_control_point_grab (item, event);
638 case GainAutomationLineItem:
639 case PanAutomationLineItem:
640 case RedirectAutomationLineItem:
641 start_line_grab_from_line (item, event);
645 // XXX need automation mode to identify which
647 // start_line_grab_from_regionview (item, event);
657 if (event->type == GDK_BUTTON_PRESS) {
658 start_mouse_zoom (item, event);
665 if (item_type == RegionItem) {
666 start_time_fx (item, event);
671 /* handled in release */
680 switch (mouse_mode) {
682 if (event->type == GDK_BUTTON_PRESS) {
685 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
686 start_region_copy_grab (item, event);
688 start_region_grab (item, event);
692 case GainAutomationControlPointItem:
693 case PanAutomationControlPointItem:
694 case RedirectAutomationControlPointItem:
695 start_control_point_grab (item, event);
706 case RegionViewNameHighlight:
707 start_trim (item, event);
712 start_trim (clicked_regionview->get_name_highlight(), event);
723 if (event->type == GDK_BUTTON_PRESS) {
724 /* relax till release */
731 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
732 temporal_zoom_session();
734 temporal_zoom_to_frame (true, event_frame(event));
749 switch (mouse_mode) {
751 //temporal_zoom_to_frame (true, where);
752 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
753 temporal_zoom_to_frame (true, where);
756 temporal_zoom_step (true);
761 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
762 scroll_backward (0.6f);
765 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
766 scroll_tracks_up_line ();
768 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
769 if (clicked_trackview) {
770 if (!current_stepping_trackview) {
771 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
772 current_stepping_trackview = clicked_trackview;
774 gettimeofday (&last_track_height_step_timestamp, 0);
775 current_stepping_trackview->step_height (true);
778 else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
779 temporal_zoom_to_frame (true, where);
786 switch (mouse_mode) {
788 // temporal_zoom_to_frame (false, where);
789 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
790 temporal_zoom_to_frame (false, where);
793 temporal_zoom_step (false);
798 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
799 scroll_forward (0.6f);
802 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
803 scroll_tracks_down_line ();
805 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
806 if (clicked_trackview) {
807 if (!current_stepping_trackview) {
808 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
809 current_stepping_trackview = clicked_trackview;
811 gettimeofday (&last_track_height_step_timestamp, 0);
812 current_stepping_trackview->step_height (false);
814 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
815 temporal_zoom_to_frame (false, where);
830 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
832 nframes_t where = event_frame (event, 0, 0);
834 /* no action if we're recording */
836 if (session && session->actively_recording()) {
840 /* first, see if we're finishing a drag ... */
842 if (drag_info.item) {
843 if (end_grab (item, event)) {
844 /* grab dragged, so do nothing else */
849 button_selection (item, event, item_type);
851 /* edit events get handled here */
853 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
859 case TempoMarkerItem:
860 edit_tempo_marker (item);
863 case MeterMarkerItem:
864 edit_meter_marker (item);
868 if (clicked_regionview->name_active()) {
869 return mouse_rename_region (item, event);
879 /* context menu events get handled here */
881 if (Keyboard::is_context_menu_event (&event->button)) {
883 if (drag_info.item == 0) {
885 /* no matter which button pops up the context menu, tell the menu
886 widget to use button 1 to drive menu selection.
891 case FadeInHandleItem:
893 case FadeOutHandleItem:
894 popup_fade_context_menu (1, event->button.time, item, item_type);
898 popup_track_context_menu (1, event->button.time, item_type, false, where);
902 case RegionViewNameHighlight:
904 popup_track_context_menu (1, event->button.time, item_type, false, where);
908 popup_track_context_menu (1, event->button.time, item_type, true, where);
911 case AutomationTrackItem:
912 popup_track_context_menu (1, event->button.time, item_type, false, where);
916 case RangeMarkerBarItem:
917 case TransportMarkerBarItem:
920 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
924 marker_context_menu (&event->button, item);
927 case TempoMarkerItem:
928 tm_marker_context_menu (&event->button, item);
931 case MeterMarkerItem:
932 tm_marker_context_menu (&event->button, item);
935 case CrossfadeViewItem:
936 popup_track_context_menu (1, event->button.time, item_type, false, where);
939 /* <CMT Additions> */
941 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
943 case ImageFrameTimeAxisItem:
944 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
947 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
949 case MarkerTimeAxisItem:
950 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
952 /* <CMT Additions> */
963 /* delete events get handled here */
965 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
968 case TempoMarkerItem:
969 remove_tempo_marker (item);
972 case MeterMarkerItem:
973 remove_meter_marker (item);
977 remove_marker (*item, event);
981 if (mouse_mode == MouseObject) {
982 remove_clicked_region ();
986 case GainControlPointItem:
987 if (mouse_mode == MouseGain) {
988 remove_gain_control_point (item, event);
992 case GainAutomationControlPointItem:
993 case PanAutomationControlPointItem:
994 case RedirectAutomationControlPointItem:
995 remove_control_point (item, event);
1004 switch (event->button.button) {
1007 switch (item_type) {
1008 /* see comments in button_press_handler */
1009 case EditCursorItem:
1010 case PlayheadCursorItem:
1013 case GainAutomationLineItem:
1014 case PanAutomationLineItem:
1015 case RedirectAutomationLineItem:
1016 case StartSelectionTrimItem:
1017 case EndSelectionTrimItem:
1021 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1022 snap_to (where, 0, true);
1024 mouse_add_new_marker (where);
1028 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1031 mouse_add_new_tempo_event (where);
1035 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1043 switch (mouse_mode) {
1045 switch (item_type) {
1046 case AutomationTrackItem:
1047 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1061 // Gain only makes sense for audio regions
1062 if ( ! dynamic_cast<AudioRegionView*>(clicked_regionview))
1065 switch (item_type) {
1067 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1071 case AutomationTrackItem:
1072 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1073 add_automation_event (item, event, where, event->button.y);
1082 switch (item_type) {
1084 audition_selected_region ();
1101 switch (mouse_mode) {
1104 switch (item_type) {
1106 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1108 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1111 // Button2 click is unused
1124 // x_style_paste (where, 1.0);
1144 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1150 switch (item_type) {
1151 case GainControlPointItem:
1152 if (mouse_mode == MouseGain) {
1153 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1154 cp->set_visible (true);
1158 at_y = cp->get_y ();
1159 cp->item->i2w (at_x, at_y);
1163 fraction = 1.0 - (cp->get_y() / cp->line.height());
1165 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1166 show_verbose_canvas_cursor ();
1168 if (is_drawable()) {
1169 track_canvas.get_window()->set_cursor (*fader_cursor);
1174 case GainAutomationControlPointItem:
1175 case PanAutomationControlPointItem:
1176 case RedirectAutomationControlPointItem:
1177 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1178 cp->set_visible (true);
1182 at_y = cp->get_y ();
1183 cp->item->i2w (at_x, at_y);
1187 fraction = 1.0 - (cp->get_y() / cp->line.height());
1189 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1190 show_verbose_canvas_cursor ();
1192 if (is_drawable()) {
1193 track_canvas.get_window()->set_cursor (*fader_cursor);
1198 if (mouse_mode == MouseGain) {
1199 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1201 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1202 if (is_drawable()) {
1203 track_canvas.get_window()->set_cursor (*fader_cursor);
1208 case GainAutomationLineItem:
1209 case RedirectAutomationLineItem:
1210 case PanAutomationLineItem:
1212 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1214 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1216 if (is_drawable()) {
1217 track_canvas.get_window()->set_cursor (*fader_cursor);
1221 case RegionViewNameHighlight:
1222 if (is_drawable() && mouse_mode == MouseObject) {
1223 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1227 case StartSelectionTrimItem:
1228 case EndSelectionTrimItem:
1229 /* <CMT Additions> */
1230 case ImageFrameHandleStartItem:
1231 case ImageFrameHandleEndItem:
1232 case MarkerViewHandleStartItem:
1233 case MarkerViewHandleEndItem:
1234 /* </CMT Additions> */
1236 if (is_drawable()) {
1237 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1241 case EditCursorItem:
1242 case PlayheadCursorItem:
1243 if (is_drawable()) {
1244 track_canvas.get_window()->set_cursor (*grabber_cursor);
1248 case RegionViewName:
1250 /* when the name is not an active item, the entire name highlight is for trimming */
1252 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1253 if (mouse_mode == MouseObject && is_drawable()) {
1254 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1260 case AutomationTrackItem:
1261 if (is_drawable()) {
1262 Gdk::Cursor *cursor;
1263 switch (mouse_mode) {
1265 cursor = selector_cursor;
1268 cursor = zoom_cursor;
1271 cursor = cross_hair_cursor;
1275 track_canvas.get_window()->set_cursor (*cursor);
1277 AutomationTimeAxisView* atv;
1278 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1279 clear_entered_track = false;
1280 set_entered_track (atv);
1286 case RangeMarkerBarItem:
1287 case TransportMarkerBarItem:
1290 if (is_drawable()) {
1291 time_canvas.get_window()->set_cursor (*timebar_cursor);
1296 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1299 marker->set_color_rgba (color_map[cEnteredMarker]);
1301 case MeterMarkerItem:
1302 case TempoMarkerItem:
1303 if (is_drawable()) {
1304 time_canvas.get_window()->set_cursor (*timebar_cursor);
1307 case FadeInHandleItem:
1308 case FadeOutHandleItem:
1309 if (mouse_mode == MouseObject) {
1310 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1312 rect->property_fill_color_rgba() = 0;
1313 rect->property_outline_pixels() = 1;
1322 /* second pass to handle entered track status in a comprehensible way.
1325 switch (item_type) {
1327 case GainAutomationLineItem:
1328 case RedirectAutomationLineItem:
1329 case PanAutomationLineItem:
1330 case GainControlPointItem:
1331 case GainAutomationControlPointItem:
1332 case PanAutomationControlPointItem:
1333 case RedirectAutomationControlPointItem:
1334 /* these do not affect the current entered track state */
1335 clear_entered_track = false;
1338 case AutomationTrackItem:
1339 /* handled above already */
1343 set_entered_track (0);
1351 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1360 switch (item_type) {
1361 case GainControlPointItem:
1362 case GainAutomationControlPointItem:
1363 case PanAutomationControlPointItem:
1364 case RedirectAutomationControlPointItem:
1365 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1366 if (cp->line.npoints() > 1) {
1367 if (!cp->selected) {
1368 cp->set_visible (false);
1372 if (is_drawable()) {
1373 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1376 hide_verbose_canvas_cursor ();
1379 case RegionViewNameHighlight:
1380 case StartSelectionTrimItem:
1381 case EndSelectionTrimItem:
1382 case EditCursorItem:
1383 case PlayheadCursorItem:
1384 /* <CMT Additions> */
1385 case ImageFrameHandleStartItem:
1386 case ImageFrameHandleEndItem:
1387 case MarkerViewHandleStartItem:
1388 case MarkerViewHandleEndItem:
1389 /* </CMT Additions> */
1390 if (is_drawable()) {
1391 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1396 case GainAutomationLineItem:
1397 case RedirectAutomationLineItem:
1398 case PanAutomationLineItem:
1399 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1401 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1403 line->property_fill_color_rgba() = al->get_line_color();
1405 if (is_drawable()) {
1406 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1410 case RegionViewName:
1411 /* see enter_handler() for notes */
1412 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1413 if (is_drawable() && mouse_mode == MouseObject) {
1414 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1419 case RangeMarkerBarItem:
1420 case TransportMarkerBarItem:
1424 if (is_drawable()) {
1425 time_canvas.get_window()->set_cursor (*timebar_cursor);
1430 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1433 loc = find_location_from_marker (marker, is_start);
1434 if (loc) location_flags_changed (loc, this);
1436 case MeterMarkerItem:
1437 case TempoMarkerItem:
1439 if (is_drawable()) {
1440 time_canvas.get_window()->set_cursor (*timebar_cursor);
1445 case FadeInHandleItem:
1446 case FadeOutHandleItem:
1447 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1449 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1451 rect->property_fill_color_rgba() = rv->get_fill_color();
1452 rect->property_outline_pixels() = 0;
1457 case AutomationTrackItem:
1458 if (is_drawable()) {
1459 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1460 clear_entered_track = true;
1461 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1473 Editor::left_automation_track ()
1475 if (clear_entered_track) {
1476 set_entered_track (0);
1477 clear_entered_track = false;
1483 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1487 /* We call this so that MOTION_NOTIFY events continue to be
1488 delivered to the canvas. We need to do this because we set
1489 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1490 the density of the events, at the expense of a round-trip
1491 to the server. Given that this will mostly occur on cases
1492 where DISPLAY = :0.0, and given the cost of what the motion
1493 event might do, its a good tradeoff.
1496 track_canvas.get_pointer (x, y);
1498 if (current_stepping_trackview) {
1499 /* don't keep the persistent stepped trackview if the mouse moves */
1500 current_stepping_trackview = 0;
1501 step_timeout.disconnect ();
1504 if (session && session->actively_recording()) {
1505 /* Sorry. no dragging stuff around while we record */
1509 drag_info.item_type = item_type;
1510 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1511 &drag_info.current_pointer_y);
1513 if (!from_autoscroll && drag_info.item) {
1514 /* item != 0 is the best test i can think of for dragging.
1516 if (!drag_info.move_threshold_passed) {
1518 drag_info.move_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1520 // and change the initial grab loc/frame if this drag info wants us to
1522 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1523 drag_info.grab_frame = drag_info.current_pointer_frame;
1524 drag_info.grab_x = drag_info.current_pointer_x;
1525 drag_info.grab_y = drag_info.current_pointer_y;
1526 drag_info.last_pointer_frame = drag_info.grab_frame;
1527 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1532 switch (item_type) {
1533 case PlayheadCursorItem:
1534 case EditCursorItem:
1536 case GainControlPointItem:
1537 case RedirectAutomationControlPointItem:
1538 case GainAutomationControlPointItem:
1539 case PanAutomationControlPointItem:
1540 case TempoMarkerItem:
1541 case MeterMarkerItem:
1542 case RegionViewNameHighlight:
1543 case StartSelectionTrimItem:
1544 case EndSelectionTrimItem:
1547 case RedirectAutomationLineItem:
1548 case GainAutomationLineItem:
1549 case PanAutomationLineItem:
1550 case FadeInHandleItem:
1551 case FadeOutHandleItem:
1552 /* <CMT Additions> */
1553 case ImageFrameHandleStartItem:
1554 case ImageFrameHandleEndItem:
1555 case MarkerViewHandleStartItem:
1556 case MarkerViewHandleEndItem:
1557 /* </CMT Additions> */
1558 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1559 (event->motion.state & Gdk::BUTTON2_MASK))) {
1560 if (!from_autoscroll) {
1561 maybe_autoscroll (event);
1563 (this->*(drag_info.motion_callback)) (item, event);
1572 switch (mouse_mode) {
1577 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1578 (event->motion.state & GDK_BUTTON2_MASK))) {
1579 if (!from_autoscroll) {
1580 maybe_autoscroll (event);
1582 (this->*(drag_info.motion_callback)) (item, event);
1593 track_canvas_motion (event);
1594 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1602 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1604 if (drag_info.item == 0) {
1605 fatal << _("programming error: start_grab called without drag item") << endmsg;
1611 cursor = grabber_cursor;
1614 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1616 if (event->button.button == 2) {
1617 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1618 drag_info.y_constrained = true;
1619 drag_info.x_constrained = false;
1621 drag_info.y_constrained = false;
1622 drag_info.x_constrained = true;
1625 drag_info.x_constrained = false;
1626 drag_info.y_constrained = false;
1629 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1630 drag_info.last_pointer_frame = drag_info.grab_frame;
1631 drag_info.current_pointer_frame = drag_info.grab_frame;
1632 drag_info.current_pointer_x = drag_info.grab_x;
1633 drag_info.current_pointer_y = drag_info.grab_y;
1634 drag_info.cumulative_x_drag = 0;
1635 drag_info.cumulative_y_drag = 0;
1636 drag_info.first_move = true;
1637 drag_info.move_threshold_passed = false;
1638 drag_info.want_move_threshold = false;
1639 drag_info.pointer_frame_offset = 0;
1640 drag_info.brushing = false;
1641 drag_info.copied_location = 0;
1643 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1645 event->button.time);
1647 if (session && session->transport_rolling()) {
1648 drag_info.was_rolling = true;
1650 drag_info.was_rolling = false;
1653 switch (snap_type) {
1654 case SnapToRegionStart:
1655 case SnapToRegionEnd:
1656 case SnapToRegionSync:
1657 case SnapToRegionBoundary:
1658 build_region_boundary_cache ();
1666 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1668 drag_info.item->ungrab (0);
1669 drag_info.item = new_item;
1672 cursor = grabber_cursor;
1675 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1679 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1681 bool did_drag = false;
1683 stop_canvas_autoscroll ();
1685 if (drag_info.item == 0) {
1689 drag_info.item->ungrab (event->button.time);
1691 if (drag_info.finished_callback) {
1692 (this->*(drag_info.finished_callback)) (item, event);
1695 did_drag = !drag_info.first_move;
1697 hide_verbose_canvas_cursor();
1700 drag_info.copy = false;
1701 drag_info.motion_callback = 0;
1702 drag_info.finished_callback = 0;
1703 drag_info.last_trackview = 0;
1704 drag_info.last_frame_position = 0;
1705 drag_info.grab_frame = 0;
1706 drag_info.last_pointer_frame = 0;
1707 drag_info.current_pointer_frame = 0;
1708 drag_info.brushing = false;
1710 if (drag_info.copied_location) {
1711 delete drag_info.copied_location;
1712 drag_info.copied_location = 0;
1719 Editor::set_edit_cursor (GdkEvent* event)
1721 nframes_t pointer_frame = event_frame (event);
1723 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1724 if (snap_type != SnapToEditCursor) {
1725 snap_to (pointer_frame);
1729 edit_cursor->set_position (pointer_frame);
1730 edit_cursor_clock.set (pointer_frame);
1734 Editor::set_playhead_cursor (GdkEvent* event)
1736 nframes_t pointer_frame = event_frame (event);
1738 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1739 snap_to (pointer_frame);
1743 session->request_locate (pointer_frame, session->transport_rolling());
1748 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1750 drag_info.item = item;
1751 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1752 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1756 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1757 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1761 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1763 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1767 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1769 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1771 nframes_t fade_length;
1773 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1774 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1780 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1784 if (pos < (arv->region()->position() + 64)) {
1785 fade_length = 64; // this should be a minimum defined somewhere
1786 } else if (pos > arv->region()->last_frame()) {
1787 fade_length = arv->region()->length();
1789 fade_length = pos - arv->region()->position();
1791 /* mapover the region selection */
1793 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1795 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1801 tmp->reset_fade_in_shape_width (fade_length);
1804 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1806 drag_info.first_move = false;
1810 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1812 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1814 nframes_t fade_length;
1816 if (drag_info.first_move) return;
1818 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1819 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1824 if (pos < (arv->region()->position() + 64)) {
1825 fade_length = 64; // this should be a minimum defined somewhere
1826 } else if (pos > arv->region()->last_frame()) {
1827 fade_length = arv->region()->length();
1829 fade_length = pos - arv->region()->position();
1832 begin_reversible_command (_("change fade in length"));
1834 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1836 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1842 AutomationList& alist = tmp->audio_region()->fade_in();
1843 XMLNode &before = alist.get_state();
1845 tmp->audio_region()->set_fade_in_length (fade_length);
1847 XMLNode &after = alist.get_state();
1848 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1851 commit_reversible_command ();
1855 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1857 drag_info.item = item;
1858 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1859 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1863 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1864 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1868 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1870 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1874 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1876 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1878 nframes_t fade_length;
1880 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1881 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1887 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1891 if (pos > (arv->region()->last_frame() - 64)) {
1892 fade_length = 64; // this should really be a minimum fade defined somewhere
1894 else if (pos < arv->region()->position()) {
1895 fade_length = arv->region()->length();
1898 fade_length = arv->region()->last_frame() - pos;
1901 /* mapover the region selection */
1903 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1905 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1911 tmp->reset_fade_out_shape_width (fade_length);
1914 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1916 drag_info.first_move = false;
1920 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1922 if (drag_info.first_move) return;
1924 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1926 nframes_t fade_length;
1928 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1929 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1935 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1939 if (pos > (arv->region()->last_frame() - 64)) {
1940 fade_length = 64; // this should really be a minimum fade defined somewhere
1942 else if (pos < arv->region()->position()) {
1943 fade_length = arv->region()->length();
1946 fade_length = arv->region()->last_frame() - pos;
1949 begin_reversible_command (_("change fade out length"));
1951 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1953 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1959 AutomationList& alist = tmp->audio_region()->fade_out();
1960 XMLNode &before = alist.get_state();
1962 tmp->audio_region()->set_fade_out_length (fade_length);
1964 XMLNode &after = alist.get_state();
1965 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1968 commit_reversible_command ();
1972 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1974 drag_info.item = item;
1975 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1976 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1980 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1981 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1985 Cursor* cursor = (Cursor *) drag_info.data;
1987 if (cursor == playhead_cursor) {
1988 _dragging_playhead = true;
1990 if (session && drag_info.was_rolling) {
1991 session->request_stop ();
1995 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1997 show_verbose_time_cursor (cursor->current_frame, 10);
2001 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2003 Cursor* cursor = (Cursor *) drag_info.data;
2004 nframes_t adjusted_frame;
2006 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2007 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2013 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2014 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
2015 snap_to (adjusted_frame);
2019 if (adjusted_frame == drag_info.last_pointer_frame) return;
2021 cursor->set_position (adjusted_frame);
2023 if (cursor == edit_cursor) {
2024 edit_cursor_clock.set (cursor->current_frame);
2026 UpdateAllTransportClocks (cursor->current_frame);
2029 show_verbose_time_cursor (cursor->current_frame, 10);
2031 drag_info.last_pointer_frame = adjusted_frame;
2032 drag_info.first_move = false;
2036 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2038 if (drag_info.first_move) return;
2040 cursor_drag_motion_callback (item, event);
2042 _dragging_playhead = false;
2044 if (item == &playhead_cursor->canvas_item) {
2046 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2048 } else if (item == &edit_cursor->canvas_item) {
2049 edit_cursor->set_position (edit_cursor->current_frame);
2050 edit_cursor_clock.set (edit_cursor->current_frame);
2055 Editor::update_marker_drag_item (Location *location)
2057 double x1 = frame_to_pixel (location->start());
2058 double x2 = frame_to_pixel (location->end());
2060 if (location->is_mark()) {
2061 marker_drag_line_points.front().set_x(x1);
2062 marker_drag_line_points.back().set_x(x1);
2063 marker_drag_line->property_points() = marker_drag_line_points;
2066 range_marker_drag_rect->property_x1() = x1;
2067 range_marker_drag_rect->property_x2() = x2;
2072 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2076 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2077 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2083 Location *location = find_location_from_marker (marker, is_start);
2085 drag_info.item = item;
2086 drag_info.data = marker;
2087 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2088 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2092 drag_info.copied_location = new Location (*location);
2093 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2095 update_marker_drag_item (location);
2097 if (location->is_mark()) {
2098 marker_drag_line->show();
2099 marker_drag_line->raise_to_top();
2102 range_marker_drag_rect->show();
2103 range_marker_drag_rect->raise_to_top();
2106 if (is_start) show_verbose_time_cursor (location->start(), 10);
2107 else show_verbose_time_cursor (location->end(), 10);
2111 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2114 Marker* marker = (Marker *) drag_info.data;
2115 Location *real_location;
2116 Location *copy_location;
2118 bool move_both = false;
2122 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2123 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2129 nframes_t next = newframe;
2131 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2132 snap_to (newframe, 0, true);
2135 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2139 /* call this to find out if its the start or end */
2141 real_location = find_location_from_marker (marker, is_start);
2143 /* use the copy that we're "dragging" around */
2145 copy_location = drag_info.copied_location;
2147 f_delta = copy_location->end() - copy_location->start();
2149 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2153 if (copy_location->is_mark()) {
2156 copy_location->set_start (newframe);
2160 if (is_start) { // start-of-range marker
2163 copy_location->set_start (newframe);
2164 copy_location->set_end (newframe + f_delta);
2165 } else if (newframe < copy_location->end()) {
2166 copy_location->set_start (newframe);
2168 snap_to (next, 1, true);
2169 copy_location->set_end (next);
2170 copy_location->set_start (newframe);
2173 } else { // end marker
2176 copy_location->set_end (newframe);
2177 copy_location->set_start (newframe - f_delta);
2178 } else if (newframe > copy_location->start()) {
2179 copy_location->set_end (newframe);
2181 } else if (newframe > 0) {
2182 snap_to (next, -1, true);
2183 copy_location->set_start (next);
2184 copy_location->set_end (newframe);
2189 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2190 drag_info.first_move = false;
2192 update_marker_drag_item (copy_location);
2194 LocationMarkers* lm = find_location_markers (real_location);
2195 lm->set_position (copy_location->start(), copy_location->end());
2197 show_verbose_time_cursor (newframe, 10);
2201 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2203 if (drag_info.first_move) {
2204 marker_drag_motion_callback (item, event);
2208 Marker* marker = (Marker *) drag_info.data;
2212 begin_reversible_command ( _("move marker") );
2213 XMLNode &before = session->locations()->get_state();
2215 Location * location = find_location_from_marker (marker, is_start);
2218 if (location->is_mark()) {
2219 location->set_start (drag_info.copied_location->start());
2221 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2225 XMLNode &after = session->locations()->get_state();
2226 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2227 commit_reversible_command ();
2229 marker_drag_line->hide();
2230 range_marker_drag_rect->hide();
2234 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2237 MeterMarker* meter_marker;
2239 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2240 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2244 meter_marker = dynamic_cast<MeterMarker*> (marker);
2246 MetricSection& section (meter_marker->meter());
2248 if (!section.movable()) {
2252 drag_info.item = item;
2253 drag_info.data = marker;
2254 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2255 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2259 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2261 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2265 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2268 MeterMarker* meter_marker;
2270 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2271 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2275 meter_marker = dynamic_cast<MeterMarker*> (marker);
2277 // create a dummy marker for visual representation of moving the copy.
2278 // The actual copying is not done before we reach the finish callback.
2280 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2281 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2282 *new MeterSection(meter_marker->meter()));
2284 drag_info.item = &new_marker->the_item();
2285 drag_info.copy = true;
2286 drag_info.data = new_marker;
2287 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2288 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2292 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2294 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2298 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2300 MeterMarker* marker = (MeterMarker *) drag_info.data;
2301 nframes_t adjusted_frame;
2303 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2304 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2310 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2311 snap_to (adjusted_frame);
2314 if (adjusted_frame == drag_info.last_pointer_frame) return;
2316 marker->set_position (adjusted_frame);
2319 drag_info.last_pointer_frame = adjusted_frame;
2320 drag_info.first_move = false;
2322 show_verbose_time_cursor (adjusted_frame, 10);
2326 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2328 if (drag_info.first_move) return;
2330 meter_marker_drag_motion_callback (drag_info.item, event);
2332 MeterMarker* marker = (MeterMarker *) drag_info.data;
2335 TempoMap& map (session->tempo_map());
2336 map.bbt_time (drag_info.last_pointer_frame, when);
2338 if (drag_info.copy == true) {
2339 begin_reversible_command (_("copy meter mark"));
2340 XMLNode &before = map.get_state();
2341 map.add_meter (marker->meter(), when);
2342 XMLNode &after = map.get_state();
2343 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2344 commit_reversible_command ();
2346 // delete the dummy marker we used for visual representation of copying.
2347 // a new visual marker will show up automatically.
2350 begin_reversible_command (_("move meter mark"));
2351 XMLNode &before = map.get_state();
2352 map.move_meter (marker->meter(), when);
2353 XMLNode &after = map.get_state();
2354 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2355 commit_reversible_command ();
2360 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2363 TempoMarker* tempo_marker;
2365 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2366 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2370 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2371 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2375 MetricSection& section (tempo_marker->tempo());
2377 if (!section.movable()) {
2381 drag_info.item = item;
2382 drag_info.data = marker;
2383 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2384 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2388 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2389 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2393 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2396 TempoMarker* tempo_marker;
2398 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2399 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2403 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2404 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2408 // create a dummy marker for visual representation of moving the copy.
2409 // The actual copying is not done before we reach the finish callback.
2411 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2412 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2413 *new TempoSection(tempo_marker->tempo()));
2415 drag_info.item = &new_marker->the_item();
2416 drag_info.copy = true;
2417 drag_info.data = new_marker;
2418 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2419 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2423 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2425 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2429 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2431 TempoMarker* marker = (TempoMarker *) drag_info.data;
2432 nframes_t adjusted_frame;
2434 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2435 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2441 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2442 snap_to (adjusted_frame);
2445 if (adjusted_frame == drag_info.last_pointer_frame) return;
2447 /* OK, we've moved far enough to make it worth actually move the thing. */
2449 marker->set_position (adjusted_frame);
2451 show_verbose_time_cursor (adjusted_frame, 10);
2453 drag_info.last_pointer_frame = adjusted_frame;
2454 drag_info.first_move = false;
2458 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2460 if (drag_info.first_move) return;
2462 tempo_marker_drag_motion_callback (drag_info.item, event);
2464 TempoMarker* marker = (TempoMarker *) drag_info.data;
2467 TempoMap& map (session->tempo_map());
2468 map.bbt_time (drag_info.last_pointer_frame, when);
2470 if (drag_info.copy == true) {
2471 begin_reversible_command (_("copy tempo mark"));
2472 XMLNode &before = map.get_state();
2473 map.add_tempo (marker->tempo(), when);
2474 XMLNode &after = map.get_state();
2475 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2476 commit_reversible_command ();
2478 // delete the dummy marker we used for visual representation of copying.
2479 // a new visual marker will show up automatically.
2482 begin_reversible_command (_("move tempo mark"));
2483 XMLNode &before = map.get_state();
2484 map.move_tempo (marker->tempo(), when);
2485 XMLNode &after = map.get_state();
2486 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2487 commit_reversible_command ();
2492 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2494 ControlPoint* control_point;
2496 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2497 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2501 // We shouldn't remove the first or last gain point
2502 if (control_point->line.is_last_point(*control_point) ||
2503 control_point->line.is_first_point(*control_point)) {
2507 control_point->line.remove_point (*control_point);
2511 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2513 ControlPoint* control_point;
2515 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2516 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2520 control_point->line.remove_point (*control_point);
2524 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2526 ControlPoint* control_point;
2528 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2529 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2533 drag_info.item = item;
2534 drag_info.data = control_point;
2535 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2536 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2538 start_grab (event, fader_cursor);
2540 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2542 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2543 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2544 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2546 show_verbose_canvas_cursor ();
2550 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2552 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2554 double cx = drag_info.current_pointer_x;
2555 double cy = drag_info.current_pointer_y;
2557 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2558 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2560 if (drag_info.x_constrained) {
2561 cx = drag_info.grab_x;
2563 if (drag_info.y_constrained) {
2564 cy = drag_info.grab_y;
2567 cp->line.parent_group().w2i (cx, cy);
2571 cy = min ((double) cp->line.height(), cy);
2573 //translate cx to frames
2574 nframes_t cx_frames = unit_to_frame (cx);
2576 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2577 snap_to (cx_frames);
2580 float fraction = 1.0 - (cy / cp->line.height());
2584 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2590 cp->line.point_drag (*cp, cx_frames , fraction, push);
2592 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2594 drag_info.first_move = false;
2598 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2600 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2602 if (drag_info.first_move) {
2606 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2607 reset_point_selection ();
2611 control_point_drag_motion_callback (item, event);
2613 cp->line.end_drag (cp);
2617 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2619 switch (mouse_mode) {
2621 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2622 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2630 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2634 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2635 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2639 start_line_grab (al, event);
2643 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2647 nframes_t frame_within_region;
2649 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2653 cx = event->button.x;
2654 cy = event->button.y;
2655 line->parent_group().w2i (cx, cy);
2656 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2658 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2659 current_line_drag_info.after)) {
2660 /* no adjacent points */
2664 drag_info.item = &line->grab_item();
2665 drag_info.data = line;
2666 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2667 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2669 start_grab (event, fader_cursor);
2671 double fraction = 1.0 - (cy / line->height());
2673 line->start_drag (0, drag_info.grab_frame, fraction);
2675 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2676 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2677 show_verbose_canvas_cursor ();
2681 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2683 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2684 double cx = drag_info.current_pointer_x;
2685 double cy = drag_info.current_pointer_y;
2687 line->parent_group().w2i (cx, cy);
2690 fraction = 1.0 - (cy / line->height());
2694 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2700 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2702 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2706 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2708 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2709 line_drag_motion_callback (item, event);
2714 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2716 if (selection->regions.empty() || clicked_regionview == 0) {
2720 drag_info.copy = false;
2721 drag_info.item = item;
2722 drag_info.data = clicked_regionview;
2723 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2724 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2729 TimeAxisView* tvp = clicked_trackview;
2730 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2732 if (tv && tv->is_audio_track()) {
2733 speed = tv->get_diskstream()->speed();
2736 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2737 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2738 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2739 // we want a move threshold
2740 drag_info.want_move_threshold = true;
2742 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2744 begin_reversible_command (_("move region(s)"));
2748 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2750 if (selection->regions.empty() || clicked_regionview == 0) {
2754 drag_info.copy = true;
2755 drag_info.item = item;
2756 drag_info.data = clicked_regionview;
2760 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2761 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2764 if (atv && atv->is_audio_track()) {
2765 speed = atv->get_diskstream()->speed();
2768 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2769 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2770 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2771 // we want a move threshold
2772 drag_info.want_move_threshold = true;
2773 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2774 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2775 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2779 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2781 if (selection->regions.empty() || clicked_regionview == 0) {
2785 drag_info.copy = false;
2786 drag_info.item = item;
2787 drag_info.data = clicked_regionview;
2788 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2789 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2794 TimeAxisView* tvp = clicked_trackview;
2795 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2797 if (tv && tv->is_audio_track()) {
2798 speed = tv->get_diskstream()->speed();
2801 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2802 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2803 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2804 // we want a move threshold
2805 drag_info.want_move_threshold = true;
2806 drag_info.brushing = true;
2808 begin_reversible_command (_("Drag region brush"));
2812 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2816 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2817 nframes_t pending_region_position = 0;
2818 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2819 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2820 bool clamp_y_axis = false;
2821 vector<int32_t> height_list(512) ;
2822 vector<int32_t>::iterator j;
2824 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2826 drag_info.want_move_threshold = false; // don't copy again
2828 /* this is committed in the grab finished callback. */
2830 begin_reversible_command (_("Drag region copy"));
2832 /* duplicate the region(s) */
2834 vector<RegionView*> new_regionviews;
2836 set<boost::shared_ptr<Playlist> > affected_playlists;
2837 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2839 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2844 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
2845 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2847 insert_result = affected_playlists.insert (to_playlist);
2848 if (insert_result.second) {
2849 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
2852 latest_regionview = 0;
2854 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2856 /* create a new region with the same name. */
2858 // FIXME: ew. need a (virtual) Region::duplicate() or something?
2860 boost::shared_ptr<Region> newregion;
2861 boost::shared_ptr<Region> ar;
2863 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
2864 newregion = RegionFactory::create (ar);
2866 assert(newregion != 0);
2868 /* if the original region was locked, we don't care */
2870 newregion->set_locked (false);
2872 to_playlist->add_region (newregion, (nframes_t) (rv->region()->position() * atv->get_diskstream()->speed()));
2876 if (latest_regionview) {
2877 new_regionviews.push_back (latest_regionview);
2883 if (new_regionviews.empty()) {
2887 /* reset selection to new regionviews */
2889 selection->set (new_regionviews);
2891 /* reset drag_info data to reflect the fact that we are dragging the copies */
2893 drag_info.data = new_regionviews.front();
2894 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2897 /* Which trackview is this ? */
2899 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2900 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2902 /* The region motion is only processed if the pointer is over
2906 if (!tv || !tv->is_audio_track()) {
2907 /* To make sure we hide the verbose canvas cursor when the mouse is
2908 not held over and audiotrack.
2910 hide_verbose_canvas_cursor ();
2914 original_pointer_order = drag_info.last_trackview->order;
2916 /************************************************************
2918 ************************************************************/
2920 if (drag_info.brushing) {
2921 clamp_y_axis = true;
2926 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2928 int32_t children = 0, numtracks = 0;
2929 // XXX hard coding track limit, oh my, so very very bad
2930 bitset <1024> tracks (0x00);
2931 /* get a bitmask representing the visible tracks */
2933 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2934 TimeAxisView *tracklist_timeview;
2935 tracklist_timeview = (*i);
2936 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2937 list<TimeAxisView*> children_list;
2939 /* zeroes are audio tracks. ones are other types. */
2941 if (!atv2->hidden()) {
2943 if (visible_y_high < atv2->order) {
2944 visible_y_high = atv2->order;
2946 if (visible_y_low > atv2->order) {
2947 visible_y_low = atv2->order;
2950 if (!atv2->is_audio_track()) {
2951 tracks = tracks |= (0x01 << atv2->order);
2954 height_list[atv2->order] = (*i)->height;
2956 if ((children_list = atv2->get_child_list()).size() > 0) {
2957 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2958 tracks = tracks |= (0x01 << (atv2->order + children));
2959 height_list[atv2->order + children] = (*j)->height;
2967 /* find the actual span according to the canvas */
2969 canvas_pointer_y_span = pointer_y_span;
2970 if (drag_info.last_trackview->order >= tv->order) {
2972 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2973 if (height_list[y] == 0 ) {
2974 canvas_pointer_y_span--;
2979 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2980 if ( height_list[y] == 0 ) {
2981 canvas_pointer_y_span++;
2986 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2987 RegionView* rv2 = (*i);
2988 double ix1, ix2, iy1, iy2;
2991 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2992 rv2->get_canvas_group()->i2w (ix1, iy1);
2993 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2994 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2996 if (atv2->order != original_pointer_order) {
2997 /* this isn't the pointer track */
2999 if (canvas_pointer_y_span > 0) {
3001 /* moving up the canvas */
3002 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3004 int32_t visible_tracks = 0;
3005 while (visible_tracks < canvas_pointer_y_span ) {
3008 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3009 /* we're passing through a hidden track */
3014 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3015 clamp_y_axis = true;
3019 clamp_y_axis = true;
3022 } else if (canvas_pointer_y_span < 0) {
3024 /*moving down the canvas*/
3026 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3029 int32_t visible_tracks = 0;
3031 while (visible_tracks > canvas_pointer_y_span ) {
3034 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3038 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3039 clamp_y_axis = true;
3044 clamp_y_axis = true;
3050 /* this is the pointer's track */
3051 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3052 clamp_y_axis = true;
3053 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3054 clamp_y_axis = true;
3062 } else if (drag_info.last_trackview == tv) {
3063 clamp_y_axis = true;
3067 if (!clamp_y_axis) {
3068 drag_info.last_trackview = tv;
3071 /************************************************************
3073 ************************************************************/
3075 /* compute the amount of pointer motion in frames, and where
3076 the region would be if we moved it by that much.
3079 if (drag_info.move_threshold_passed) {
3081 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3083 nframes_t sync_frame;
3084 nframes_t sync_offset;
3087 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3089 sync_offset = rv->region()->sync_offset (sync_dir);
3090 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3092 /* we snap if the snap modifier is not enabled.
3095 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3096 snap_to (sync_frame);
3099 if (sync_frame - sync_offset <= sync_frame) {
3100 pending_region_position = sync_frame - (sync_dir*sync_offset);
3102 pending_region_position = 0;
3106 pending_region_position = 0;
3109 if (pending_region_position > max_frames - rv->region()->length()) {
3110 pending_region_position = drag_info.last_frame_position;
3113 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3115 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3117 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3118 to make it appear at the new location.
3121 if (pending_region_position > drag_info.last_frame_position) {
3122 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3124 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3127 drag_info.last_frame_position = pending_region_position;
3134 /* threshold not passed */
3139 /*************************************************************
3141 ************************************************************/
3143 if (x_delta == 0 && (pointer_y_span == 0)) {
3144 /* haven't reached next snap point, and we're not switching
3145 trackviews. nothing to do.
3151 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3153 RegionView* rv2 = (*i);
3155 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3157 double ix1, ix2, iy1, iy2;
3158 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3159 rv2->get_canvas_group()->i2w (ix1, iy1);
3168 /*************************************************************
3170 ************************************************************/
3172 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3173 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3175 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3177 RegionView* rv = (*i);
3178 double ix1, ix2, iy1, iy2;
3179 int32_t temp_pointer_y_span = pointer_y_span;
3181 /* get item BBox, which will be relative to parent. so we have
3182 to query on a child, then convert to world coordinates using
3186 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3187 rv->get_canvas_group()->i2w (ix1, iy1);
3188 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3189 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3190 AudioTimeAxisView* temp_atv;
3192 if ((pointer_y_span != 0) && !clamp_y_axis) {
3195 for (j = height_list.begin(); j!= height_list.end(); j++) {
3196 if (x == canvas_atv->order) {
3197 /* we found the track the region is on */
3198 if (x != original_pointer_order) {
3199 /*this isn't from the same track we're dragging from */
3200 temp_pointer_y_span = canvas_pointer_y_span;
3202 while (temp_pointer_y_span > 0) {
3203 /* we're moving up canvas-wise,
3204 so we need to find the next track height
3206 if (j != height_list.begin()) {
3209 if (x != original_pointer_order) {
3210 /* we're not from the dragged track, so ignore hidden tracks. */
3212 temp_pointer_y_span++;
3216 temp_pointer_y_span--;
3218 while (temp_pointer_y_span < 0) {
3220 if (x != original_pointer_order) {
3222 temp_pointer_y_span--;
3226 if (j != height_list.end()) {
3229 temp_pointer_y_span++;
3231 /* find out where we'll be when we move and set height accordingly */
3233 tvp2 = trackview_by_y_position (iy1 + y_delta);
3234 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3235 rv->set_height (temp_atv->height);
3237 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3238 personally, i think this can confuse things, but never mind.
3241 //const GdkColor& col (temp_atv->view->get_region_color());
3242 //rv->set_color (const_cast<GdkColor&>(col));
3249 /* prevent the regionview from being moved to before
3250 the zero position on the canvas.
3255 if (-x_delta > ix1) {
3258 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3259 x_delta = max_frames - rv->region()->last_frame();
3262 if (drag_info.first_move) {
3264 /* hide any dependent views */
3266 rv->get_time_axis_view().hide_dependent_views (*rv);
3268 /* this is subtle. raising the regionview itself won't help,
3269 because raise_to_top() just puts the item on the top of
3270 its parent's stack. so, we need to put the trackview canvas_display group
3271 on the top, since its parent is the whole canvas.
3274 rv->get_canvas_group()->raise_to_top();
3275 rv->get_time_axis_view().canvas_display->raise_to_top();
3276 cursor_group->raise_to_top();
3278 /* freeze the playlists from notifying till
3282 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3283 if (atv && atv->is_audio_track()) {
3284 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>(atv->get_diskstream()->playlist());
3286 /* only freeze and capture state once */
3288 insert_result = motion_frozen_playlists.insert (pl);
3289 if (insert_result.second) {
3291 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
3295 rv->region()->set_opaque(false);
3298 if (drag_info.brushing) {
3299 mouse_brush_insert_region (rv, pending_region_position);
3301 rv->move (x_delta, y_delta);
3305 if (drag_info.first_move) {
3306 cursor_group->raise_to_top();
3309 drag_info.first_move = false;
3311 if (x_delta != 0 && !drag_info.brushing) {
3312 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3318 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3321 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3322 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3323 bool nocommit = true;
3325 RouteTimeAxisView* atv;
3326 bool regionview_y_movement;
3327 bool regionview_x_movement;
3329 /* first_move is set to false if the regionview has been moved in the
3333 if (drag_info.first_move) {
3340 /* The regionview has been moved at some stage during the grab so we need
3341 to account for any mouse movement between this event and the last one.
3344 region_drag_motion_callback (item, event);
3346 if (drag_info.brushing) {
3347 /* all changes were made during motion event handlers */
3351 /* adjust for track speed */
3354 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3355 if (atv && atv->get_diskstream()) {
3356 speed = atv->get_diskstream()->speed();
3359 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3360 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3362 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3363 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3365 if (regionview_y_movement) {
3367 /* motion between tracks */
3369 list<RegionView*> new_selection;
3371 /* moved to a different audio track. */
3373 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3375 RegionView* rv2 = (*i);
3377 /* the region that used to be in the old playlist is not
3378 moved to the new one - we make a copy of it. as a result,
3379 any existing editor for the region should no longer be
3383 if (!drag_info.copy) {
3384 rv2->hide_region_editor();
3386 new_selection.push_back (rv2);
3390 /* first, freeze the target tracks */
3392 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3394 boost::shared_ptr<Playlist> from_playlist;
3395 boost::shared_ptr<Playlist> to_playlist;
3397 double ix1, ix2, iy1, iy2;
3399 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3400 (*i)->get_canvas_group()->i2w (ix1, iy1);
3401 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3402 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3404 (*i)->region()->set_opaque (true);
3406 from_playlist = (*i)->region()->playlist();
3407 to_playlist = atv2->playlist();
3409 /* the from_playlist was frozen in the "first_move" case
3410 of the motion handler. the insert can fail,
3411 but that doesn't matter. it just means
3412 we already have the playlist in the list.
3415 motion_frozen_playlists.insert (from_playlist);
3417 /* only freeze the to_playlist once */
3419 insert_result = motion_frozen_playlists.insert(to_playlist);
3420 if (insert_result.second) {
3421 to_playlist->freeze();
3422 session->add_command(new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3427 /* now do it again with the actual operations */
3429 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3431 boost::shared_ptr<Playlist> from_playlist;
3432 boost::shared_ptr<Playlist> to_playlist;
3434 double ix1, ix2, iy1, iy2;
3436 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3437 (*i)->get_canvas_group()->i2w (ix1, iy1);
3438 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3439 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3441 from_playlist = (*i)->region()->playlist();
3442 to_playlist = atv2->playlist();
3444 latest_regionview = 0;
3446 where = (nframes_t) (unit_to_frame (ix1) * speed);
3447 boost::shared_ptr<Region> new_region (RegionFactory::create ((*i)->region()));
3449 from_playlist->remove_region (((*i)->region()));
3451 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3452 to_playlist->add_region (new_region, where);
3455 if (latest_regionview) {
3456 selection->add (latest_regionview);
3462 /* motion within a single track */
3464 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3468 if (rv->region()->locked()) {
3472 if (regionview_x_movement) {
3473 double ownspeed = 1.0;
3474 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3476 if (atv && atv->get_diskstream()) {
3477 ownspeed = atv->get_diskstream()->speed();
3480 /* base the new region position on the current position of the regionview.*/
3482 double ix1, ix2, iy1, iy2;
3484 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3485 rv->get_canvas_group()->i2w (ix1, iy1);
3486 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3490 where = rv->region()->position();
3493 rv->get_time_axis_view().reveal_dependent_views (*rv);
3495 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3497 rv->region()->set_position (where, (void *) this);
3498 rv->region()->set_opaque (true);
3503 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3505 session->add_command (new MementoCommand<Playlist>(*((*p).get()), 0, & (*p)->get_state()));
3508 motion_frozen_playlists.clear ();
3511 commit_reversible_command ();
3516 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3518 /* Either add to or set the set the region selection, unless
3519 this is an alignment click (control used)
3522 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3523 TimeAxisView* tv = &rv.get_time_axis_view();
3524 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3526 if (atv && atv->is_audio_track()) {
3527 speed = atv->get_diskstream()->speed();
3530 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3532 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3534 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3536 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3540 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3546 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3552 nframes_t frame_rate;
3559 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3560 case AudioClock::BBT:
3561 session->bbt_time (frame, bbt);
3562 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3565 case AudioClock::SMPTE:
3566 session->smpte_time (frame, smpte);
3567 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3570 case AudioClock::MinSec:
3571 /* XXX this is copied from show_verbose_duration_cursor() */
3572 frame_rate = session->frame_rate();
3573 hours = frame / (frame_rate * 3600);
3574 frame = frame % (frame_rate * 3600);
3575 mins = frame / (frame_rate * 60);
3576 frame = frame % (frame_rate * 60);
3577 secs = (float) frame / (float) frame_rate;
3578 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3582 snprintf (buf, sizeof(buf), "%u", frame);
3586 if (xpos >= 0 && ypos >=0) {
3587 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3590 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3592 show_verbose_canvas_cursor ();
3596 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3603 nframes_t distance, frame_rate;
3605 Meter meter_at_start(session->tempo_map().meter_at(start));
3611 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3612 case AudioClock::BBT:
3613 session->bbt_time (start, sbbt);
3614 session->bbt_time (end, ebbt);
3617 /* XXX this computation won't work well if the
3618 user makes a selection that spans any meter changes.
3621 ebbt.bars -= sbbt.bars;
3622 if (ebbt.beats >= sbbt.beats) {
3623 ebbt.beats -= sbbt.beats;
3626 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3628 if (ebbt.ticks >= sbbt.ticks) {
3629 ebbt.ticks -= sbbt.ticks;
3632 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3635 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3638 case AudioClock::SMPTE:
3639 session->smpte_duration (end - start, smpte);
3640 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3643 case AudioClock::MinSec:
3644 /* XXX this stuff should be elsewhere.. */
3645 distance = end - start;
3646 frame_rate = session->frame_rate();
3647 hours = distance / (frame_rate * 3600);
3648 distance = distance % (frame_rate * 3600);
3649 mins = distance / (frame_rate * 60);
3650 distance = distance % (frame_rate * 60);
3651 secs = (float) distance / (float) frame_rate;
3652 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3656 snprintf (buf, sizeof(buf), "%u", end - start);
3660 if (xpos >= 0 && ypos >=0) {
3661 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3664 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3666 show_verbose_canvas_cursor ();
3670 Editor::collect_new_region_view (RegionView* rv)
3672 latest_regionview = rv;
3676 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3678 if (clicked_regionview == 0) {
3682 /* lets try to create new Region for the selection */
3684 vector<boost::shared_ptr<AudioRegion> > new_regions;
3685 create_region_from_selection (new_regions);
3687 if (new_regions.empty()) {
3691 /* XXX fix me one day to use all new regions */
3693 boost::shared_ptr<Region> region (new_regions.front());
3695 /* add it to the current stream/playlist.
3697 tricky: the streamview for the track will add a new regionview. we will
3698 catch the signal it sends when it creates the regionview to
3699 set the regionview we want to then drag.
3702 latest_regionview = 0;
3703 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3705 /* A selection grab currently creates two undo/redo operations, one for
3706 creating the new region and another for moving it.
3709 begin_reversible_command (_("selection grab"));
3711 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3713 XMLNode *before = &(playlist->get_state());
3714 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3715 XMLNode *after = &(playlist->get_state());
3716 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3718 commit_reversible_command ();
3722 if (latest_regionview == 0) {
3723 /* something went wrong */
3727 /* we need to deselect all other regionviews, and select this one
3728 i'm ignoring undo stuff, because the region creation will take care of it */
3729 selection->set (latest_regionview);
3731 drag_info.item = latest_regionview->get_canvas_group();
3732 drag_info.data = latest_regionview;
3733 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3734 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3738 drag_info.last_trackview = clicked_trackview;
3739 drag_info.last_frame_position = latest_regionview->region()->position();
3740 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3742 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3746 Editor::cancel_selection ()
3748 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3749 (*i)->hide_selection ();
3751 begin_reversible_command (_("cancel selection"));
3752 selection->clear ();
3753 clicked_selection = 0;
3754 commit_reversible_command ();
3758 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3760 nframes_t start = 0;
3767 drag_info.item = item;
3768 drag_info.motion_callback = &Editor::drag_selection;
3769 drag_info.finished_callback = &Editor::end_selection_op;
3774 case CreateSelection:
3775 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3776 drag_info.copy = true;
3778 drag_info.copy = false;
3780 start_grab (event, selector_cursor);
3783 case SelectionStartTrim:
3784 if (clicked_trackview) {
3785 clicked_trackview->order_selection_trims (item, true);
3787 start_grab (event, trimmer_cursor);
3788 start = selection->time[clicked_selection].start;
3789 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3792 case SelectionEndTrim:
3793 if (clicked_trackview) {
3794 clicked_trackview->order_selection_trims (item, false);
3796 start_grab (event, trimmer_cursor);
3797 end = selection->time[clicked_selection].end;
3798 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3802 start = selection->time[clicked_selection].start;
3804 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3808 if (selection_op == SelectionMove) {
3809 show_verbose_time_cursor(start, 10);
3811 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3816 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3818 nframes_t start = 0;
3821 nframes_t pending_position;
3823 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3824 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3827 pending_position = 0;
3830 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3831 snap_to (pending_position);
3834 /* only alter selection if the current frame is
3835 different from the last frame position (adjusted)
3838 if (pending_position == drag_info.last_pointer_frame) return;
3840 switch (selection_op) {
3841 case CreateSelection:
3843 if (drag_info.first_move) {
3844 snap_to (drag_info.grab_frame);
3847 if (pending_position < drag_info.grab_frame) {
3848 start = pending_position;
3849 end = drag_info.grab_frame;
3851 end = pending_position;
3852 start = drag_info.grab_frame;
3855 /* first drag: Either add to the selection
3856 or create a new selection->
3859 if (drag_info.first_move) {
3861 begin_reversible_command (_("range selection"));
3863 if (drag_info.copy) {
3864 /* adding to the selection */
3865 clicked_selection = selection->add (start, end);
3866 drag_info.copy = false;
3868 /* new selection-> */
3869 clicked_selection = selection->set (clicked_trackview, start, end);
3874 case SelectionStartTrim:
3876 if (drag_info.first_move) {
3877 begin_reversible_command (_("trim selection start"));
3880 start = selection->time[clicked_selection].start;
3881 end = selection->time[clicked_selection].end;
3883 if (pending_position > end) {
3886 start = pending_position;
3890 case SelectionEndTrim:
3892 if (drag_info.first_move) {
3893 begin_reversible_command (_("trim selection end"));
3896 start = selection->time[clicked_selection].start;
3897 end = selection->time[clicked_selection].end;
3899 if (pending_position < start) {
3902 end = pending_position;
3909 if (drag_info.first_move) {
3910 begin_reversible_command (_("move selection"));
3913 start = selection->time[clicked_selection].start;
3914 end = selection->time[clicked_selection].end;
3916 length = end - start;
3918 start = pending_position;
3921 end = start + length;
3926 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3927 start_canvas_autoscroll (1);
3931 selection->replace (clicked_selection, start, end);
3934 drag_info.last_pointer_frame = pending_position;
3935 drag_info.first_move = false;
3937 if (selection_op == SelectionMove) {
3938 show_verbose_time_cursor(start, 10);
3940 show_verbose_time_cursor(pending_position, 10);
3945 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3947 if (!drag_info.first_move) {
3948 drag_selection (item, event);
3949 /* XXX this is not object-oriented programming at all. ick */
3950 if (selection->time.consolidate()) {
3951 selection->TimeChanged ();
3953 commit_reversible_command ();
3955 /* just a click, no pointer movement.*/
3957 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3959 selection->clear_time();
3964 /* XXX what happens if its a music selection? */
3965 session->set_audio_range (selection->time);
3966 stop_canvas_autoscroll ();
3970 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3973 TimeAxisView* tvp = clicked_trackview;
3974 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3976 if (tv && tv->is_audio_track()) {
3977 speed = tv->get_diskstream()->speed();
3980 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3981 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3982 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3984 motion_frozen_playlists.clear();
3986 //drag_info.item = clicked_regionview->get_name_highlight();
3987 drag_info.item = item;
3988 drag_info.motion_callback = &Editor::trim_motion_callback;
3989 drag_info.finished_callback = &Editor::trim_finished_callback;
3991 start_grab (event, trimmer_cursor);
3993 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3994 trim_op = ContentsTrim;
3996 /* These will get overridden for a point trim.*/
3997 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3998 /* closer to start */
3999 trim_op = StartTrim;
4000 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4008 show_verbose_time_cursor(region_start, 10);
4011 show_verbose_time_cursor(region_end, 10);
4014 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4020 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4022 RegionView* rv = clicked_regionview;
4023 nframes_t frame_delta = 0;
4024 bool left_direction;
4025 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4027 /* snap modifier works differently here..
4028 its' current state has to be passed to the
4029 various trim functions in order to work properly
4033 TimeAxisView* tvp = clicked_trackview;
4034 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4035 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4037 if (tv && tv->is_audio_track()) {
4038 speed = tv->get_diskstream()->speed();
4041 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4042 left_direction = true;
4044 left_direction = false;
4048 snap_to (drag_info.current_pointer_frame);
4051 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4055 if (drag_info.first_move) {
4061 trim_type = "Region start trim";
4064 trim_type = "Region end trim";
4067 trim_type = "Region content trim";
4071 begin_reversible_command (trim_type);
4073 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4074 (*i)->region()->set_opaque(false);
4075 (*i)->region()->freeze ();
4077 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4079 arv->temporarily_hide_envelope ();
4081 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4082 insert_result = motion_frozen_playlists.insert (pl);
4083 if (insert_result.second) {
4084 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4089 if (left_direction) {
4090 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4092 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4097 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4100 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4101 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4107 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4110 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4111 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4118 bool swap_direction = false;
4120 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4121 swap_direction = true;
4124 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4125 i != selection->regions.by_layer().end(); ++i)
4127 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4135 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4138 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4141 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4145 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4146 drag_info.first_move = false;
4150 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4152 boost::shared_ptr<Region> region (rv.region());
4154 if (region->locked()) {
4158 nframes_t new_bound;
4161 TimeAxisView* tvp = clicked_trackview;
4162 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4164 if (tv && tv->is_audio_track()) {
4165 speed = tv->get_diskstream()->speed();
4168 if (left_direction) {
4169 if (swap_direction) {
4170 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4172 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4175 if (swap_direction) {
4176 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4178 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4183 snap_to (new_bound);
4185 region->trim_start ((nframes_t) (new_bound * speed), this);
4186 rv.region_changed (StartChanged);
4190 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4192 boost::shared_ptr<Region> region (rv.region());
4194 if (region->locked()) {
4198 nframes_t new_bound;
4201 TimeAxisView* tvp = clicked_trackview;
4202 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4204 if (tv && tv->is_audio_track()) {
4205 speed = tv->get_diskstream()->speed();
4208 if (left_direction) {
4209 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4211 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4215 snap_to (new_bound, (left_direction ? 0 : 1));
4218 region->trim_front ((nframes_t) (new_bound * speed), this);
4220 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4224 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4226 boost::shared_ptr<Region> region (rv.region());
4228 if (region->locked()) {
4232 nframes_t new_bound;
4235 TimeAxisView* tvp = clicked_trackview;
4236 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4238 if (tv && tv->is_audio_track()) {
4239 speed = tv->get_diskstream()->speed();
4242 if (left_direction) {
4243 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4245 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4249 snap_to (new_bound);
4251 region->trim_end ((nframes_t) (new_bound * speed), this);
4252 rv.region_changed (LengthChanged);
4256 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4258 if (!drag_info.first_move) {
4259 trim_motion_callback (item, event);
4261 if (!clicked_regionview->get_selected()) {
4262 thaw_region_after_trim (*clicked_regionview);
4265 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4266 i != selection->regions.by_layer().end(); ++i)
4268 thaw_region_after_trim (**i);
4269 (*i)->region()->set_opaque(true);
4273 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4275 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4278 motion_frozen_playlists.clear ();
4280 commit_reversible_command();
4282 /* no mouse movement */
4288 Editor::point_trim (GdkEvent* event)
4290 RegionView* rv = clicked_regionview;
4291 nframes_t new_bound = drag_info.current_pointer_frame;
4293 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4294 snap_to (new_bound);
4297 /* Choose action dependant on which button was pressed */
4298 switch (event->button.button) {
4300 trim_op = StartTrim;
4301 begin_reversible_command (_("Start point trim"));
4303 if (rv->get_selected()) {
4305 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4306 i != selection->regions.by_layer().end(); ++i)
4308 if (!(*i)->region()->locked()) {
4309 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4310 XMLNode &before = pl->get_state();
4311 (*i)->region()->trim_front (new_bound, this);
4312 XMLNode &after = pl->get_state();
4313 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4319 if (!rv->region()->locked()) {
4320 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4321 XMLNode &before = pl->get_state();
4322 rv->region()->trim_front (new_bound, this);
4323 XMLNode &after = pl->get_state();
4324 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4328 commit_reversible_command();
4333 begin_reversible_command (_("End point trim"));
4335 if (rv->get_selected()) {
4337 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4339 if (!(*i)->region()->locked()) {
4340 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4341 XMLNode &before = pl->get_state();
4342 (*i)->region()->trim_end (new_bound, this);
4343 XMLNode &after = pl->get_state();
4344 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4350 if (!rv->region()->locked()) {
4351 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4352 XMLNode &before = pl->get_state();
4353 rv->region()->trim_end (new_bound, this);
4354 XMLNode &after = pl->get_state();
4355 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4359 commit_reversible_command();
4368 Editor::thaw_region_after_trim (RegionView& rv)
4370 boost::shared_ptr<Region> region (rv.region());
4372 if (region->locked()) {
4376 region->thaw (_("trimmed region"));
4377 XMLNode &after = region->playlist()->get_state();
4378 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4380 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4382 arv->unhide_envelope ();
4386 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4391 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4392 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4396 Location* location = find_location_from_marker (marker, is_start);
4397 location->set_hidden (true, this);
4402 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4408 drag_info.item = item;
4409 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4410 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4412 range_marker_op = op;
4414 if (!temp_location) {
4415 temp_location = new Location;
4419 case CreateRangeMarker:
4420 case CreateTransportMarker:
4422 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4423 drag_info.copy = true;
4425 drag_info.copy = false;
4427 start_grab (event, selector_cursor);
4431 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4436 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4438 nframes_t start = 0;
4440 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4442 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4443 snap_to (drag_info.current_pointer_frame);
4446 /* only alter selection if the current frame is
4447 different from the last frame position.
4450 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4452 switch (range_marker_op) {
4453 case CreateRangeMarker:
4454 case CreateTransportMarker:
4455 if (drag_info.first_move) {
4456 snap_to (drag_info.grab_frame);
4459 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4460 start = drag_info.current_pointer_frame;
4461 end = drag_info.grab_frame;
4463 end = drag_info.current_pointer_frame;
4464 start = drag_info.grab_frame;
4467 /* first drag: Either add to the selection
4468 or create a new selection.
4471 if (drag_info.first_move) {
4473 temp_location->set (start, end);
4477 update_marker_drag_item (temp_location);
4478 range_marker_drag_rect->show();
4479 range_marker_drag_rect->raise_to_top();
4485 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4486 start_canvas_autoscroll (1);
4490 temp_location->set (start, end);
4492 double x1 = frame_to_pixel (start);
4493 double x2 = frame_to_pixel (end);
4494 crect->property_x1() = x1;
4495 crect->property_x2() = x2;
4497 update_marker_drag_item (temp_location);
4500 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4501 drag_info.first_move = false;
4503 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4508 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4510 Location * newloc = 0;
4513 if (!drag_info.first_move) {
4514 drag_range_markerbar_op (item, event);
4516 switch (range_marker_op) {
4517 case CreateRangeMarker:
4519 begin_reversible_command (_("new range marker"));
4520 XMLNode &before = session->locations()->get_state();
4521 session->locations()->next_available_name(rangename,"unnamed");
4522 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4523 session->locations()->add (newloc, true);
4524 XMLNode &after = session->locations()->get_state();
4525 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4526 commit_reversible_command ();
4528 range_bar_drag_rect->hide();
4529 range_marker_drag_rect->hide();
4533 case CreateTransportMarker:
4534 // popup menu to pick loop or punch
4535 new_transport_marker_context_menu (&event->button, item);
4540 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4542 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4547 start = session->locations()->first_mark_before (drag_info.grab_frame);
4548 end = session->locations()->first_mark_after (drag_info.grab_frame);
4550 if (end == max_frames) {
4551 end = session->current_end_frame ();
4555 start = session->current_start_frame ();
4558 switch (mouse_mode) {
4560 /* find the two markers on either side and then make the selection from it */
4561 cerr << "select between " << start << " .. " << end << endl;
4562 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4566 /* find the two markers on either side of the click and make the range out of it */
4567 selection->set (0, start, end);
4576 stop_canvas_autoscroll ();
4582 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4584 drag_info.item = item;
4585 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4586 drag_info.finished_callback = &Editor::end_mouse_zoom;
4588 start_grab (event, zoom_cursor);
4590 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4594 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4599 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4600 snap_to (drag_info.current_pointer_frame);
4602 if (drag_info.first_move) {
4603 snap_to (drag_info.grab_frame);
4607 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4609 /* base start and end on initial click position */
4610 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4611 start = drag_info.current_pointer_frame;
4612 end = drag_info.grab_frame;
4614 end = drag_info.current_pointer_frame;
4615 start = drag_info.grab_frame;
4620 if (drag_info.first_move) {
4622 zoom_rect->raise_to_top();
4625 reposition_zoom_rect(start, end);
4627 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4628 drag_info.first_move = false;
4630 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4635 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4637 if (!drag_info.first_move) {
4638 drag_mouse_zoom (item, event);
4640 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4641 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4643 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4646 temporal_zoom_to_frame (false, drag_info.grab_frame);
4648 temporal_zoom_step (false);
4649 center_screen (drag_info.grab_frame);
4657 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4659 double x1 = frame_to_pixel (start);
4660 double x2 = frame_to_pixel (end);
4661 double y2 = full_canvas_height - 1.0;
4663 zoom_rect->property_x1() = x1;
4664 zoom_rect->property_y1() = 1.0;
4665 zoom_rect->property_x2() = x2;
4666 zoom_rect->property_y2() = y2;
4670 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4672 drag_info.item = item;
4673 drag_info.motion_callback = &Editor::drag_rubberband_select;
4674 drag_info.finished_callback = &Editor::end_rubberband_select;
4676 start_grab (event, cross_hair_cursor);
4678 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4682 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4689 /* use a bigger drag threshold than the default */
4691 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4695 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4696 if (drag_info.first_move) {
4697 snap_to (drag_info.grab_frame);
4699 snap_to (drag_info.current_pointer_frame);
4702 /* base start and end on initial click position */
4704 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4705 start = drag_info.current_pointer_frame;
4706 end = drag_info.grab_frame;
4708 end = drag_info.current_pointer_frame;
4709 start = drag_info.grab_frame;
4712 if (drag_info.current_pointer_y < drag_info.grab_y) {
4713 y1 = drag_info.current_pointer_y;
4714 y2 = drag_info.grab_y;
4716 y2 = drag_info.current_pointer_y;
4717 y1 = drag_info.grab_y;
4721 if (start != end || y1 != y2) {
4723 double x1 = frame_to_pixel (start);
4724 double x2 = frame_to_pixel (end);
4726 rubberband_rect->property_x1() = x1;
4727 rubberband_rect->property_y1() = y1;
4728 rubberband_rect->property_x2() = x2;
4729 rubberband_rect->property_y2() = y2;
4731 rubberband_rect->show();
4732 rubberband_rect->raise_to_top();
4734 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4735 drag_info.first_move = false;
4737 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4742 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4744 if (!drag_info.first_move) {
4746 drag_rubberband_select (item, event);
4749 if (drag_info.current_pointer_y < drag_info.grab_y) {
4750 y1 = drag_info.current_pointer_y;
4751 y2 = drag_info.grab_y;
4754 y2 = drag_info.current_pointer_y;
4755 y1 = drag_info.grab_y;
4759 Selection::Operation op = Keyboard::selection_type (event->button.state);
4762 begin_reversible_command (_("rubberband selection"));
4764 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4765 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4767 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4771 commit_reversible_command ();
4775 selection->clear_tracks();
4776 selection->clear_regions();
4777 selection->clear_points ();
4778 selection->clear_lines ();
4781 rubberband_rect->hide();
4786 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4788 using namespace Gtkmm2ext;
4790 ArdourPrompter prompter (false);
4792 prompter.set_prompt (_("Name for region:"));
4793 prompter.set_initial_text (clicked_regionview->region()->name());
4794 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4795 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4796 prompter.show_all ();
4797 switch (prompter.run ()) {
4798 case Gtk::RESPONSE_ACCEPT:
4800 prompter.get_result(str);
4802 clicked_regionview->region()->set_name (str);
4810 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4812 drag_info.item = item;
4813 drag_info.motion_callback = &Editor::time_fx_motion;
4814 drag_info.finished_callback = &Editor::end_time_fx;
4818 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4822 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4824 RegionView* rv = clicked_regionview;
4826 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4827 snap_to (drag_info.current_pointer_frame);
4830 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4834 if (drag_info.current_pointer_frame > rv->region()->position()) {
4835 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4838 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4839 drag_info.first_move = false;
4841 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4845 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4847 clicked_regionview->get_time_axis_view().hide_timestretch ();
4849 if (drag_info.first_move) {
4853 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4854 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4856 begin_reversible_command (_("timestretch"));
4858 if (run_timestretch (selection->regions, percentage) == 0) {
4859 session->commit_reversible_command ();
4864 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4866 /* no brushing without a useful snap setting */
4869 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4872 switch (snap_mode) {
4874 return; /* can't work because it allows region to be placed anywhere */
4879 switch (snap_type) {
4882 case SnapToEditCursor:
4889 /* don't brush a copy over the original */
4891 if (pos == rv->region()->position()) {
4895 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4897 if (atv == 0 || !atv->is_audio_track()) {
4901 boost::shared_ptr<Playlist> playlist = atv->playlist();
4902 double speed = atv->get_diskstream()->speed();
4904 XMLNode &before = playlist->get_state();
4905 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4906 XMLNode &after = playlist->get_state();
4907 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4909 // playlist is frozen, so we have to update manually
4911 playlist->Modified(); /* EMIT SIGNAL */
4915 Editor::track_height_step_timeout ()
4918 struct timeval delta;
4920 gettimeofday (&now, 0);
4921 timersub (&now, &last_track_height_step_timestamp, &delta);
4923 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4924 current_stepping_trackview = 0;