2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <pbd/error.h>
30 #include <gtkmm2ext/utils.h>
31 #include <pbd/memento_command.h>
33 #include "ardour_ui.h"
35 #include "time_axis_view.h"
36 #include "audio_time_axis.h"
37 #include "audio_region_view.h"
39 #include "streamview.h"
40 #include "region_gain_line.h"
41 #include "automation_time_axis.h"
44 #include "selection.h"
47 #include "rgb_macros.h"
49 #include <ardour/types.h>
50 #include <ardour/route.h>
51 #include <ardour/audio_track.h>
52 #include <ardour/audio_diskstream.h>
53 #include <ardour/playlist.h>
54 #include <ardour/audioplaylist.h>
55 #include <ardour/audioregion.h>
56 #include <ardour/midi_region.h>
57 #include <ardour/dB.h>
58 #include <ardour/utils.h>
59 #include <ardour/region_factory.h>
66 using namespace ARDOUR;
70 using namespace Editing;
73 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
87 switch (event->type) {
88 case GDK_BUTTON_RELEASE:
89 case GDK_BUTTON_PRESS:
90 case GDK_2BUTTON_PRESS:
91 case GDK_3BUTTON_PRESS:
92 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
94 case GDK_MOTION_NOTIFY:
95 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
97 case GDK_ENTER_NOTIFY:
98 case GDK_LEAVE_NOTIFY:
99 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
102 case GDK_KEY_RELEASE:
103 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
106 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
110 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
111 position is negative (as can be the case with motion events in particular),
112 the frame location is always positive.
115 return pixel_to_frame (*pcx);
119 Editor::mouse_mode_toggled (MouseMode m)
121 if (ignore_mouse_mode_toggle) {
127 if (mouse_select_button.get_active()) {
133 if (mouse_move_button.get_active()) {
139 if (mouse_gain_button.get_active()) {
145 if (mouse_zoom_button.get_active()) {
151 if (mouse_timefx_button.get_active()) {
157 if (mouse_audition_button.get_active()) {
168 Editor::set_mouse_mode (MouseMode m, bool force)
170 if (drag_info.item) {
174 if (!force && m == mouse_mode) {
182 if (mouse_mode != MouseRange) {
184 /* in all modes except range, hide the range selection,
185 show the object (region) selection.
188 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
189 (*i)->set_should_show_selection (true);
191 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
192 (*i)->hide_selection ();
198 in range mode,show the range selection.
201 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
202 if ((*i)->get_selected()) {
203 (*i)->show_selection (selection->time);
208 /* XXX the hack of unsetting all other buttongs should go
209 away once GTK2 allows us to use regular radio buttons drawn like
210 normal buttons, rather than my silly GroupedButton hack.
213 ignore_mouse_mode_toggle = true;
215 switch (mouse_mode) {
217 mouse_select_button.set_active (true);
218 current_canvas_cursor = selector_cursor;
222 mouse_move_button.set_active (true);
223 current_canvas_cursor = grabber_cursor;
227 mouse_gain_button.set_active (true);
228 current_canvas_cursor = cross_hair_cursor;
232 mouse_zoom_button.set_active (true);
233 current_canvas_cursor = zoom_cursor;
237 mouse_timefx_button.set_active (true);
238 current_canvas_cursor = time_fx_cursor; // just use playhead
242 mouse_audition_button.set_active (true);
243 current_canvas_cursor = speaker_cursor;
247 ignore_mouse_mode_toggle = false;
250 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
255 Editor::step_mouse_mode (bool next)
257 switch (current_mouse_mode()) {
259 if (next) set_mouse_mode (MouseRange);
260 else set_mouse_mode (MouseTimeFX);
264 if (next) set_mouse_mode (MouseZoom);
265 else set_mouse_mode (MouseObject);
269 if (next) set_mouse_mode (MouseGain);
270 else set_mouse_mode (MouseRange);
274 if (next) set_mouse_mode (MouseTimeFX);
275 else set_mouse_mode (MouseZoom);
279 if (next) set_mouse_mode (MouseAudition);
280 else set_mouse_mode (MouseGain);
284 if (next) set_mouse_mode (MouseObject);
285 else set_mouse_mode (MouseTimeFX);
291 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
295 /* in object/audition/timefx mode, any button press sets
296 the selection if the object can be selected. this is a
297 bit of hack, because we want to avoid this if the
298 mouse operation is a region alignment.
300 note: not dbl-click or triple-click
303 if (((mouse_mode != MouseObject) &&
304 (mouse_mode != MouseAudition || item_type != RegionItem) &&
305 (mouse_mode != MouseTimeFX || item_type != RegionItem)) ||
306 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
311 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
313 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
315 /* no selection action on modified button-2 or button-3 events */
321 Selection::Operation op = Keyboard::selection_type (event->button.state);
322 bool press = (event->type == GDK_BUTTON_PRESS);
324 begin_reversible_command (_("select on click"));
328 commit = set_selected_regionview_from_click (press, op, true);
331 case RegionViewNameHighlight:
333 commit = set_selected_regionview_from_click (press, op, true);
336 case FadeInHandleItem:
338 case FadeOutHandleItem:
340 commit = set_selected_regionview_from_click (press, op, true);
343 case GainAutomationControlPointItem:
344 case PanAutomationControlPointItem:
345 case RedirectAutomationControlPointItem:
346 commit = set_selected_control_point_from_click (op, false);
350 // commit = set_selected_track_from_click (press, op, true);
353 case AutomationTrackItem:
354 commit = set_selected_track_from_click (press, op, true);
361 #define SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
362 #ifdef SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
363 /* in range mode, button 1/2/3 press potentially selects a track */
365 if (mouse_mode == MouseRange &&
366 event->type == GDK_BUTTON_PRESS &&
367 event->button.button <= 3) {
372 case AutomationTrackItem:
373 commit = set_selected_track_from_click (true, 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_axisview) {
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_axisview;
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_axisview) {
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_axisview;
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_axisview)->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_axisview)->
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_axisview;
2730 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2732 if (tv && tv->is_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* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2764 if (rtv && rtv->is_track()) {
2765 speed = rtv->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_axisview;
2795 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2797 if (tv && tv->is_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* rtv = 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 = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2856 /* create a new region with the same name. */
2858 boost::shared_ptr<Region> newregion;
2860 newregion = RegionFactory::create (rv->region());
2861 assert(newregion != 0);
2863 /* if the original region was locked, we don't care */
2865 newregion->set_locked (false);
2867 to_playlist->add_region (newregion, (nframes_t) (rv->region()->position() * rtv->get_diskstream()->speed()));
2871 if (latest_regionview) {
2872 new_regionviews.push_back (latest_regionview);
2878 if (new_regionviews.empty()) {
2882 /* reset selection to new regionviews */
2884 selection->set (new_regionviews);
2886 /* reset drag_info data to reflect the fact that we are dragging the copies */
2888 drag_info.data = new_regionviews.front();
2889 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2892 /* Which trackview is this ? */
2894 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2895 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2897 /* The region motion is only processed if the pointer is over
2901 if (!tv || !tv->is_track()) {
2902 /* To make sure we hide the verbose canvas cursor when the mouse is
2903 not held over a track.
2905 hide_verbose_canvas_cursor ();
2909 original_pointer_order = drag_info.last_trackview->order;
2911 /************************************************************
2913 ************************************************************/
2915 if (drag_info.brushing) {
2916 clamp_y_axis = true;
2921 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2923 int32_t children = 0, numtracks = 0;
2924 // XXX hard coding track limit, oh my, so very very bad
2925 bitset <1024> tracks (0x00);
2926 /* get a bitmask representing the visible tracks */
2928 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2929 TimeAxisView *tracklist_timeview;
2930 tracklist_timeview = (*i);
2931 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2932 list<TimeAxisView*> children_list;
2934 /* zeroes are audio tracks. ones are other types. */
2936 if (!rtv2->hidden()) {
2938 if (visible_y_high < rtv2->order) {
2939 visible_y_high = rtv2->order;
2941 if (visible_y_low > rtv2->order) {
2942 visible_y_low = rtv2->order;
2945 if (!rtv2->is_track()) {
2946 tracks = tracks |= (0x01 << rtv2->order);
2949 height_list[rtv2->order] = (*i)->height;
2951 if ((children_list = rtv2->get_child_list()).size() > 0) {
2952 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2953 tracks = tracks |= (0x01 << (rtv2->order + children));
2954 height_list[rtv2->order + children] = (*j)->height;
2962 /* find the actual span according to the canvas */
2964 canvas_pointer_y_span = pointer_y_span;
2965 if (drag_info.last_trackview->order >= tv->order) {
2967 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2968 if (height_list[y] == 0 ) {
2969 canvas_pointer_y_span--;
2974 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2975 if ( height_list[y] == 0 ) {
2976 canvas_pointer_y_span++;
2981 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2982 RegionView* rv2 = (*i);
2983 double ix1, ix2, iy1, iy2;
2986 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2987 rv2->get_canvas_group()->i2w (ix1, iy1);
2988 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2989 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2991 if (rtv2->order != original_pointer_order) {
2992 /* this isn't the pointer track */
2994 if (canvas_pointer_y_span > 0) {
2996 /* moving up the canvas */
2997 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
2999 int32_t visible_tracks = 0;
3000 while (visible_tracks < canvas_pointer_y_span ) {
3003 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3004 /* we're passing through a hidden track */
3009 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3010 clamp_y_axis = true;
3014 clamp_y_axis = true;
3017 } else if (canvas_pointer_y_span < 0) {
3019 /*moving down the canvas*/
3021 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3024 int32_t visible_tracks = 0;
3026 while (visible_tracks > canvas_pointer_y_span ) {
3029 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3033 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3034 clamp_y_axis = true;
3039 clamp_y_axis = true;
3045 /* this is the pointer's track */
3046 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3047 clamp_y_axis = true;
3048 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3049 clamp_y_axis = true;
3057 } else if (drag_info.last_trackview == tv) {
3058 clamp_y_axis = true;
3062 if (!clamp_y_axis) {
3063 drag_info.last_trackview = tv;
3066 /************************************************************
3068 ************************************************************/
3070 /* compute the amount of pointer motion in frames, and where
3071 the region would be if we moved it by that much.
3074 if (drag_info.move_threshold_passed) {
3076 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3078 nframes_t sync_frame;
3079 nframes_t sync_offset;
3082 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3084 sync_offset = rv->region()->sync_offset (sync_dir);
3085 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3087 /* we snap if the snap modifier is not enabled.
3090 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3091 snap_to (sync_frame);
3094 if (sync_frame - sync_offset <= sync_frame) {
3095 pending_region_position = sync_frame - (sync_dir*sync_offset);
3097 pending_region_position = 0;
3101 pending_region_position = 0;
3104 if (pending_region_position > max_frames - rv->region()->length()) {
3105 pending_region_position = drag_info.last_frame_position;
3108 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3110 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3112 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3113 to make it appear at the new location.
3116 if (pending_region_position > drag_info.last_frame_position) {
3117 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3119 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3122 drag_info.last_frame_position = pending_region_position;
3129 /* threshold not passed */
3134 /*************************************************************
3136 ************************************************************/
3138 if (x_delta == 0 && (pointer_y_span == 0)) {
3139 /* haven't reached next snap point, and we're not switching
3140 trackviews. nothing to do.
3146 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3148 RegionView* rv2 = (*i);
3150 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3152 double ix1, ix2, iy1, iy2;
3153 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3154 rv2->get_canvas_group()->i2w (ix1, iy1);
3163 /*************************************************************
3165 ************************************************************/
3167 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3168 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3170 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3172 RegionView* rv = (*i);
3173 double ix1, ix2, iy1, iy2;
3174 int32_t temp_pointer_y_span = pointer_y_span;
3176 /* get item BBox, which will be relative to parent. so we have
3177 to query on a child, then convert to world coordinates using
3181 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3182 rv->get_canvas_group()->i2w (ix1, iy1);
3183 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3184 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3185 RouteTimeAxisView* temp_rtv;
3187 if ((pointer_y_span != 0) && !clamp_y_axis) {
3190 for (j = height_list.begin(); j!= height_list.end(); j++) {
3191 if (x == canvas_rtv->order) {
3192 /* we found the track the region is on */
3193 if (x != original_pointer_order) {
3194 /*this isn't from the same track we're dragging from */
3195 temp_pointer_y_span = canvas_pointer_y_span;
3197 while (temp_pointer_y_span > 0) {
3198 /* we're moving up canvas-wise,
3199 so we need to find the next track height
3201 if (j != height_list.begin()) {
3204 if (x != original_pointer_order) {
3205 /* we're not from the dragged track, so ignore hidden tracks. */
3207 temp_pointer_y_span++;
3211 temp_pointer_y_span--;
3213 while (temp_pointer_y_span < 0) {
3215 if (x != original_pointer_order) {
3217 temp_pointer_y_span--;
3221 if (j != height_list.end()) {
3224 temp_pointer_y_span++;
3226 /* find out where we'll be when we move and set height accordingly */
3228 tvp2 = trackview_by_y_position (iy1 + y_delta);
3229 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3230 rv->set_height (temp_rtv->height);
3232 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3233 personally, i think this can confuse things, but never mind.
3236 //const GdkColor& col (temp_rtv->view->get_region_color());
3237 //rv->set_color (const_cast<GdkColor&>(col));
3244 /* prevent the regionview from being moved to before
3245 the zero position on the canvas.
3250 if (-x_delta > ix1) {
3253 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3254 x_delta = max_frames - rv->region()->last_frame();
3257 if (drag_info.first_move) {
3259 /* hide any dependent views */
3261 rv->get_time_axis_view().hide_dependent_views (*rv);
3263 /* this is subtle. raising the regionview itself won't help,
3264 because raise_to_top() just puts the item on the top of
3265 its parent's stack. so, we need to put the trackview canvas_display group
3266 on the top, since its parent is the whole canvas.
3269 rv->get_canvas_group()->raise_to_top();
3270 rv->get_time_axis_view().canvas_display->raise_to_top();
3271 cursor_group->raise_to_top();
3273 /* freeze the playlists from notifying till
3277 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3278 if (rtv && rtv->is_audio_track()) {
3279 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>(rtv->get_diskstream()->playlist());
3281 /* only freeze and capture state once */
3283 insert_result = motion_frozen_playlists.insert (pl);
3284 if (insert_result.second) {
3286 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
3290 rv->region()->set_opaque(false);
3293 if (drag_info.brushing) {
3294 mouse_brush_insert_region (rv, pending_region_position);
3296 rv->move (x_delta, y_delta);
3300 if (drag_info.first_move) {
3301 cursor_group->raise_to_top();
3304 drag_info.first_move = false;
3306 if (x_delta != 0 && !drag_info.brushing) {
3307 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3313 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3316 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3317 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3318 bool nocommit = true;
3320 RouteTimeAxisView* atv;
3321 bool regionview_y_movement;
3322 bool regionview_x_movement;
3324 /* first_move is set to false if the regionview has been moved in the
3328 if (drag_info.first_move) {
3335 /* The regionview has been moved at some stage during the grab so we need
3336 to account for any mouse movement between this event and the last one.
3339 region_drag_motion_callback (item, event);
3341 if (drag_info.brushing) {
3342 /* all changes were made during motion event handlers */
3346 /* adjust for track speed */
3349 atv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3350 if (atv && atv->get_diskstream()) {
3351 speed = atv->get_diskstream()->speed();
3354 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3355 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3357 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3358 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3360 if (regionview_y_movement) {
3362 /* motion between tracks */
3364 list<RegionView*> new_selection;
3366 /* moved to a different audio track. */
3368 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3370 RegionView* rv2 = (*i);
3372 /* the region that used to be in the old playlist is not
3373 moved to the new one - we make a copy of it. as a result,
3374 any existing editor for the region should no longer be
3378 if (!drag_info.copy) {
3379 rv2->hide_region_editor();
3381 new_selection.push_back (rv2);
3385 /* first, freeze the target tracks */
3387 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3389 boost::shared_ptr<Playlist> from_playlist;
3390 boost::shared_ptr<Playlist> to_playlist;
3392 double ix1, ix2, iy1, iy2;
3394 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3395 (*i)->get_canvas_group()->i2w (ix1, iy1);
3396 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3397 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3399 (*i)->region()->set_opaque (true);
3401 from_playlist = (*i)->region()->playlist();
3402 to_playlist = atv2->playlist();
3404 /* the from_playlist was frozen in the "first_move" case
3405 of the motion handler. the insert can fail,
3406 but that doesn't matter. it just means
3407 we already have the playlist in the list.
3410 motion_frozen_playlists.insert (from_playlist);
3412 /* only freeze the to_playlist once */
3414 insert_result = motion_frozen_playlists.insert(to_playlist);
3415 if (insert_result.second) {
3416 to_playlist->freeze();
3417 session->add_command(new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3422 /* now do it again with the actual operations */
3424 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3426 boost::shared_ptr<Playlist> from_playlist;
3427 boost::shared_ptr<Playlist> to_playlist;
3429 double ix1, ix2, iy1, iy2;
3431 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3432 (*i)->get_canvas_group()->i2w (ix1, iy1);
3433 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3434 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3436 from_playlist = (*i)->region()->playlist();
3437 to_playlist = atv2->playlist();
3439 latest_regionview = 0;
3441 where = (nframes_t) (unit_to_frame (ix1) * speed);
3442 boost::shared_ptr<Region> new_region (RegionFactory::create ((*i)->region()));
3444 from_playlist->remove_region (((*i)->region()));
3446 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3447 to_playlist->add_region (new_region, where);
3450 if (latest_regionview) {
3451 selection->add (latest_regionview);
3457 /* motion within a single track */
3459 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3463 if (rv->region()->locked()) {
3467 if (regionview_x_movement) {
3468 double ownspeed = 1.0;
3469 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3471 if (atv && atv->get_diskstream()) {
3472 ownspeed = atv->get_diskstream()->speed();
3475 /* base the new region position on the current position of the regionview.*/
3477 double ix1, ix2, iy1, iy2;
3479 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3480 rv->get_canvas_group()->i2w (ix1, iy1);
3481 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3485 where = rv->region()->position();
3488 rv->get_time_axis_view().reveal_dependent_views (*rv);
3490 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3492 rv->region()->set_position (where, (void *) this);
3493 rv->region()->set_opaque (true);
3498 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3500 session->add_command (new MementoCommand<Playlist>(*((*p).get()), 0, & (*p)->get_state()));
3503 motion_frozen_playlists.clear ();
3506 commit_reversible_command ();
3511 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3513 /* Either add to or set the set the region selection, unless
3514 this is an alignment click (control used)
3517 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3518 TimeAxisView* tv = &rv.get_time_axis_view();
3519 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
3521 if (atv && atv->is_track()) {
3522 speed = atv->get_diskstream()->speed();
3525 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3527 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3529 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3531 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3535 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3541 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3547 nframes_t frame_rate;
3554 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3555 case AudioClock::BBT:
3556 session->bbt_time (frame, bbt);
3557 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3560 case AudioClock::SMPTE:
3561 session->smpte_time (frame, smpte);
3562 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3565 case AudioClock::MinSec:
3566 /* XXX this is copied from show_verbose_duration_cursor() */
3567 frame_rate = session->frame_rate();
3568 hours = frame / (frame_rate * 3600);
3569 frame = frame % (frame_rate * 3600);
3570 mins = frame / (frame_rate * 60);
3571 frame = frame % (frame_rate * 60);
3572 secs = (float) frame / (float) frame_rate;
3573 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3577 snprintf (buf, sizeof(buf), "%u", frame);
3581 if (xpos >= 0 && ypos >=0) {
3582 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3585 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3587 show_verbose_canvas_cursor ();
3591 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3598 nframes_t distance, frame_rate;
3600 Meter meter_at_start(session->tempo_map().meter_at(start));
3606 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3607 case AudioClock::BBT:
3608 session->bbt_time (start, sbbt);
3609 session->bbt_time (end, ebbt);
3612 /* XXX this computation won't work well if the
3613 user makes a selection that spans any meter changes.
3616 ebbt.bars -= sbbt.bars;
3617 if (ebbt.beats >= sbbt.beats) {
3618 ebbt.beats -= sbbt.beats;
3621 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3623 if (ebbt.ticks >= sbbt.ticks) {
3624 ebbt.ticks -= sbbt.ticks;
3627 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3630 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3633 case AudioClock::SMPTE:
3634 session->smpte_duration (end - start, smpte);
3635 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3638 case AudioClock::MinSec:
3639 /* XXX this stuff should be elsewhere.. */
3640 distance = end - start;
3641 frame_rate = session->frame_rate();
3642 hours = distance / (frame_rate * 3600);
3643 distance = distance % (frame_rate * 3600);
3644 mins = distance / (frame_rate * 60);
3645 distance = distance % (frame_rate * 60);
3646 secs = (float) distance / (float) frame_rate;
3647 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3651 snprintf (buf, sizeof(buf), "%u", end - start);
3655 if (xpos >= 0 && ypos >=0) {
3656 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3659 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3661 show_verbose_canvas_cursor ();
3665 Editor::collect_new_region_view (RegionView* rv)
3667 latest_regionview = rv;
3671 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3673 if (clicked_regionview == 0) {
3677 /* lets try to create new Region for the selection */
3679 vector<boost::shared_ptr<AudioRegion> > new_regions;
3680 create_region_from_selection (new_regions);
3682 if (new_regions.empty()) {
3686 /* XXX fix me one day to use all new regions */
3688 boost::shared_ptr<Region> region (new_regions.front());
3690 /* add it to the current stream/playlist.
3692 tricky: the streamview for the track will add a new regionview. we will
3693 catch the signal it sends when it creates the regionview to
3694 set the regionview we want to then drag.
3697 latest_regionview = 0;
3698 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3700 /* A selection grab currently creates two undo/redo operations, one for
3701 creating the new region and another for moving it.
3704 begin_reversible_command (_("selection grab"));
3706 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3708 XMLNode *before = &(playlist->get_state());
3709 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3710 XMLNode *after = &(playlist->get_state());
3711 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3713 commit_reversible_command ();
3717 if (latest_regionview == 0) {
3718 /* something went wrong */
3722 /* we need to deselect all other regionviews, and select this one
3723 i'm ignoring undo stuff, because the region creation will take care of it */
3724 selection->set (latest_regionview);
3726 drag_info.item = latest_regionview->get_canvas_group();
3727 drag_info.data = latest_regionview;
3728 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3729 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3733 drag_info.last_trackview = clicked_axisview;
3734 drag_info.last_frame_position = latest_regionview->region()->position();
3735 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3737 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3741 Editor::cancel_selection ()
3743 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3744 (*i)->hide_selection ();
3746 begin_reversible_command (_("cancel selection"));
3747 selection->clear ();
3748 clicked_selection = 0;
3749 commit_reversible_command ();
3753 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3755 nframes_t start = 0;
3762 drag_info.item = item;
3763 drag_info.motion_callback = &Editor::drag_selection;
3764 drag_info.finished_callback = &Editor::end_selection_op;
3769 case CreateSelection:
3770 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3771 drag_info.copy = true;
3773 drag_info.copy = false;
3775 start_grab (event, selector_cursor);
3778 case SelectionStartTrim:
3779 if (clicked_axisview) {
3780 clicked_axisview->order_selection_trims (item, true);
3782 start_grab (event, trimmer_cursor);
3783 start = selection->time[clicked_selection].start;
3784 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3787 case SelectionEndTrim:
3788 if (clicked_axisview) {
3789 clicked_axisview->order_selection_trims (item, false);
3791 start_grab (event, trimmer_cursor);
3792 end = selection->time[clicked_selection].end;
3793 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3797 start = selection->time[clicked_selection].start;
3799 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3803 if (selection_op == SelectionMove) {
3804 show_verbose_time_cursor(start, 10);
3806 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3811 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3813 nframes_t start = 0;
3816 nframes_t pending_position;
3818 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3819 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3822 pending_position = 0;
3825 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3826 snap_to (pending_position);
3829 /* only alter selection if the current frame is
3830 different from the last frame position (adjusted)
3833 if (pending_position == drag_info.last_pointer_frame) return;
3835 switch (selection_op) {
3836 case CreateSelection:
3838 if (drag_info.first_move) {
3839 snap_to (drag_info.grab_frame);
3842 if (pending_position < drag_info.grab_frame) {
3843 start = pending_position;
3844 end = drag_info.grab_frame;
3846 end = pending_position;
3847 start = drag_info.grab_frame;
3850 /* first drag: Either add to the selection
3851 or create a new selection->
3854 if (drag_info.first_move) {
3856 begin_reversible_command (_("range selection"));
3858 if (drag_info.copy) {
3859 /* adding to the selection */
3860 clicked_selection = selection->add (start, end);
3861 drag_info.copy = false;
3863 /* new selection-> */
3864 clicked_selection = selection->set (clicked_axisview, start, end);
3869 case SelectionStartTrim:
3871 if (drag_info.first_move) {
3872 begin_reversible_command (_("trim selection start"));
3875 start = selection->time[clicked_selection].start;
3876 end = selection->time[clicked_selection].end;
3878 if (pending_position > end) {
3881 start = pending_position;
3885 case SelectionEndTrim:
3887 if (drag_info.first_move) {
3888 begin_reversible_command (_("trim selection end"));
3891 start = selection->time[clicked_selection].start;
3892 end = selection->time[clicked_selection].end;
3894 if (pending_position < start) {
3897 end = pending_position;
3904 if (drag_info.first_move) {
3905 begin_reversible_command (_("move selection"));
3908 start = selection->time[clicked_selection].start;
3909 end = selection->time[clicked_selection].end;
3911 length = end - start;
3913 start = pending_position;
3916 end = start + length;
3921 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3922 start_canvas_autoscroll (1);
3926 selection->replace (clicked_selection, start, end);
3929 drag_info.last_pointer_frame = pending_position;
3930 drag_info.first_move = false;
3932 if (selection_op == SelectionMove) {
3933 show_verbose_time_cursor(start, 10);
3935 show_verbose_time_cursor(pending_position, 10);
3940 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3942 if (!drag_info.first_move) {
3943 drag_selection (item, event);
3944 /* XXX this is not object-oriented programming at all. ick */
3945 if (selection->time.consolidate()) {
3946 selection->TimeChanged ();
3948 commit_reversible_command ();
3950 /* just a click, no pointer movement.*/
3952 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3954 selection->clear_time();
3959 /* XXX what happens if its a music selection? */
3960 session->set_audio_range (selection->time);
3961 stop_canvas_autoscroll ();
3965 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3968 TimeAxisView* tvp = clicked_axisview;
3969 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3971 if (tv && tv->is_track()) {
3972 speed = tv->get_diskstream()->speed();
3975 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3976 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3977 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3979 motion_frozen_playlists.clear();
3981 //drag_info.item = clicked_regionview->get_name_highlight();
3982 drag_info.item = item;
3983 drag_info.motion_callback = &Editor::trim_motion_callback;
3984 drag_info.finished_callback = &Editor::trim_finished_callback;
3986 start_grab (event, trimmer_cursor);
3988 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3989 trim_op = ContentsTrim;
3991 /* These will get overridden for a point trim.*/
3992 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3993 /* closer to start */
3994 trim_op = StartTrim;
3995 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4003 show_verbose_time_cursor(region_start, 10);
4006 show_verbose_time_cursor(region_end, 10);
4009 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4015 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4017 RegionView* rv = clicked_regionview;
4018 nframes_t frame_delta = 0;
4019 bool left_direction;
4020 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4022 /* snap modifier works differently here..
4023 its' current state has to be passed to the
4024 various trim functions in order to work properly
4028 TimeAxisView* tvp = clicked_axisview;
4029 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4030 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4032 if (tv && tv->is_track()) {
4033 speed = tv->get_diskstream()->speed();
4036 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4037 left_direction = true;
4039 left_direction = false;
4043 snap_to (drag_info.current_pointer_frame);
4046 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4050 if (drag_info.first_move) {
4056 trim_type = "Region start trim";
4059 trim_type = "Region end trim";
4062 trim_type = "Region content trim";
4066 begin_reversible_command (trim_type);
4068 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4069 (*i)->region()->set_opaque(false);
4070 (*i)->region()->freeze ();
4072 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4074 arv->temporarily_hide_envelope ();
4076 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4077 insert_result = motion_frozen_playlists.insert (pl);
4078 if (insert_result.second) {
4079 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4084 if (left_direction) {
4085 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4087 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4092 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4095 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4096 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4102 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4105 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4106 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4113 bool swap_direction = false;
4115 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4116 swap_direction = true;
4119 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4120 i != selection->regions.by_layer().end(); ++i)
4122 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4130 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4133 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4136 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4140 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4141 drag_info.first_move = false;
4145 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4147 boost::shared_ptr<Region> region (rv.region());
4149 if (region->locked()) {
4153 nframes_t new_bound;
4156 TimeAxisView* tvp = clicked_axisview;
4157 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4159 if (tv && tv->is_track()) {
4160 speed = tv->get_diskstream()->speed();
4163 if (left_direction) {
4164 if (swap_direction) {
4165 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4167 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4170 if (swap_direction) {
4171 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4173 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4178 snap_to (new_bound);
4180 region->trim_start ((nframes_t) (new_bound * speed), this);
4181 rv.region_changed (StartChanged);
4185 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4187 boost::shared_ptr<Region> region (rv.region());
4189 if (region->locked()) {
4193 nframes_t new_bound;
4196 TimeAxisView* tvp = clicked_axisview;
4197 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4199 if (tv && tv->is_track()) {
4200 speed = tv->get_diskstream()->speed();
4203 if (left_direction) {
4204 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4206 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4210 snap_to (new_bound, (left_direction ? 0 : 1));
4213 region->trim_front ((nframes_t) (new_bound * speed), this);
4215 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4219 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4221 boost::shared_ptr<Region> region (rv.region());
4223 if (region->locked()) {
4227 nframes_t new_bound;
4230 TimeAxisView* tvp = clicked_axisview;
4231 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4233 if (tv && tv->is_track()) {
4234 speed = tv->get_diskstream()->speed();
4237 if (left_direction) {
4238 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4240 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4244 snap_to (new_bound);
4246 region->trim_end ((nframes_t) (new_bound * speed), this);
4247 rv.region_changed (LengthChanged);
4251 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4253 if (!drag_info.first_move) {
4254 trim_motion_callback (item, event);
4256 if (!clicked_regionview->get_selected()) {
4257 thaw_region_after_trim (*clicked_regionview);
4260 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4261 i != selection->regions.by_layer().end(); ++i)
4263 thaw_region_after_trim (**i);
4264 (*i)->region()->set_opaque(true);
4268 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4270 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4273 motion_frozen_playlists.clear ();
4275 commit_reversible_command();
4277 /* no mouse movement */
4283 Editor::point_trim (GdkEvent* event)
4285 RegionView* rv = clicked_regionview;
4286 nframes_t new_bound = drag_info.current_pointer_frame;
4288 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4289 snap_to (new_bound);
4292 /* Choose action dependant on which button was pressed */
4293 switch (event->button.button) {
4295 trim_op = StartTrim;
4296 begin_reversible_command (_("Start point trim"));
4298 if (rv->get_selected()) {
4300 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4301 i != selection->regions.by_layer().end(); ++i)
4303 if (!(*i)->region()->locked()) {
4304 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4305 XMLNode &before = pl->get_state();
4306 (*i)->region()->trim_front (new_bound, this);
4307 XMLNode &after = pl->get_state();
4308 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4314 if (!rv->region()->locked()) {
4315 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4316 XMLNode &before = pl->get_state();
4317 rv->region()->trim_front (new_bound, this);
4318 XMLNode &after = pl->get_state();
4319 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4323 commit_reversible_command();
4328 begin_reversible_command (_("End point trim"));
4330 if (rv->get_selected()) {
4332 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4334 if (!(*i)->region()->locked()) {
4335 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4336 XMLNode &before = pl->get_state();
4337 (*i)->region()->trim_end (new_bound, this);
4338 XMLNode &after = pl->get_state();
4339 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4345 if (!rv->region()->locked()) {
4346 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4347 XMLNode &before = pl->get_state();
4348 rv->region()->trim_end (new_bound, this);
4349 XMLNode &after = pl->get_state();
4350 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4354 commit_reversible_command();
4363 Editor::thaw_region_after_trim (RegionView& rv)
4365 boost::shared_ptr<Region> region (rv.region());
4367 if (region->locked()) {
4371 region->thaw (_("trimmed region"));
4372 XMLNode &after = region->playlist()->get_state();
4373 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4375 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4377 arv->unhide_envelope ();
4381 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4386 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4387 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4391 Location* location = find_location_from_marker (marker, is_start);
4392 location->set_hidden (true, this);
4397 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4403 drag_info.item = item;
4404 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4405 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4407 range_marker_op = op;
4409 if (!temp_location) {
4410 temp_location = new Location;
4414 case CreateRangeMarker:
4415 case CreateTransportMarker:
4417 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4418 drag_info.copy = true;
4420 drag_info.copy = false;
4422 start_grab (event, selector_cursor);
4426 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4431 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4433 nframes_t start = 0;
4435 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4437 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4438 snap_to (drag_info.current_pointer_frame);
4441 /* only alter selection if the current frame is
4442 different from the last frame position.
4445 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4447 switch (range_marker_op) {
4448 case CreateRangeMarker:
4449 case CreateTransportMarker:
4450 if (drag_info.first_move) {
4451 snap_to (drag_info.grab_frame);
4454 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4455 start = drag_info.current_pointer_frame;
4456 end = drag_info.grab_frame;
4458 end = drag_info.current_pointer_frame;
4459 start = drag_info.grab_frame;
4462 /* first drag: Either add to the selection
4463 or create a new selection.
4466 if (drag_info.first_move) {
4468 temp_location->set (start, end);
4472 update_marker_drag_item (temp_location);
4473 range_marker_drag_rect->show();
4474 range_marker_drag_rect->raise_to_top();
4480 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4481 start_canvas_autoscroll (1);
4485 temp_location->set (start, end);
4487 double x1 = frame_to_pixel (start);
4488 double x2 = frame_to_pixel (end);
4489 crect->property_x1() = x1;
4490 crect->property_x2() = x2;
4492 update_marker_drag_item (temp_location);
4495 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4496 drag_info.first_move = false;
4498 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4503 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4505 Location * newloc = 0;
4508 if (!drag_info.first_move) {
4509 drag_range_markerbar_op (item, event);
4511 switch (range_marker_op) {
4512 case CreateRangeMarker:
4514 begin_reversible_command (_("new range marker"));
4515 XMLNode &before = session->locations()->get_state();
4516 session->locations()->next_available_name(rangename,"unnamed");
4517 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4518 session->locations()->add (newloc, true);
4519 XMLNode &after = session->locations()->get_state();
4520 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4521 commit_reversible_command ();
4523 range_bar_drag_rect->hide();
4524 range_marker_drag_rect->hide();
4528 case CreateTransportMarker:
4529 // popup menu to pick loop or punch
4530 new_transport_marker_context_menu (&event->button, item);
4535 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4537 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4542 start = session->locations()->first_mark_before (drag_info.grab_frame);
4543 end = session->locations()->first_mark_after (drag_info.grab_frame);
4545 if (end == max_frames) {
4546 end = session->current_end_frame ();
4550 start = session->current_start_frame ();
4553 switch (mouse_mode) {
4555 /* find the two markers on either side and then make the selection from it */
4556 cerr << "select between " << start << " .. " << end << endl;
4557 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4561 /* find the two markers on either side of the click and make the range out of it */
4562 selection->set (0, start, end);
4571 stop_canvas_autoscroll ();
4577 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4579 drag_info.item = item;
4580 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4581 drag_info.finished_callback = &Editor::end_mouse_zoom;
4583 start_grab (event, zoom_cursor);
4585 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4589 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4594 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4595 snap_to (drag_info.current_pointer_frame);
4597 if (drag_info.first_move) {
4598 snap_to (drag_info.grab_frame);
4602 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4604 /* base start and end on initial click position */
4605 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4606 start = drag_info.current_pointer_frame;
4607 end = drag_info.grab_frame;
4609 end = drag_info.current_pointer_frame;
4610 start = drag_info.grab_frame;
4615 if (drag_info.first_move) {
4617 zoom_rect->raise_to_top();
4620 reposition_zoom_rect(start, end);
4622 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4623 drag_info.first_move = false;
4625 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4630 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4632 if (!drag_info.first_move) {
4633 drag_mouse_zoom (item, event);
4635 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4636 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4638 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4641 temporal_zoom_to_frame (false, drag_info.grab_frame);
4643 temporal_zoom_step (false);
4644 center_screen (drag_info.grab_frame);
4652 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4654 double x1 = frame_to_pixel (start);
4655 double x2 = frame_to_pixel (end);
4656 double y2 = full_canvas_height - 1.0;
4658 zoom_rect->property_x1() = x1;
4659 zoom_rect->property_y1() = 1.0;
4660 zoom_rect->property_x2() = x2;
4661 zoom_rect->property_y2() = y2;
4665 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4667 drag_info.item = item;
4668 drag_info.motion_callback = &Editor::drag_rubberband_select;
4669 drag_info.finished_callback = &Editor::end_rubberband_select;
4671 start_grab (event, cross_hair_cursor);
4673 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4677 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4684 /* use a bigger drag threshold than the default */
4686 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4690 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4691 if (drag_info.first_move) {
4692 snap_to (drag_info.grab_frame);
4694 snap_to (drag_info.current_pointer_frame);
4697 /* base start and end on initial click position */
4699 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4700 start = drag_info.current_pointer_frame;
4701 end = drag_info.grab_frame;
4703 end = drag_info.current_pointer_frame;
4704 start = drag_info.grab_frame;
4707 if (drag_info.current_pointer_y < drag_info.grab_y) {
4708 y1 = drag_info.current_pointer_y;
4709 y2 = drag_info.grab_y;
4711 y2 = drag_info.current_pointer_y;
4712 y1 = drag_info.grab_y;
4716 if (start != end || y1 != y2) {
4718 double x1 = frame_to_pixel (start);
4719 double x2 = frame_to_pixel (end);
4721 rubberband_rect->property_x1() = x1;
4722 rubberband_rect->property_y1() = y1;
4723 rubberband_rect->property_x2() = x2;
4724 rubberband_rect->property_y2() = y2;
4726 rubberband_rect->show();
4727 rubberband_rect->raise_to_top();
4729 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4730 drag_info.first_move = false;
4732 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4737 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4739 if (!drag_info.first_move) {
4741 drag_rubberband_select (item, event);
4744 if (drag_info.current_pointer_y < drag_info.grab_y) {
4745 y1 = drag_info.current_pointer_y;
4746 y2 = drag_info.grab_y;
4749 y2 = drag_info.current_pointer_y;
4750 y1 = drag_info.grab_y;
4754 Selection::Operation op = Keyboard::selection_type (event->button.state);
4757 begin_reversible_command (_("rubberband selection"));
4759 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4760 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4762 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4766 commit_reversible_command ();
4770 selection->clear_tracks();
4771 selection->clear_regions();
4772 selection->clear_points ();
4773 selection->clear_lines ();
4776 rubberband_rect->hide();
4781 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4783 using namespace Gtkmm2ext;
4785 ArdourPrompter prompter (false);
4787 prompter.set_prompt (_("Name for region:"));
4788 prompter.set_initial_text (clicked_regionview->region()->name());
4789 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4790 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4791 prompter.show_all ();
4792 switch (prompter.run ()) {
4793 case Gtk::RESPONSE_ACCEPT:
4795 prompter.get_result(str);
4797 clicked_regionview->region()->set_name (str);
4805 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4807 drag_info.item = item;
4808 drag_info.motion_callback = &Editor::time_fx_motion;
4809 drag_info.finished_callback = &Editor::end_time_fx;
4813 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4817 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4819 RegionView* rv = clicked_regionview;
4821 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4822 snap_to (drag_info.current_pointer_frame);
4825 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4829 if (drag_info.current_pointer_frame > rv->region()->position()) {
4830 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4833 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4834 drag_info.first_move = false;
4836 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4840 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4842 clicked_regionview->get_time_axis_view().hide_timestretch ();
4844 if (drag_info.first_move) {
4848 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4849 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4851 begin_reversible_command (_("timestretch"));
4853 if (run_timestretch (selection->regions, percentage) == 0) {
4854 session->commit_reversible_command ();
4859 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4861 /* no brushing without a useful snap setting */
4864 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4867 switch (snap_mode) {
4869 return; /* can't work because it allows region to be placed anywhere */
4874 switch (snap_type) {
4877 case SnapToEditCursor:
4884 /* don't brush a copy over the original */
4886 if (pos == rv->region()->position()) {
4890 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4892 if (atv == 0 || !atv->is_track()) {
4896 boost::shared_ptr<Playlist> playlist = atv->playlist();
4897 double speed = atv->get_diskstream()->speed();
4899 XMLNode &before = playlist->get_state();
4900 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4901 XMLNode &after = playlist->get_state();
4902 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4904 // playlist is frozen, so we have to update manually
4906 playlist->Modified(); /* EMIT SIGNAL */
4910 Editor::track_height_step_timeout ()
4913 struct timeval delta;
4915 gettimeofday (&now, 0);
4916 timersub (&now, &last_track_height_step_timestamp, &delta);
4918 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4919 current_stepping_trackview = 0;