2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <pbd/error.h>
30 #include <gtkmm2ext/utils.h>
31 #include <pbd/memento_command.h>
33 #include "ardour_ui.h"
35 #include "time_axis_view.h"
36 #include "audio_time_axis.h"
37 #include "audio_region_view.h"
39 #include "streamview.h"
40 #include "region_gain_line.h"
41 #include "automation_time_axis.h"
44 #include "selection.h"
47 #include "rgb_macros.h"
49 #include <ardour/types.h>
50 #include <ardour/route.h>
51 #include <ardour/audio_track.h>
52 #include <ardour/audio_diskstream.h>
53 #include <ardour/playlist.h>
54 #include <ardour/audioplaylist.h>
55 #include <ardour/audioregion.h>
56 #include <ardour/midi_region.h>
57 #include <ardour/dB.h>
58 #include <ardour/utils.h>
59 #include <ardour/region_factory.h>
66 using namespace ARDOUR;
70 using namespace Editing;
73 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
87 switch (event->type) {
88 case GDK_BUTTON_RELEASE:
89 case GDK_BUTTON_PRESS:
90 case GDK_2BUTTON_PRESS:
91 case GDK_3BUTTON_PRESS:
92 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
94 case GDK_MOTION_NOTIFY:
95 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
97 case GDK_ENTER_NOTIFY:
98 case GDK_LEAVE_NOTIFY:
99 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
102 case GDK_KEY_RELEASE:
103 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
106 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
110 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
111 position is negative (as can be the case with motion events in particular),
112 the frame location is always positive.
115 return pixel_to_frame (*pcx);
119 Editor::mouse_mode_toggled (MouseMode m)
121 if (ignore_mouse_mode_toggle) {
127 if (mouse_select_button.get_active()) {
133 if (mouse_move_button.get_active()) {
139 if (mouse_gain_button.get_active()) {
145 if (mouse_zoom_button.get_active()) {
151 if (mouse_timefx_button.get_active()) {
157 if (mouse_audition_button.get_active()) {
168 Editor::set_mouse_mode (MouseMode m, bool force)
170 if (drag_info.item) {
174 if (!force && m == mouse_mode) {
182 if (mouse_mode != MouseRange) {
184 /* in all modes except range, hide the range selection,
185 show the object (region) selection.
188 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
189 (*i)->set_should_show_selection (true);
191 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
192 (*i)->hide_selection ();
198 in range mode,show the range selection.
201 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
202 if ((*i)->get_selected()) {
203 (*i)->show_selection (selection->time);
208 /* XXX the hack of unsetting all other buttongs should go
209 away once GTK2 allows us to use regular radio buttons drawn like
210 normal buttons, rather than my silly GroupedButton hack.
213 ignore_mouse_mode_toggle = true;
215 switch (mouse_mode) {
217 mouse_select_button.set_active (true);
218 current_canvas_cursor = selector_cursor;
222 mouse_move_button.set_active (true);
223 current_canvas_cursor = grabber_cursor;
227 mouse_gain_button.set_active (true);
228 current_canvas_cursor = cross_hair_cursor;
232 mouse_zoom_button.set_active (true);
233 current_canvas_cursor = zoom_cursor;
237 mouse_timefx_button.set_active (true);
238 current_canvas_cursor = time_fx_cursor; // just use playhead
242 mouse_audition_button.set_active (true);
243 current_canvas_cursor = speaker_cursor;
247 ignore_mouse_mode_toggle = false;
250 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
255 Editor::step_mouse_mode (bool next)
257 switch (current_mouse_mode()) {
259 if (next) set_mouse_mode (MouseRange);
260 else set_mouse_mode (MouseTimeFX);
264 if (next) set_mouse_mode (MouseZoom);
265 else set_mouse_mode (MouseObject);
269 if (next) set_mouse_mode (MouseGain);
270 else set_mouse_mode (MouseRange);
274 if (next) set_mouse_mode (MouseTimeFX);
275 else set_mouse_mode (MouseZoom);
279 if (next) set_mouse_mode (MouseAudition);
280 else set_mouse_mode (MouseGain);
284 if (next) set_mouse_mode (MouseObject);
285 else set_mouse_mode (MouseTimeFX);
291 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
297 /* in object/audition/timefx mode, any button press sets
298 the selection if the object can be selected. this is a
299 bit of hack, because we want to avoid this if the
300 mouse operation is a region alignment.
302 note: not dbl-click or triple-click
305 if (((mouse_mode != MouseObject) &&
306 (mouse_mode != MouseAudition || item_type != RegionItem) &&
307 (mouse_mode != MouseTimeFX || item_type != RegionItem)) ||
308 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
313 Selection::Operation op = Keyboard::selection_type (event->button.state);
314 bool press = (event->type == GDK_BUTTON_PRESS);
316 begin_reversible_command (_("select on click"));
320 /* XXX make tying track/region selection optional */
321 c1 = set_selected_track_from_click (op, true);
322 c2 = set_selected_regionview_from_click (press, op, true);
326 case RegionViewNameHighlight:
328 /* XXX make tying track/region selection optional */
329 c1 = set_selected_track_from_click (op, true);
330 c2 = set_selected_regionview_from_click (press, op, true);
334 case GainAutomationControlPointItem:
335 case PanAutomationControlPointItem:
336 case RedirectAutomationControlPointItem:
337 /* XXX make tying track/region selection optional */
338 c1 = set_selected_track_from_click (op, true);
339 c2 = set_selected_control_point_from_click (op, false);
344 commit = set_selected_track_from_click (op, true);
347 case AutomationTrackItem:
348 commit = set_selected_track_from_click (op, true);
355 #define SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
356 #ifdef SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
357 /* in range mode, button 1/2/3 press potentially selects a track */
359 if (mouse_mode == MouseRange &&
360 event->type == GDK_BUTTON_PRESS &&
361 event->button.button <= 3) {
366 case AutomationTrackItem:
367 commit = set_selected_track_from_click (op, true);
376 commit_reversible_command ();
381 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
383 nframes_t where = event_frame (event, 0, 0);
385 track_canvas.grab_focus();
387 if (session && session->actively_recording()) {
391 button_selection (item, event, item_type);
393 if (drag_info.item == 0 &&
394 (Keyboard::is_delete_event (&event->button) ||
395 Keyboard::is_context_menu_event (&event->button) ||
396 Keyboard::is_edit_event (&event->button))) {
398 /* handled by button release */
402 switch (event->button.button) {
405 if (event->type == GDK_BUTTON_PRESS) {
407 if (drag_info.item) {
408 drag_info.item->ungrab (event->button.time);
411 /* single mouse clicks on any of these item types operate
412 independent of mouse mode, mostly because they are
413 not on the main track canvas or because we want
419 case PlayheadCursorItem:
420 start_cursor_grab (item, event);
424 if (Keyboard::modifier_state_equals (event->button.state,
425 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
426 hide_marker (item, event);
428 start_marker_grab (item, event);
432 case TempoMarkerItem:
433 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
434 start_tempo_marker_copy_grab (item, event);
436 start_tempo_marker_grab (item, event);
440 case MeterMarkerItem:
441 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
442 start_meter_marker_copy_grab (item, event);
444 start_meter_marker_grab (item, event);
454 case RangeMarkerBarItem:
455 start_range_markerbar_op (item, event, CreateRangeMarker);
459 case TransportMarkerBarItem:
460 start_range_markerbar_op (item, event, CreateTransportMarker);
469 switch (mouse_mode) {
472 case StartSelectionTrimItem:
473 start_selection_op (item, event, SelectionStartTrim);
476 case EndSelectionTrimItem:
477 start_selection_op (item, event, SelectionEndTrim);
481 if (Keyboard::modifier_state_contains
482 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
483 // contains and not equals because I can't use alt as a modifier alone.
484 start_selection_grab (item, event);
485 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
486 /* grab selection for moving */
487 start_selection_op (item, event, SelectionMove);
490 /* this was debated, but decided the more common action was to
491 make a new selection */
492 start_selection_op (item, event, CreateSelection);
497 start_selection_op (item, event, CreateSelection);
503 if (Keyboard::modifier_state_contains (event->button.state,
504 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt))
505 && event->type == GDK_BUTTON_PRESS) {
507 start_rubberband_select (item, event);
509 } else if (event->type == GDK_BUTTON_PRESS) {
512 case FadeInHandleItem:
513 start_fade_in_grab (item, event);
516 case FadeOutHandleItem:
517 start_fade_out_grab (item, event);
521 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
522 start_region_copy_grab (item, event);
523 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
524 start_region_brush_grab (item, event);
526 start_region_grab (item, event);
530 case RegionViewNameHighlight:
531 start_trim (item, event);
536 /* rename happens on edit clicks */
537 start_trim (clicked_regionview->get_name_highlight(), event);
541 case GainAutomationControlPointItem:
542 case PanAutomationControlPointItem:
543 case RedirectAutomationControlPointItem:
544 start_control_point_grab (item, event);
548 case GainAutomationLineItem:
549 case PanAutomationLineItem:
550 case RedirectAutomationLineItem:
551 start_line_grab_from_line (item, event);
556 case AutomationTrackItem:
557 start_rubberband_select (item, event);
560 /* <CMT Additions> */
561 case ImageFrameHandleStartItem:
562 imageframe_start_handle_op(item, event) ;
565 case ImageFrameHandleEndItem:
566 imageframe_end_handle_op(item, event) ;
569 case MarkerViewHandleStartItem:
570 markerview_item_start_handle_op(item, event) ;
573 case MarkerViewHandleEndItem:
574 markerview_item_end_handle_op(item, event) ;
577 /* </CMT Additions> */
579 /* <CMT Additions> */
581 start_markerview_grab(item, event) ;
584 start_imageframe_grab(item, event) ;
586 /* </CMT Additions> */
602 // start_line_grab_from_regionview (item, event);
605 case GainControlPointItem:
606 start_control_point_grab (item, event);
610 start_line_grab_from_line (item, event);
613 case GainAutomationControlPointItem:
614 case PanAutomationControlPointItem:
615 case RedirectAutomationControlPointItem:
616 start_control_point_grab (item, event);
627 case GainAutomationControlPointItem:
628 case PanAutomationControlPointItem:
629 case RedirectAutomationControlPointItem:
630 start_control_point_grab (item, event);
633 case GainAutomationLineItem:
634 case PanAutomationLineItem:
635 case RedirectAutomationLineItem:
636 start_line_grab_from_line (item, event);
640 // XXX need automation mode to identify which
642 // start_line_grab_from_regionview (item, event);
652 if (event->type == GDK_BUTTON_PRESS) {
653 start_mouse_zoom (item, event);
660 if (item_type == RegionItem) {
661 start_time_fx (item, event);
666 /* handled in release */
675 switch (mouse_mode) {
677 if (event->type == GDK_BUTTON_PRESS) {
680 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
681 start_region_copy_grab (item, event);
683 start_region_grab (item, event);
687 case GainAutomationControlPointItem:
688 case PanAutomationControlPointItem:
689 case RedirectAutomationControlPointItem:
690 start_control_point_grab (item, event);
701 case RegionViewNameHighlight:
702 start_trim (item, event);
707 start_trim (clicked_regionview->get_name_highlight(), event);
718 if (event->type == GDK_BUTTON_PRESS) {
719 /* relax till release */
726 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
727 temporal_zoom_session();
729 temporal_zoom_to_frame (true, event_frame(event));
744 switch (mouse_mode) {
746 //temporal_zoom_to_frame (true, where);
747 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
748 temporal_zoom_to_frame (true, where);
751 temporal_zoom_step (true);
756 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
757 scroll_backward (0.6f);
760 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
761 scroll_tracks_up_line ();
763 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
764 if (clicked_axisview) {
765 if (!current_stepping_trackview) {
766 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
767 current_stepping_trackview = clicked_axisview;
769 gettimeofday (&last_track_height_step_timestamp, 0);
770 current_stepping_trackview->step_height (true);
773 else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
774 temporal_zoom_to_frame (true, where);
781 switch (mouse_mode) {
783 // temporal_zoom_to_frame (false, where);
784 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
785 temporal_zoom_to_frame (false, where);
788 temporal_zoom_step (false);
793 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
794 scroll_forward (0.6f);
797 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
798 scroll_tracks_down_line ();
800 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
801 if (clicked_axisview) {
802 if (!current_stepping_trackview) {
803 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
804 current_stepping_trackview = clicked_axisview;
806 gettimeofday (&last_track_height_step_timestamp, 0);
807 current_stepping_trackview->step_height (false);
809 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
810 temporal_zoom_to_frame (false, where);
825 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
827 nframes_t where = event_frame (event, 0, 0);
829 /* no action if we're recording */
831 if (session && session->actively_recording()) {
835 /* first, see if we're finishing a drag ... */
837 if (drag_info.item) {
838 if (end_grab (item, event)) {
839 /* grab dragged, so do nothing else */
844 button_selection (item, event, item_type);
846 /* edit events get handled here */
848 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
854 case TempoMarkerItem:
855 edit_tempo_marker (item);
858 case MeterMarkerItem:
859 edit_meter_marker (item);
863 if (clicked_regionview->name_active()) {
864 return mouse_rename_region (item, event);
874 /* context menu events get handled here */
876 if (Keyboard::is_context_menu_event (&event->button)) {
878 if (drag_info.item == 0) {
880 /* no matter which button pops up the context menu, tell the menu
881 widget to use button 1 to drive menu selection.
886 case FadeInHandleItem:
888 case FadeOutHandleItem:
889 popup_fade_context_menu (1, event->button.time, item, item_type);
893 popup_track_context_menu (1, event->button.time, item_type, false, where);
897 case RegionViewNameHighlight:
899 popup_track_context_menu (1, event->button.time, item_type, false, where);
903 popup_track_context_menu (1, event->button.time, item_type, true, where);
906 case AutomationTrackItem:
907 popup_track_context_menu (1, event->button.time, item_type, false, where);
911 case RangeMarkerBarItem:
912 case TransportMarkerBarItem:
915 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
919 marker_context_menu (&event->button, item);
922 case TempoMarkerItem:
923 tm_marker_context_menu (&event->button, item);
926 case MeterMarkerItem:
927 tm_marker_context_menu (&event->button, item);
930 case CrossfadeViewItem:
931 popup_track_context_menu (1, event->button.time, item_type, false, where);
934 /* <CMT Additions> */
936 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
938 case ImageFrameTimeAxisItem:
939 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
942 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
944 case MarkerTimeAxisItem:
945 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
947 /* <CMT Additions> */
958 /* delete events get handled here */
960 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
963 case TempoMarkerItem:
964 remove_tempo_marker (item);
967 case MeterMarkerItem:
968 remove_meter_marker (item);
972 remove_marker (*item, event);
976 if (mouse_mode == MouseObject) {
977 remove_clicked_region ();
981 case GainControlPointItem:
982 if (mouse_mode == MouseGain) {
983 remove_gain_control_point (item, event);
987 case GainAutomationControlPointItem:
988 case PanAutomationControlPointItem:
989 case RedirectAutomationControlPointItem:
990 remove_control_point (item, event);
999 switch (event->button.button) {
1002 switch (item_type) {
1003 /* see comments in button_press_handler */
1004 case EditCursorItem:
1005 case PlayheadCursorItem:
1008 case GainAutomationLineItem:
1009 case PanAutomationLineItem:
1010 case RedirectAutomationLineItem:
1011 case StartSelectionTrimItem:
1012 case EndSelectionTrimItem:
1016 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1017 snap_to (where, 0, true);
1019 mouse_add_new_marker (where);
1023 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1026 mouse_add_new_tempo_event (where);
1030 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1038 switch (mouse_mode) {
1040 switch (item_type) {
1041 case AutomationTrackItem:
1042 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->add_automation_event
1056 // Gain only makes sense for audio regions
1057 if ( ! dynamic_cast<AudioRegionView*>(clicked_regionview))
1060 switch (item_type) {
1062 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1066 case AutomationTrackItem:
1067 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1068 add_automation_event (item, event, where, event->button.y);
1077 switch (item_type) {
1079 audition_selected_region ();
1096 switch (mouse_mode) {
1099 switch (item_type) {
1101 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1103 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1106 // Button2 click is unused
1119 // x_style_paste (where, 1.0);
1139 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1145 switch (item_type) {
1146 case GainControlPointItem:
1147 if (mouse_mode == MouseGain) {
1148 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1149 cp->set_visible (true);
1153 at_y = cp->get_y ();
1154 cp->item->i2w (at_x, at_y);
1158 fraction = 1.0 - (cp->get_y() / cp->line.height());
1160 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1161 show_verbose_canvas_cursor ();
1163 if (is_drawable()) {
1164 track_canvas.get_window()->set_cursor (*fader_cursor);
1169 case GainAutomationControlPointItem:
1170 case PanAutomationControlPointItem:
1171 case RedirectAutomationControlPointItem:
1172 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1173 cp->set_visible (true);
1177 at_y = cp->get_y ();
1178 cp->item->i2w (at_x, at_y);
1182 fraction = 1.0 - (cp->get_y() / cp->line.height());
1184 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1185 show_verbose_canvas_cursor ();
1187 if (is_drawable()) {
1188 track_canvas.get_window()->set_cursor (*fader_cursor);
1193 if (mouse_mode == MouseGain) {
1194 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1196 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1197 if (is_drawable()) {
1198 track_canvas.get_window()->set_cursor (*fader_cursor);
1203 case GainAutomationLineItem:
1204 case RedirectAutomationLineItem:
1205 case PanAutomationLineItem:
1207 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1209 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1211 if (is_drawable()) {
1212 track_canvas.get_window()->set_cursor (*fader_cursor);
1216 case RegionViewNameHighlight:
1217 if (is_drawable() && mouse_mode == MouseObject) {
1218 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1222 case StartSelectionTrimItem:
1223 case EndSelectionTrimItem:
1224 /* <CMT Additions> */
1225 case ImageFrameHandleStartItem:
1226 case ImageFrameHandleEndItem:
1227 case MarkerViewHandleStartItem:
1228 case MarkerViewHandleEndItem:
1229 /* </CMT Additions> */
1231 if (is_drawable()) {
1232 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1236 case EditCursorItem:
1237 case PlayheadCursorItem:
1238 if (is_drawable()) {
1239 track_canvas.get_window()->set_cursor (*grabber_cursor);
1243 case RegionViewName:
1245 /* when the name is not an active item, the entire name highlight is for trimming */
1247 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1248 if (mouse_mode == MouseObject && is_drawable()) {
1249 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1255 case AutomationTrackItem:
1256 if (is_drawable()) {
1257 Gdk::Cursor *cursor;
1258 switch (mouse_mode) {
1260 cursor = selector_cursor;
1263 cursor = zoom_cursor;
1266 cursor = cross_hair_cursor;
1270 track_canvas.get_window()->set_cursor (*cursor);
1272 AutomationTimeAxisView* atv;
1273 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1274 clear_entered_track = false;
1275 set_entered_track (atv);
1281 case RangeMarkerBarItem:
1282 case TransportMarkerBarItem:
1285 if (is_drawable()) {
1286 time_canvas.get_window()->set_cursor (*timebar_cursor);
1291 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1294 marker->set_color_rgba (color_map[cEnteredMarker]);
1296 case MeterMarkerItem:
1297 case TempoMarkerItem:
1298 if (is_drawable()) {
1299 time_canvas.get_window()->set_cursor (*timebar_cursor);
1302 case FadeInHandleItem:
1303 case FadeOutHandleItem:
1304 if (mouse_mode == MouseObject) {
1305 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1307 rect->property_fill_color_rgba() = 0;
1308 rect->property_outline_pixels() = 1;
1317 /* second pass to handle entered track status in a comprehensible way.
1320 switch (item_type) {
1322 case GainAutomationLineItem:
1323 case RedirectAutomationLineItem:
1324 case PanAutomationLineItem:
1325 case GainControlPointItem:
1326 case GainAutomationControlPointItem:
1327 case PanAutomationControlPointItem:
1328 case RedirectAutomationControlPointItem:
1329 /* these do not affect the current entered track state */
1330 clear_entered_track = false;
1333 case AutomationTrackItem:
1334 /* handled above already */
1338 set_entered_track (0);
1346 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1355 switch (item_type) {
1356 case GainControlPointItem:
1357 case GainAutomationControlPointItem:
1358 case PanAutomationControlPointItem:
1359 case RedirectAutomationControlPointItem:
1360 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1361 if (cp->line.npoints() > 1) {
1362 if (!cp->selected) {
1363 cp->set_visible (false);
1367 if (is_drawable()) {
1368 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1371 hide_verbose_canvas_cursor ();
1374 case RegionViewNameHighlight:
1375 case StartSelectionTrimItem:
1376 case EndSelectionTrimItem:
1377 case EditCursorItem:
1378 case PlayheadCursorItem:
1379 /* <CMT Additions> */
1380 case ImageFrameHandleStartItem:
1381 case ImageFrameHandleEndItem:
1382 case MarkerViewHandleStartItem:
1383 case MarkerViewHandleEndItem:
1384 /* </CMT Additions> */
1385 if (is_drawable()) {
1386 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1391 case GainAutomationLineItem:
1392 case RedirectAutomationLineItem:
1393 case PanAutomationLineItem:
1394 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1396 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1398 line->property_fill_color_rgba() = al->get_line_color();
1400 if (is_drawable()) {
1401 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1405 case RegionViewName:
1406 /* see enter_handler() for notes */
1407 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1408 if (is_drawable() && mouse_mode == MouseObject) {
1409 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1414 case RangeMarkerBarItem:
1415 case TransportMarkerBarItem:
1419 if (is_drawable()) {
1420 time_canvas.get_window()->set_cursor (*timebar_cursor);
1425 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1428 loc = find_location_from_marker (marker, is_start);
1429 if (loc) location_flags_changed (loc, this);
1431 case MeterMarkerItem:
1432 case TempoMarkerItem:
1434 if (is_drawable()) {
1435 time_canvas.get_window()->set_cursor (*timebar_cursor);
1440 case FadeInHandleItem:
1441 case FadeOutHandleItem:
1442 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1444 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1446 rect->property_fill_color_rgba() = rv->get_fill_color();
1447 rect->property_outline_pixels() = 0;
1452 case AutomationTrackItem:
1453 if (is_drawable()) {
1454 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1455 clear_entered_track = true;
1456 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1468 Editor::left_automation_track ()
1470 if (clear_entered_track) {
1471 set_entered_track (0);
1472 clear_entered_track = false;
1478 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1482 /* We call this so that MOTION_NOTIFY events continue to be
1483 delivered to the canvas. We need to do this because we set
1484 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1485 the density of the events, at the expense of a round-trip
1486 to the server. Given that this will mostly occur on cases
1487 where DISPLAY = :0.0, and given the cost of what the motion
1488 event might do, its a good tradeoff.
1491 track_canvas.get_pointer (x, y);
1493 if (current_stepping_trackview) {
1494 /* don't keep the persistent stepped trackview if the mouse moves */
1495 current_stepping_trackview = 0;
1496 step_timeout.disconnect ();
1499 if (session && session->actively_recording()) {
1500 /* Sorry. no dragging stuff around while we record */
1504 drag_info.item_type = item_type;
1505 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1506 &drag_info.current_pointer_y);
1508 if (!from_autoscroll && drag_info.item) {
1509 /* item != 0 is the best test i can think of for dragging.
1511 if (!drag_info.move_threshold_passed) {
1513 drag_info.move_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1515 // and change the initial grab loc/frame if this drag info wants us to
1517 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1518 drag_info.grab_frame = drag_info.current_pointer_frame;
1519 drag_info.grab_x = drag_info.current_pointer_x;
1520 drag_info.grab_y = drag_info.current_pointer_y;
1521 drag_info.last_pointer_frame = drag_info.grab_frame;
1522 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1527 switch (item_type) {
1528 case PlayheadCursorItem:
1529 case EditCursorItem:
1531 case GainControlPointItem:
1532 case RedirectAutomationControlPointItem:
1533 case GainAutomationControlPointItem:
1534 case PanAutomationControlPointItem:
1535 case TempoMarkerItem:
1536 case MeterMarkerItem:
1537 case RegionViewNameHighlight:
1538 case StartSelectionTrimItem:
1539 case EndSelectionTrimItem:
1542 case RedirectAutomationLineItem:
1543 case GainAutomationLineItem:
1544 case PanAutomationLineItem:
1545 case FadeInHandleItem:
1546 case FadeOutHandleItem:
1547 /* <CMT Additions> */
1548 case ImageFrameHandleStartItem:
1549 case ImageFrameHandleEndItem:
1550 case MarkerViewHandleStartItem:
1551 case MarkerViewHandleEndItem:
1552 /* </CMT Additions> */
1553 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1554 (event->motion.state & Gdk::BUTTON2_MASK))) {
1555 if (!from_autoscroll) {
1556 maybe_autoscroll (event);
1558 (this->*(drag_info.motion_callback)) (item, event);
1567 switch (mouse_mode) {
1572 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1573 (event->motion.state & GDK_BUTTON2_MASK))) {
1574 if (!from_autoscroll) {
1575 maybe_autoscroll (event);
1577 (this->*(drag_info.motion_callback)) (item, event);
1588 track_canvas_motion (event);
1589 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1597 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1599 if (drag_info.item == 0) {
1600 fatal << _("programming error: start_grab called without drag item") << endmsg;
1606 cursor = grabber_cursor;
1609 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1611 if (event->button.button == 2) {
1612 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1613 drag_info.y_constrained = true;
1614 drag_info.x_constrained = false;
1616 drag_info.y_constrained = false;
1617 drag_info.x_constrained = true;
1620 drag_info.x_constrained = false;
1621 drag_info.y_constrained = false;
1624 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1625 drag_info.last_pointer_frame = drag_info.grab_frame;
1626 drag_info.current_pointer_frame = drag_info.grab_frame;
1627 drag_info.current_pointer_x = drag_info.grab_x;
1628 drag_info.current_pointer_y = drag_info.grab_y;
1629 drag_info.cumulative_x_drag = 0;
1630 drag_info.cumulative_y_drag = 0;
1631 drag_info.first_move = true;
1632 drag_info.move_threshold_passed = false;
1633 drag_info.want_move_threshold = false;
1634 drag_info.pointer_frame_offset = 0;
1635 drag_info.brushing = false;
1636 drag_info.copied_location = 0;
1638 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1640 event->button.time);
1642 if (session && session->transport_rolling()) {
1643 drag_info.was_rolling = true;
1645 drag_info.was_rolling = false;
1648 switch (snap_type) {
1649 case SnapToRegionStart:
1650 case SnapToRegionEnd:
1651 case SnapToRegionSync:
1652 case SnapToRegionBoundary:
1653 build_region_boundary_cache ();
1661 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1663 drag_info.item->ungrab (0);
1664 drag_info.item = new_item;
1667 cursor = grabber_cursor;
1670 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1674 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1676 bool did_drag = false;
1678 stop_canvas_autoscroll ();
1680 if (drag_info.item == 0) {
1684 drag_info.item->ungrab (event->button.time);
1686 if (drag_info.finished_callback) {
1687 (this->*(drag_info.finished_callback)) (item, event);
1690 did_drag = !drag_info.first_move;
1692 hide_verbose_canvas_cursor();
1695 drag_info.copy = false;
1696 drag_info.motion_callback = 0;
1697 drag_info.finished_callback = 0;
1698 drag_info.last_trackview = 0;
1699 drag_info.last_frame_position = 0;
1700 drag_info.grab_frame = 0;
1701 drag_info.last_pointer_frame = 0;
1702 drag_info.current_pointer_frame = 0;
1703 drag_info.brushing = false;
1705 if (drag_info.copied_location) {
1706 delete drag_info.copied_location;
1707 drag_info.copied_location = 0;
1714 Editor::set_edit_cursor (GdkEvent* event)
1716 nframes_t pointer_frame = event_frame (event);
1718 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1719 if (snap_type != SnapToEditCursor) {
1720 snap_to (pointer_frame);
1724 edit_cursor->set_position (pointer_frame);
1725 edit_cursor_clock.set (pointer_frame);
1729 Editor::set_playhead_cursor (GdkEvent* event)
1731 nframes_t pointer_frame = event_frame (event);
1733 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1734 snap_to (pointer_frame);
1738 session->request_locate (pointer_frame, session->transport_rolling());
1743 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1745 drag_info.item = item;
1746 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1747 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1751 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1752 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1756 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1758 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1762 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1764 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1766 nframes_t fade_length;
1768 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1769 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1775 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1779 if (pos < (arv->region()->position() + 64)) {
1780 fade_length = 64; // this should be a minimum defined somewhere
1781 } else if (pos > arv->region()->last_frame()) {
1782 fade_length = arv->region()->length();
1784 fade_length = pos - arv->region()->position();
1787 arv->reset_fade_in_shape_width (fade_length);
1789 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1791 drag_info.first_move = false;
1795 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1797 if (drag_info.first_move) return;
1799 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1801 nframes_t fade_length;
1803 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1804 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1810 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1814 if (pos < (arv->region()->position() + 64)) {
1815 fade_length = 64; // this should be a minimum defined somewhere
1817 else if (pos > arv->region()->last_frame()) {
1818 fade_length = arv->region()->length();
1821 fade_length = pos - arv->region()->position();
1824 begin_reversible_command (_("change fade in length"));
1825 AutomationList& alist = arv->audio_region()->fade_in();
1826 XMLNode &before = alist.get_state();
1828 arv->audio_region()->set_fade_in_length (fade_length);
1830 XMLNode &after = alist.get_state();
1831 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1832 commit_reversible_command ();
1833 fade_in_drag_motion_callback (item, event);
1837 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1839 drag_info.item = item;
1840 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1841 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1845 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1846 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1850 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1852 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1856 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1858 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1860 nframes_t fade_length;
1862 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1863 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1869 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1873 if (pos > (arv->region()->last_frame() - 64)) {
1874 fade_length = 64; // this should really be a minimum fade defined somewhere
1876 else if (pos < arv->region()->position()) {
1877 fade_length = arv->region()->length();
1880 fade_length = arv->region()->last_frame() - pos;
1883 arv->reset_fade_out_shape_width (fade_length);
1885 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1887 drag_info.first_move = false;
1891 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1893 if (drag_info.first_move) return;
1895 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1897 nframes_t fade_length;
1899 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1900 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1906 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1910 if (pos > (arv->region()->last_frame() - 64)) {
1911 fade_length = 64; // this should really be a minimum fade defined somewhere
1913 else if (pos < arv->region()->position()) {
1914 fade_length = arv->region()->length();
1917 fade_length = arv->region()->last_frame() - pos;
1920 begin_reversible_command (_("change fade out length"));
1921 AutomationList& alist = arv->audio_region()->fade_out();
1922 XMLNode &before = alist.get_state();
1924 arv->audio_region()->set_fade_out_length (fade_length);
1926 XMLNode &after = alist.get_state();
1927 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1928 commit_reversible_command ();
1930 fade_out_drag_motion_callback (item, event);
1934 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1936 drag_info.item = item;
1937 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1938 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1942 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1943 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1947 Cursor* cursor = (Cursor *) drag_info.data;
1949 if (cursor == playhead_cursor) {
1950 _dragging_playhead = true;
1952 if (session && drag_info.was_rolling) {
1953 session->request_stop ();
1957 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1959 show_verbose_time_cursor (cursor->current_frame, 10);
1963 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1965 Cursor* cursor = (Cursor *) drag_info.data;
1966 nframes_t adjusted_frame;
1968 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1969 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1975 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1976 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1977 snap_to (adjusted_frame);
1981 if (adjusted_frame == drag_info.last_pointer_frame) return;
1983 cursor->set_position (adjusted_frame);
1985 if (cursor == edit_cursor) {
1986 edit_cursor_clock.set (cursor->current_frame);
1988 UpdateAllTransportClocks (cursor->current_frame);
1991 show_verbose_time_cursor (cursor->current_frame, 10);
1993 drag_info.last_pointer_frame = adjusted_frame;
1994 drag_info.first_move = false;
1998 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2000 if (drag_info.first_move) return;
2002 cursor_drag_motion_callback (item, event);
2004 _dragging_playhead = false;
2006 if (item == &playhead_cursor->canvas_item) {
2008 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2010 } else if (item == &edit_cursor->canvas_item) {
2011 edit_cursor->set_position (edit_cursor->current_frame);
2012 edit_cursor_clock.set (edit_cursor->current_frame);
2017 Editor::update_marker_drag_item (Location *location)
2019 double x1 = frame_to_pixel (location->start());
2020 double x2 = frame_to_pixel (location->end());
2022 if (location->is_mark()) {
2023 marker_drag_line_points.front().set_x(x1);
2024 marker_drag_line_points.back().set_x(x1);
2025 marker_drag_line->property_points() = marker_drag_line_points;
2028 range_marker_drag_rect->property_x1() = x1;
2029 range_marker_drag_rect->property_x2() = x2;
2034 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2038 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2039 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2045 Location *location = find_location_from_marker (marker, is_start);
2047 drag_info.item = item;
2048 drag_info.data = marker;
2049 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2050 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2054 drag_info.copied_location = new Location (*location);
2055 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2057 update_marker_drag_item (location);
2059 if (location->is_mark()) {
2060 marker_drag_line->show();
2061 marker_drag_line->raise_to_top();
2064 range_marker_drag_rect->show();
2065 range_marker_drag_rect->raise_to_top();
2068 if (is_start) show_verbose_time_cursor (location->start(), 10);
2069 else show_verbose_time_cursor (location->end(), 10);
2073 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2076 Marker* marker = (Marker *) drag_info.data;
2077 Location *real_location;
2078 Location *copy_location;
2080 bool move_both = false;
2084 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2085 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2091 nframes_t next = newframe;
2093 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2094 snap_to (newframe, 0, true);
2097 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2101 /* call this to find out if its the start or end */
2103 real_location = find_location_from_marker (marker, is_start);
2105 /* use the copy that we're "dragging" around */
2107 copy_location = drag_info.copied_location;
2109 f_delta = copy_location->end() - copy_location->start();
2111 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2115 if (copy_location->is_mark()) {
2118 copy_location->set_start (newframe);
2122 if (is_start) { // start-of-range marker
2125 copy_location->set_start (newframe);
2126 copy_location->set_end (newframe + f_delta);
2127 } else if (newframe < copy_location->end()) {
2128 copy_location->set_start (newframe);
2130 snap_to (next, 1, true);
2131 copy_location->set_end (next);
2132 copy_location->set_start (newframe);
2135 } else { // end marker
2138 copy_location->set_end (newframe);
2139 copy_location->set_start (newframe - f_delta);
2140 } else if (newframe > copy_location->start()) {
2141 copy_location->set_end (newframe);
2143 } else if (newframe > 0) {
2144 snap_to (next, -1, true);
2145 copy_location->set_start (next);
2146 copy_location->set_end (newframe);
2151 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2152 drag_info.first_move = false;
2154 update_marker_drag_item (copy_location);
2156 LocationMarkers* lm = find_location_markers (real_location);
2157 lm->set_position (copy_location->start(), copy_location->end());
2159 show_verbose_time_cursor (newframe, 10);
2163 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2165 if (drag_info.first_move) {
2166 marker_drag_motion_callback (item, event);
2170 Marker* marker = (Marker *) drag_info.data;
2174 begin_reversible_command ( _("move marker") );
2175 XMLNode &before = session->locations()->get_state();
2177 Location * location = find_location_from_marker (marker, is_start);
2180 if (location->is_mark()) {
2181 location->set_start (drag_info.copied_location->start());
2183 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2187 XMLNode &after = session->locations()->get_state();
2188 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2189 commit_reversible_command ();
2191 marker_drag_line->hide();
2192 range_marker_drag_rect->hide();
2196 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2199 MeterMarker* meter_marker;
2201 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2202 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2206 meter_marker = dynamic_cast<MeterMarker*> (marker);
2208 MetricSection& section (meter_marker->meter());
2210 if (!section.movable()) {
2214 drag_info.item = item;
2215 drag_info.data = marker;
2216 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2217 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2221 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2223 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2227 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2230 MeterMarker* meter_marker;
2232 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2233 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2237 meter_marker = dynamic_cast<MeterMarker*> (marker);
2239 // create a dummy marker for visual representation of moving the copy.
2240 // The actual copying is not done before we reach the finish callback.
2242 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2243 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2244 *new MeterSection(meter_marker->meter()));
2246 drag_info.item = &new_marker->the_item();
2247 drag_info.copy = true;
2248 drag_info.data = new_marker;
2249 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2250 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2254 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2256 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2260 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2262 MeterMarker* marker = (MeterMarker *) drag_info.data;
2263 nframes_t adjusted_frame;
2265 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2266 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2272 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2273 snap_to (adjusted_frame);
2276 if (adjusted_frame == drag_info.last_pointer_frame) return;
2278 marker->set_position (adjusted_frame);
2281 drag_info.last_pointer_frame = adjusted_frame;
2282 drag_info.first_move = false;
2284 show_verbose_time_cursor (adjusted_frame, 10);
2288 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2290 if (drag_info.first_move) return;
2292 meter_marker_drag_motion_callback (drag_info.item, event);
2294 MeterMarker* marker = (MeterMarker *) drag_info.data;
2297 TempoMap& map (session->tempo_map());
2298 map.bbt_time (drag_info.last_pointer_frame, when);
2300 if (drag_info.copy == true) {
2301 begin_reversible_command (_("copy meter mark"));
2302 XMLNode &before = map.get_state();
2303 map.add_meter (marker->meter(), when);
2304 XMLNode &after = map.get_state();
2305 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2306 commit_reversible_command ();
2308 // delete the dummy marker we used for visual representation of copying.
2309 // a new visual marker will show up automatically.
2312 begin_reversible_command (_("move meter mark"));
2313 XMLNode &before = map.get_state();
2314 map.move_meter (marker->meter(), when);
2315 XMLNode &after = map.get_state();
2316 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2317 commit_reversible_command ();
2322 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2325 TempoMarker* tempo_marker;
2327 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2328 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2332 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2333 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2337 MetricSection& section (tempo_marker->tempo());
2339 if (!section.movable()) {
2343 drag_info.item = item;
2344 drag_info.data = marker;
2345 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2346 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2350 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2351 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2355 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2358 TempoMarker* tempo_marker;
2360 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2361 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2365 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2366 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2370 // create a dummy marker for visual representation of moving the copy.
2371 // The actual copying is not done before we reach the finish callback.
2373 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2374 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2375 *new TempoSection(tempo_marker->tempo()));
2377 drag_info.item = &new_marker->the_item();
2378 drag_info.copy = true;
2379 drag_info.data = new_marker;
2380 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2381 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2385 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2387 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2391 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2393 TempoMarker* marker = (TempoMarker *) drag_info.data;
2394 nframes_t adjusted_frame;
2396 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2397 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2403 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2404 snap_to (adjusted_frame);
2407 if (adjusted_frame == drag_info.last_pointer_frame) return;
2409 /* OK, we've moved far enough to make it worth actually move the thing. */
2411 marker->set_position (adjusted_frame);
2413 show_verbose_time_cursor (adjusted_frame, 10);
2415 drag_info.last_pointer_frame = adjusted_frame;
2416 drag_info.first_move = false;
2420 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2422 if (drag_info.first_move) return;
2424 tempo_marker_drag_motion_callback (drag_info.item, event);
2426 TempoMarker* marker = (TempoMarker *) drag_info.data;
2429 TempoMap& map (session->tempo_map());
2430 map.bbt_time (drag_info.last_pointer_frame, when);
2432 if (drag_info.copy == true) {
2433 begin_reversible_command (_("copy tempo mark"));
2434 XMLNode &before = map.get_state();
2435 map.add_tempo (marker->tempo(), when);
2436 XMLNode &after = map.get_state();
2437 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2438 commit_reversible_command ();
2440 // delete the dummy marker we used for visual representation of copying.
2441 // a new visual marker will show up automatically.
2444 begin_reversible_command (_("move tempo mark"));
2445 XMLNode &before = map.get_state();
2446 map.move_tempo (marker->tempo(), when);
2447 XMLNode &after = map.get_state();
2448 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2449 commit_reversible_command ();
2454 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2456 ControlPoint* control_point;
2458 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2459 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2463 // We shouldn't remove the first or last gain point
2464 if (control_point->line.is_last_point(*control_point) ||
2465 control_point->line.is_first_point(*control_point)) {
2469 control_point->line.remove_point (*control_point);
2473 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2475 ControlPoint* control_point;
2477 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2478 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2482 control_point->line.remove_point (*control_point);
2486 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2488 ControlPoint* control_point;
2490 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2491 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2495 drag_info.item = item;
2496 drag_info.data = control_point;
2497 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2498 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2500 start_grab (event, fader_cursor);
2502 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2504 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2505 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2506 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2508 show_verbose_canvas_cursor ();
2512 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2514 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2516 double cx = drag_info.current_pointer_x;
2517 double cy = drag_info.current_pointer_y;
2519 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2520 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2522 if (drag_info.x_constrained) {
2523 cx = drag_info.grab_x;
2525 if (drag_info.y_constrained) {
2526 cy = drag_info.grab_y;
2529 cp->line.parent_group().w2i (cx, cy);
2533 cy = min ((double) cp->line.height(), cy);
2535 //translate cx to frames
2536 nframes_t cx_frames = unit_to_frame (cx);
2538 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2539 snap_to (cx_frames);
2542 float fraction = 1.0 - (cy / cp->line.height());
2546 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2552 cp->line.point_drag (*cp, cx_frames , fraction, push);
2554 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2556 drag_info.first_move = false;
2560 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2562 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2564 if (drag_info.first_move) {
2568 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2569 reset_point_selection ();
2573 control_point_drag_motion_callback (item, event);
2575 cp->line.end_drag (cp);
2579 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2581 switch (mouse_mode) {
2583 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2584 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2592 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2596 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2597 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2601 start_line_grab (al, event);
2605 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2609 nframes_t frame_within_region;
2611 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2615 cx = event->button.x;
2616 cy = event->button.y;
2617 line->parent_group().w2i (cx, cy);
2618 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2620 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2621 current_line_drag_info.after)) {
2622 /* no adjacent points */
2626 drag_info.item = &line->grab_item();
2627 drag_info.data = line;
2628 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2629 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2631 start_grab (event, fader_cursor);
2633 double fraction = 1.0 - (cy / line->height());
2635 line->start_drag (0, drag_info.grab_frame, fraction);
2637 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2638 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2639 show_verbose_canvas_cursor ();
2643 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2645 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2646 double cx = drag_info.current_pointer_x;
2647 double cy = drag_info.current_pointer_y;
2649 line->parent_group().w2i (cx, cy);
2652 fraction = 1.0 - (cy / line->height());
2656 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2662 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2664 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2668 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2670 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2671 line_drag_motion_callback (item, event);
2676 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2678 if (selection->regions.empty() || clicked_regionview == 0) {
2682 drag_info.copy = false;
2683 drag_info.item = item;
2684 drag_info.data = clicked_regionview;
2685 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2686 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2691 TimeAxisView* tvp = clicked_axisview;
2692 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2694 if (tv && tv->is_track()) {
2695 speed = tv->get_diskstream()->speed();
2698 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2699 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2700 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2701 // we want a move threshold
2702 drag_info.want_move_threshold = true;
2704 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2706 begin_reversible_command (_("move region(s)"));
2710 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2712 cerr << "start region copy grab, selected regions = " << selection->regions.size() << endl;
2714 if (selection->regions.empty() || clicked_regionview == 0) {
2718 drag_info.copy = true;
2719 drag_info.item = item;
2720 drag_info.data = clicked_regionview;
2724 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2725 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2728 if (rtv && rtv->is_track()) {
2729 speed = rtv->get_diskstream()->speed();
2732 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2733 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2734 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2735 // we want a move threshold
2736 drag_info.want_move_threshold = true;
2737 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2738 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2739 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2743 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2745 if (selection->regions.empty() || clicked_regionview == 0) {
2749 drag_info.copy = false;
2750 drag_info.item = item;
2751 drag_info.data = clicked_regionview;
2752 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2753 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2758 TimeAxisView* tvp = clicked_axisview;
2759 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2761 if (tv && tv->is_track()) {
2762 speed = tv->get_diskstream()->speed();
2765 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2766 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2767 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2768 // we want a move threshold
2769 drag_info.want_move_threshold = true;
2770 drag_info.brushing = true;
2772 begin_reversible_command (_("Drag region brush"));
2776 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2780 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2781 nframes_t pending_region_position = 0;
2782 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2783 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2784 bool clamp_y_axis = false;
2785 vector<int32_t> height_list(512) ;
2786 vector<int32_t>::iterator j;
2788 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2790 drag_info.want_move_threshold = false; // don't copy again
2792 /* this is committed in the grab finished callback. */
2794 begin_reversible_command (_("Drag region copy"));
2796 /* duplicate the region(s) */
2798 vector<RegionView*> new_regionviews;
2800 set<boost::shared_ptr<Playlist> > affected_playlists;
2801 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2803 // TODO: Crossfades need to be copied!
2804 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2809 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
2810 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2812 insert_result = affected_playlists.insert (to_playlist);
2813 if (insert_result.second) {
2814 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
2817 latest_regionview = 0;
2819 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2821 /* create a new region with the same name. */
2823 boost::shared_ptr<Region> newregion;
2825 newregion = RegionFactory::create (rv->region());
2826 assert(newregion != 0);
2828 /* if the original region was locked, we don't care */
2830 newregion->set_locked (false);
2832 to_playlist->add_region (newregion, (nframes_t) (rv->region()->position() * rtv->get_diskstream()->speed()));
2836 if (latest_regionview) {
2837 new_regionviews.push_back (latest_regionview);
2843 if (new_regionviews.empty()) {
2847 /* reset selection to new regionviews */
2849 selection->set (new_regionviews);
2851 /* reset drag_info data to reflect the fact that we are dragging the copies */
2853 drag_info.data = new_regionviews.front();
2854 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2857 /* Which trackview is this ? */
2859 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2860 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2862 /* The region motion is only processed if the pointer is over
2866 if (!tv || !tv->is_track()) {
2867 /* To make sure we hide the verbose canvas cursor when the mouse is
2868 not held over a track.
2870 hide_verbose_canvas_cursor ();
2874 original_pointer_order = drag_info.last_trackview->order;
2876 /************************************************************
2878 ************************************************************/
2880 if (drag_info.brushing) {
2881 clamp_y_axis = true;
2886 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2888 int32_t children = 0, numtracks = 0;
2889 // XXX hard coding track limit, oh my, so very very bad
2890 bitset <1024> tracks (0x00);
2891 /* get a bitmask representing the visible tracks */
2893 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2894 TimeAxisView *tracklist_timeview;
2895 tracklist_timeview = (*i);
2896 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2897 list<TimeAxisView*> children_list;
2899 /* zeroes are audio tracks. ones are other types. */
2901 if (!rtv2->hidden()) {
2903 if (visible_y_high < rtv2->order) {
2904 visible_y_high = rtv2->order;
2906 if (visible_y_low > rtv2->order) {
2907 visible_y_low = rtv2->order;
2910 if (!rtv2->is_track()) {
2911 tracks = tracks |= (0x01 << rtv2->order);
2914 height_list[rtv2->order] = (*i)->height;
2916 if ((children_list = rtv2->get_child_list()).size() > 0) {
2917 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2918 tracks = tracks |= (0x01 << (rtv2->order + children));
2919 height_list[rtv2->order + children] = (*j)->height;
2927 /* find the actual span according to the canvas */
2929 canvas_pointer_y_span = pointer_y_span;
2930 if (drag_info.last_trackview->order >= tv->order) {
2932 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2933 if (height_list[y] == 0 ) {
2934 canvas_pointer_y_span--;
2939 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2940 if ( height_list[y] == 0 ) {
2941 canvas_pointer_y_span++;
2946 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2947 RegionView* rv2 = (*i);
2948 double ix1, ix2, iy1, iy2;
2951 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2952 rv2->get_canvas_group()->i2w (ix1, iy1);
2953 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2954 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2956 if (rtv2->order != original_pointer_order) {
2957 /* this isn't the pointer track */
2959 if (canvas_pointer_y_span > 0) {
2961 /* moving up the canvas */
2962 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
2964 int32_t visible_tracks = 0;
2965 while (visible_tracks < canvas_pointer_y_span ) {
2968 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2969 /* we're passing through a hidden track */
2974 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2975 clamp_y_axis = true;
2979 clamp_y_axis = true;
2982 } else if (canvas_pointer_y_span < 0) {
2984 /*moving down the canvas*/
2986 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2989 int32_t visible_tracks = 0;
2991 while (visible_tracks > canvas_pointer_y_span ) {
2994 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2998 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2999 clamp_y_axis = true;
3004 clamp_y_axis = true;
3010 /* this is the pointer's track */
3011 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3012 clamp_y_axis = true;
3013 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3014 clamp_y_axis = true;
3022 } else if (drag_info.last_trackview == tv) {
3023 clamp_y_axis = true;
3027 if (!clamp_y_axis) {
3028 drag_info.last_trackview = tv;
3031 /************************************************************
3033 ************************************************************/
3035 /* compute the amount of pointer motion in frames, and where
3036 the region would be if we moved it by that much.
3039 if (drag_info.move_threshold_passed) {
3041 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3043 nframes_t sync_frame;
3044 nframes_t sync_offset;
3047 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3049 sync_offset = rv->region()->sync_offset (sync_dir);
3050 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3052 /* we snap if the snap modifier is not enabled.
3055 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3056 snap_to (sync_frame);
3059 if (sync_frame - sync_offset <= sync_frame) {
3060 pending_region_position = sync_frame - (sync_dir*sync_offset);
3062 pending_region_position = 0;
3066 pending_region_position = 0;
3069 if (pending_region_position > max_frames - rv->region()->length()) {
3070 pending_region_position = drag_info.last_frame_position;
3073 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3075 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3077 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3078 to make it appear at the new location.
3081 if (pending_region_position > drag_info.last_frame_position) {
3082 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3084 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3087 drag_info.last_frame_position = pending_region_position;
3094 /* threshold not passed */
3099 /*************************************************************
3101 ************************************************************/
3103 if (x_delta == 0 && (pointer_y_span == 0)) {
3104 /* haven't reached next snap point, and we're not switching
3105 trackviews. nothing to do.
3111 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3113 RegionView* rv2 = (*i);
3115 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3117 double ix1, ix2, iy1, iy2;
3118 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3119 rv2->get_canvas_group()->i2w (ix1, iy1);
3128 /*************************************************************
3130 ************************************************************/
3132 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3133 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3135 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3137 RegionView* rv = (*i);
3138 double ix1, ix2, iy1, iy2;
3139 int32_t temp_pointer_y_span = pointer_y_span;
3141 /* get item BBox, which will be relative to parent. so we have
3142 to query on a child, then convert to world coordinates using
3146 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3147 rv->get_canvas_group()->i2w (ix1, iy1);
3148 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3149 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3150 RouteTimeAxisView* temp_rtv;
3152 if ((pointer_y_span != 0) && !clamp_y_axis) {
3155 for (j = height_list.begin(); j!= height_list.end(); j++) {
3156 if (x == canvas_rtv->order) {
3157 /* we found the track the region is on */
3158 if (x != original_pointer_order) {
3159 /*this isn't from the same track we're dragging from */
3160 temp_pointer_y_span = canvas_pointer_y_span;
3162 while (temp_pointer_y_span > 0) {
3163 /* we're moving up canvas-wise,
3164 so we need to find the next track height
3166 if (j != height_list.begin()) {
3169 if (x != original_pointer_order) {
3170 /* we're not from the dragged track, so ignore hidden tracks. */
3172 temp_pointer_y_span++;
3176 temp_pointer_y_span--;
3178 while (temp_pointer_y_span < 0) {
3180 if (x != original_pointer_order) {
3182 temp_pointer_y_span--;
3186 if (j != height_list.end()) {
3189 temp_pointer_y_span++;
3191 /* find out where we'll be when we move and set height accordingly */
3193 tvp2 = trackview_by_y_position (iy1 + y_delta);
3194 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3195 rv->set_height (temp_rtv->height);
3197 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3198 personally, i think this can confuse things, but never mind.
3201 //const GdkColor& col (temp_rtv->view->get_region_color());
3202 //rv->set_color (const_cast<GdkColor&>(col));
3209 /* prevent the regionview from being moved to before
3210 the zero position on the canvas.
3215 if (-x_delta > ix1) {
3218 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3219 x_delta = max_frames - rv->region()->last_frame();
3222 if (drag_info.first_move) {
3224 /* hide any dependent views */
3226 // rv->get_time_axis_view().hide_dependent_views (*rv);
3228 /* this is subtle. raising the regionview itself won't help,
3229 because raise_to_top() just puts the item on the top of
3230 its parent's stack. so, we need to put the trackview canvas_display group
3231 on the top, since its parent is the whole canvas.
3234 rv->get_canvas_group()->raise_to_top();
3235 rv->get_time_axis_view().canvas_display->raise_to_top();
3236 cursor_group->raise_to_top();
3238 /* freeze the playlists from notifying till
3242 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3243 if (rtv && rtv->is_audio_track()) {
3244 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>(rtv->get_diskstream()->playlist());
3246 /* only freeze and capture state once */
3248 insert_result = motion_frozen_playlists.insert (pl);
3249 if (insert_result.second) {
3251 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
3255 rv->region()->set_opaque(false);
3258 if (drag_info.brushing) {
3259 mouse_brush_insert_region (rv, pending_region_position);
3261 rv->move (x_delta, y_delta);
3265 if (drag_info.first_move) {
3266 cursor_group->raise_to_top();
3269 drag_info.first_move = false;
3271 if (x_delta != 0 && !drag_info.brushing) {
3272 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3278 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3281 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3282 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3283 bool nocommit = true;
3285 RouteTimeAxisView* atv;
3286 bool regionview_y_movement;
3287 bool regionview_x_movement;
3289 /* first_move is set to false if the regionview has been moved in the
3293 if (drag_info.first_move) {
3300 /* The regionview has been moved at some stage during the grab so we need
3301 to account for any mouse movement between this event and the last one.
3304 region_drag_motion_callback (item, event);
3306 if (drag_info.brushing) {
3307 /* all changes were made during motion event handlers */
3311 /* adjust for track speed */
3314 atv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3315 if (atv && atv->get_diskstream()) {
3316 speed = atv->get_diskstream()->speed();
3319 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3320 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3322 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3323 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3325 if (regionview_y_movement) {
3327 /* motion between tracks */
3329 list<RegionView*> new_selection;
3331 /* moved to a different audio track. */
3333 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3335 RegionView* rv2 = (*i);
3337 /* the region that used to be in the old playlist is not
3338 moved to the new one - we make a copy of it. as a result,
3339 any existing editor for the region should no longer be
3343 if (!drag_info.copy) {
3344 rv2->hide_region_editor();
3346 new_selection.push_back (rv2);
3350 /* first, freeze the target tracks */
3352 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3354 boost::shared_ptr<Playlist> from_playlist;
3355 boost::shared_ptr<Playlist> to_playlist;
3357 double ix1, ix2, iy1, iy2;
3359 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3360 (*i)->get_canvas_group()->i2w (ix1, iy1);
3361 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3362 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3364 (*i)->region()->set_opaque (true);
3366 from_playlist = (*i)->region()->playlist();
3367 to_playlist = atv2->playlist();
3369 /* the from_playlist was frozen in the "first_move" case
3370 of the motion handler. the insert can fail,
3371 but that doesn't matter. it just means
3372 we already have the playlist in the list.
3375 motion_frozen_playlists.insert (from_playlist);
3377 /* only freeze the to_playlist once */
3379 insert_result = motion_frozen_playlists.insert(to_playlist);
3380 if (insert_result.second) {
3381 to_playlist->freeze();
3382 session->add_command(new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3387 /* now do it again with the actual operations */
3389 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3391 boost::shared_ptr<Playlist> from_playlist;
3392 boost::shared_ptr<Playlist> to_playlist;
3394 double ix1, ix2, iy1, iy2;
3396 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3397 (*i)->get_canvas_group()->i2w (ix1, iy1);
3398 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3399 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3401 from_playlist = (*i)->region()->playlist();
3402 to_playlist = atv2->playlist();
3404 latest_regionview = 0;
3406 where = (nframes_t) (unit_to_frame (ix1) * speed);
3407 boost::shared_ptr<Region> new_region (RegionFactory::create ((*i)->region()));
3409 from_playlist->remove_region (((*i)->region()));
3411 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3412 to_playlist->add_region (new_region, where);
3415 if (latest_regionview) {
3416 selection->add (latest_regionview);
3422 /* motion within a single track */
3424 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3428 if (rv->region()->locked()) {
3432 if (regionview_x_movement) {
3433 double ownspeed = 1.0;
3434 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3436 if (atv && atv->get_diskstream()) {
3437 ownspeed = atv->get_diskstream()->speed();
3440 /* base the new region position on the current position of the regionview.*/
3442 double ix1, ix2, iy1, iy2;
3444 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3445 rv->get_canvas_group()->i2w (ix1, iy1);
3446 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3450 where = rv->region()->position();
3453 rv->get_time_axis_view().reveal_dependent_views (*rv);
3455 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3457 rv->region()->set_position (where, (void *) this);
3458 rv->region()->set_opaque (true);
3463 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3465 session->add_command (new MementoCommand<Playlist>(*((*p).get()), 0, & (*p)->get_state()));
3468 motion_frozen_playlists.clear ();
3471 commit_reversible_command ();
3476 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3478 /* Either add to or set the set the region selection, unless
3479 this is an alignment click (control used)
3482 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3483 TimeAxisView* tv = &rv.get_time_axis_view();
3484 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
3486 if (atv && atv->is_track()) {
3487 speed = atv->get_diskstream()->speed();
3490 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3492 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3494 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3496 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3500 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3506 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3512 nframes_t frame_rate;
3519 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3520 case AudioClock::BBT:
3521 session->bbt_time (frame, bbt);
3522 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3525 case AudioClock::SMPTE:
3526 session->smpte_time (frame, smpte);
3527 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3530 case AudioClock::MinSec:
3531 /* XXX this is copied from show_verbose_duration_cursor() */
3532 frame_rate = session->frame_rate();
3533 hours = frame / (frame_rate * 3600);
3534 frame = frame % (frame_rate * 3600);
3535 mins = frame / (frame_rate * 60);
3536 frame = frame % (frame_rate * 60);
3537 secs = (float) frame / (float) frame_rate;
3538 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3542 snprintf (buf, sizeof(buf), "%u", frame);
3546 if (xpos >= 0 && ypos >=0) {
3547 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3550 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3552 show_verbose_canvas_cursor ();
3556 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3563 nframes_t distance, frame_rate;
3565 Meter meter_at_start(session->tempo_map().meter_at(start));
3571 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3572 case AudioClock::BBT:
3573 session->bbt_time (start, sbbt);
3574 session->bbt_time (end, ebbt);
3577 /* XXX this computation won't work well if the
3578 user makes a selection that spans any meter changes.
3581 ebbt.bars -= sbbt.bars;
3582 if (ebbt.beats >= sbbt.beats) {
3583 ebbt.beats -= sbbt.beats;
3586 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3588 if (ebbt.ticks >= sbbt.ticks) {
3589 ebbt.ticks -= sbbt.ticks;
3592 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3595 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3598 case AudioClock::SMPTE:
3599 session->smpte_duration (end - start, smpte);
3600 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3603 case AudioClock::MinSec:
3604 /* XXX this stuff should be elsewhere.. */
3605 distance = end - start;
3606 frame_rate = session->frame_rate();
3607 hours = distance / (frame_rate * 3600);
3608 distance = distance % (frame_rate * 3600);
3609 mins = distance / (frame_rate * 60);
3610 distance = distance % (frame_rate * 60);
3611 secs = (float) distance / (float) frame_rate;
3612 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3616 snprintf (buf, sizeof(buf), "%u", end - start);
3620 if (xpos >= 0 && ypos >=0) {
3621 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3624 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3626 show_verbose_canvas_cursor ();
3630 Editor::collect_new_region_view (RegionView* rv)
3632 latest_regionview = rv;
3636 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3638 if (clicked_regionview == 0) {
3642 /* lets try to create new Region for the selection */
3644 vector<boost::shared_ptr<AudioRegion> > new_regions;
3645 create_region_from_selection (new_regions);
3647 if (new_regions.empty()) {
3651 /* XXX fix me one day to use all new regions */
3653 boost::shared_ptr<Region> region (new_regions.front());
3655 /* add it to the current stream/playlist.
3657 tricky: the streamview for the track will add a new regionview. we will
3658 catch the signal it sends when it creates the regionview to
3659 set the regionview we want to then drag.
3662 latest_regionview = 0;
3663 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3665 /* A selection grab currently creates two undo/redo operations, one for
3666 creating the new region and another for moving it.
3669 begin_reversible_command (_("selection grab"));
3671 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3673 XMLNode *before = &(playlist->get_state());
3674 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3675 XMLNode *after = &(playlist->get_state());
3676 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3678 commit_reversible_command ();
3682 if (latest_regionview == 0) {
3683 /* something went wrong */
3687 /* we need to deselect all other regionviews, and select this one
3688 i'm ignoring undo stuff, because the region creation will take care of it */
3689 selection->set (latest_regionview);
3691 drag_info.item = latest_regionview->get_canvas_group();
3692 drag_info.data = latest_regionview;
3693 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3694 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3698 drag_info.last_trackview = clicked_axisview;
3699 drag_info.last_frame_position = latest_regionview->region()->position();
3700 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3702 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3706 Editor::cancel_selection ()
3708 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3709 (*i)->hide_selection ();
3711 begin_reversible_command (_("cancel selection"));
3712 selection->clear ();
3713 clicked_selection = 0;
3714 commit_reversible_command ();
3718 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3720 nframes_t start = 0;
3727 drag_info.item = item;
3728 drag_info.motion_callback = &Editor::drag_selection;
3729 drag_info.finished_callback = &Editor::end_selection_op;
3734 case CreateSelection:
3735 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3736 drag_info.copy = true;
3738 drag_info.copy = false;
3740 start_grab (event, selector_cursor);
3743 case SelectionStartTrim:
3744 if (clicked_axisview) {
3745 clicked_axisview->order_selection_trims (item, true);
3747 start_grab (event, trimmer_cursor);
3748 start = selection->time[clicked_selection].start;
3749 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3752 case SelectionEndTrim:
3753 if (clicked_axisview) {
3754 clicked_axisview->order_selection_trims (item, false);
3756 start_grab (event, trimmer_cursor);
3757 end = selection->time[clicked_selection].end;
3758 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3762 start = selection->time[clicked_selection].start;
3764 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3768 if (selection_op == SelectionMove) {
3769 show_verbose_time_cursor(start, 10);
3771 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3776 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3778 nframes_t start = 0;
3781 nframes_t pending_position;
3783 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3784 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3787 pending_position = 0;
3790 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3791 snap_to (pending_position);
3794 /* only alter selection if the current frame is
3795 different from the last frame position (adjusted)
3798 if (pending_position == drag_info.last_pointer_frame) return;
3800 switch (selection_op) {
3801 case CreateSelection:
3803 if (drag_info.first_move) {
3804 snap_to (drag_info.grab_frame);
3807 if (pending_position < drag_info.grab_frame) {
3808 start = pending_position;
3809 end = drag_info.grab_frame;
3811 end = pending_position;
3812 start = drag_info.grab_frame;
3815 /* first drag: Either add to the selection
3816 or create a new selection->
3819 if (drag_info.first_move) {
3821 begin_reversible_command (_("range selection"));
3823 if (drag_info.copy) {
3824 /* adding to the selection */
3825 clicked_selection = selection->add (start, end);
3826 drag_info.copy = false;
3828 /* new selection-> */
3829 clicked_selection = selection->set (clicked_axisview, start, end);
3834 case SelectionStartTrim:
3836 if (drag_info.first_move) {
3837 begin_reversible_command (_("trim selection start"));
3840 start = selection->time[clicked_selection].start;
3841 end = selection->time[clicked_selection].end;
3843 if (pending_position > end) {
3846 start = pending_position;
3850 case SelectionEndTrim:
3852 if (drag_info.first_move) {
3853 begin_reversible_command (_("trim selection end"));
3856 start = selection->time[clicked_selection].start;
3857 end = selection->time[clicked_selection].end;
3859 if (pending_position < start) {
3862 end = pending_position;
3869 if (drag_info.first_move) {
3870 begin_reversible_command (_("move selection"));
3873 start = selection->time[clicked_selection].start;
3874 end = selection->time[clicked_selection].end;
3876 length = end - start;
3878 start = pending_position;
3881 end = start + length;
3886 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3887 start_canvas_autoscroll (1);
3891 selection->replace (clicked_selection, start, end);
3894 drag_info.last_pointer_frame = pending_position;
3895 drag_info.first_move = false;
3897 if (selection_op == SelectionMove) {
3898 show_verbose_time_cursor(start, 10);
3900 show_verbose_time_cursor(pending_position, 10);
3905 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3907 if (!drag_info.first_move) {
3908 drag_selection (item, event);
3909 /* XXX this is not object-oriented programming at all. ick */
3910 if (selection->time.consolidate()) {
3911 selection->TimeChanged ();
3913 commit_reversible_command ();
3915 /* just a click, no pointer movement.*/
3917 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3919 selection->clear_time();
3924 /* XXX what happens if its a music selection? */
3925 session->set_audio_range (selection->time);
3926 stop_canvas_autoscroll ();
3930 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3933 TimeAxisView* tvp = clicked_axisview;
3934 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3936 if (tv && tv->is_track()) {
3937 speed = tv->get_diskstream()->speed();
3940 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3941 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3942 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3944 motion_frozen_playlists.clear();
3946 //drag_info.item = clicked_regionview->get_name_highlight();
3947 drag_info.item = item;
3948 drag_info.motion_callback = &Editor::trim_motion_callback;
3949 drag_info.finished_callback = &Editor::trim_finished_callback;
3951 start_grab (event, trimmer_cursor);
3953 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3954 trim_op = ContentsTrim;
3956 /* These will get overridden for a point trim.*/
3957 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3958 /* closer to start */
3959 trim_op = StartTrim;
3960 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3968 show_verbose_time_cursor(region_start, 10);
3971 show_verbose_time_cursor(region_end, 10);
3974 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3980 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3982 RegionView* rv = clicked_regionview;
3983 nframes_t frame_delta = 0;
3984 bool left_direction;
3985 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3987 /* snap modifier works differently here..
3988 its' current state has to be passed to the
3989 various trim functions in order to work properly
3993 TimeAxisView* tvp = clicked_axisview;
3994 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3995 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3997 if (tv && tv->is_track()) {
3998 speed = tv->get_diskstream()->speed();
4001 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4002 left_direction = true;
4004 left_direction = false;
4008 snap_to (drag_info.current_pointer_frame);
4011 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4015 if (drag_info.first_move) {
4021 trim_type = "Region start trim";
4024 trim_type = "Region end trim";
4027 trim_type = "Region content trim";
4031 begin_reversible_command (trim_type);
4033 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4034 (*i)->region()->set_opaque(false);
4035 (*i)->region()->freeze ();
4037 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4039 arv->temporarily_hide_envelope ();
4041 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4042 insert_result = motion_frozen_playlists.insert (pl);
4043 if (insert_result.second) {
4044 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4049 if (left_direction) {
4050 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4052 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4057 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4060 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4061 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4067 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4070 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4071 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4078 bool swap_direction = false;
4080 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4081 swap_direction = true;
4084 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4085 i != selection->regions.by_layer().end(); ++i)
4087 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4095 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4098 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4101 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4105 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4106 drag_info.first_move = false;
4110 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4112 boost::shared_ptr<Region> region (rv.region());
4114 if (region->locked()) {
4118 nframes_t new_bound;
4121 TimeAxisView* tvp = clicked_axisview;
4122 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4124 if (tv && tv->is_track()) {
4125 speed = tv->get_diskstream()->speed();
4128 if (left_direction) {
4129 if (swap_direction) {
4130 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4132 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4135 if (swap_direction) {
4136 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4138 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4143 snap_to (new_bound);
4145 region->trim_start ((nframes_t) (new_bound * speed), this);
4146 rv.region_changed (StartChanged);
4150 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4152 boost::shared_ptr<Region> region (rv.region());
4154 if (region->locked()) {
4158 nframes_t new_bound;
4161 TimeAxisView* tvp = clicked_axisview;
4162 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4164 if (tv && tv->is_track()) {
4165 speed = tv->get_diskstream()->speed();
4168 if (left_direction) {
4169 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4171 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4175 snap_to (new_bound, (left_direction ? 0 : 1));
4178 region->trim_front ((nframes_t) (new_bound * speed), this);
4180 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4184 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4186 boost::shared_ptr<Region> region (rv.region());
4188 if (region->locked()) {
4192 nframes_t new_bound;
4195 TimeAxisView* tvp = clicked_axisview;
4196 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4198 if (tv && tv->is_track()) {
4199 speed = tv->get_diskstream()->speed();
4202 if (left_direction) {
4203 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4205 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4209 snap_to (new_bound);
4211 region->trim_end ((nframes_t) (new_bound * speed), this);
4212 rv.region_changed (LengthChanged);
4216 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4218 if (!drag_info.first_move) {
4219 trim_motion_callback (item, event);
4221 if (!clicked_regionview->get_selected()) {
4222 thaw_region_after_trim (*clicked_regionview);
4225 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4226 i != selection->regions.by_layer().end(); ++i)
4228 thaw_region_after_trim (**i);
4229 (*i)->region()->set_opaque(true);
4233 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4235 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4238 motion_frozen_playlists.clear ();
4240 commit_reversible_command();
4242 /* no mouse movement */
4248 Editor::point_trim (GdkEvent* event)
4250 RegionView* rv = clicked_regionview;
4251 nframes_t new_bound = drag_info.current_pointer_frame;
4253 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4254 snap_to (new_bound);
4257 /* Choose action dependant on which button was pressed */
4258 switch (event->button.button) {
4260 trim_op = StartTrim;
4261 begin_reversible_command (_("Start point trim"));
4263 if (rv->get_selected()) {
4265 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4266 i != selection->regions.by_layer().end(); ++i)
4268 if (!(*i)->region()->locked()) {
4269 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4270 XMLNode &before = pl->get_state();
4271 (*i)->region()->trim_front (new_bound, this);
4272 XMLNode &after = pl->get_state();
4273 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4279 if (!rv->region()->locked()) {
4280 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4281 XMLNode &before = pl->get_state();
4282 rv->region()->trim_front (new_bound, this);
4283 XMLNode &after = pl->get_state();
4284 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4288 commit_reversible_command();
4293 begin_reversible_command (_("End point trim"));
4295 if (rv->get_selected()) {
4297 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4299 if (!(*i)->region()->locked()) {
4300 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4301 XMLNode &before = pl->get_state();
4302 (*i)->region()->trim_end (new_bound, this);
4303 XMLNode &after = pl->get_state();
4304 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4310 if (!rv->region()->locked()) {
4311 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4312 XMLNode &before = pl->get_state();
4313 rv->region()->trim_end (new_bound, this);
4314 XMLNode &after = pl->get_state();
4315 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4319 commit_reversible_command();
4328 Editor::thaw_region_after_trim (RegionView& rv)
4330 boost::shared_ptr<Region> region (rv.region());
4332 if (region->locked()) {
4336 region->thaw (_("trimmed region"));
4337 XMLNode &after = region->playlist()->get_state();
4338 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4340 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4342 arv->unhide_envelope ();
4346 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4351 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4352 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4356 Location* location = find_location_from_marker (marker, is_start);
4357 location->set_hidden (true, this);
4362 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4368 drag_info.item = item;
4369 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4370 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4372 range_marker_op = op;
4374 if (!temp_location) {
4375 temp_location = new Location;
4379 case CreateRangeMarker:
4380 case CreateTransportMarker:
4382 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4383 drag_info.copy = true;
4385 drag_info.copy = false;
4387 start_grab (event, selector_cursor);
4391 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4396 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4398 nframes_t start = 0;
4400 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4402 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4403 snap_to (drag_info.current_pointer_frame);
4406 /* only alter selection if the current frame is
4407 different from the last frame position.
4410 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4412 switch (range_marker_op) {
4413 case CreateRangeMarker:
4414 case CreateTransportMarker:
4415 if (drag_info.first_move) {
4416 snap_to (drag_info.grab_frame);
4419 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4420 start = drag_info.current_pointer_frame;
4421 end = drag_info.grab_frame;
4423 end = drag_info.current_pointer_frame;
4424 start = drag_info.grab_frame;
4427 /* first drag: Either add to the selection
4428 or create a new selection.
4431 if (drag_info.first_move) {
4433 temp_location->set (start, end);
4437 update_marker_drag_item (temp_location);
4438 range_marker_drag_rect->show();
4439 range_marker_drag_rect->raise_to_top();
4445 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4446 start_canvas_autoscroll (1);
4450 temp_location->set (start, end);
4452 double x1 = frame_to_pixel (start);
4453 double x2 = frame_to_pixel (end);
4454 crect->property_x1() = x1;
4455 crect->property_x2() = x2;
4457 update_marker_drag_item (temp_location);
4460 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4461 drag_info.first_move = false;
4463 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4468 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4470 Location * newloc = 0;
4473 if (!drag_info.first_move) {
4474 drag_range_markerbar_op (item, event);
4476 switch (range_marker_op) {
4477 case CreateRangeMarker:
4479 begin_reversible_command (_("new range marker"));
4480 XMLNode &before = session->locations()->get_state();
4481 session->locations()->next_available_name(rangename,"unnamed");
4482 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4483 session->locations()->add (newloc, true);
4484 XMLNode &after = session->locations()->get_state();
4485 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4486 commit_reversible_command ();
4488 range_bar_drag_rect->hide();
4489 range_marker_drag_rect->hide();
4493 case CreateTransportMarker:
4494 // popup menu to pick loop or punch
4495 new_transport_marker_context_menu (&event->button, item);
4500 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4502 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4507 start = session->locations()->first_mark_before (drag_info.grab_frame);
4508 end = session->locations()->first_mark_after (drag_info.grab_frame);
4510 if (end == max_frames) {
4511 end = session->current_end_frame ();
4515 start = session->current_start_frame ();
4518 switch (mouse_mode) {
4520 /* find the two markers on either side and then make the selection from it */
4521 cerr << "select between " << start << " .. " << end << endl;
4522 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4526 /* find the two markers on either side of the click and make the range out of it */
4527 selection->set (0, start, end);
4536 stop_canvas_autoscroll ();
4542 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4544 drag_info.item = item;
4545 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4546 drag_info.finished_callback = &Editor::end_mouse_zoom;
4548 start_grab (event, zoom_cursor);
4550 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4554 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4559 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4560 snap_to (drag_info.current_pointer_frame);
4562 if (drag_info.first_move) {
4563 snap_to (drag_info.grab_frame);
4567 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4569 /* base start and end on initial click position */
4570 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4571 start = drag_info.current_pointer_frame;
4572 end = drag_info.grab_frame;
4574 end = drag_info.current_pointer_frame;
4575 start = drag_info.grab_frame;
4580 if (drag_info.first_move) {
4582 zoom_rect->raise_to_top();
4585 reposition_zoom_rect(start, end);
4587 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4588 drag_info.first_move = false;
4590 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4595 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4597 if (!drag_info.first_move) {
4598 drag_mouse_zoom (item, event);
4600 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4601 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4603 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4606 temporal_zoom_to_frame (false, drag_info.grab_frame);
4608 temporal_zoom_step (false);
4609 center_screen (drag_info.grab_frame);
4617 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4619 double x1 = frame_to_pixel (start);
4620 double x2 = frame_to_pixel (end);
4621 double y2 = full_canvas_height - 1.0;
4623 zoom_rect->property_x1() = x1;
4624 zoom_rect->property_y1() = 1.0;
4625 zoom_rect->property_x2() = x2;
4626 zoom_rect->property_y2() = y2;
4630 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4632 drag_info.item = item;
4633 drag_info.motion_callback = &Editor::drag_rubberband_select;
4634 drag_info.finished_callback = &Editor::end_rubberband_select;
4636 start_grab (event, cross_hair_cursor);
4638 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4642 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4649 /* use a bigger drag threshold than the default */
4651 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4655 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4656 // snap_to (drag_info.current_pointer_frame);
4658 // if (drag_info.first_move) {
4659 // snap_to (drag_info.grab_frame);
4664 /* base start and end on initial click position */
4665 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4666 start = drag_info.current_pointer_frame;
4667 end = drag_info.grab_frame;
4669 end = drag_info.current_pointer_frame;
4670 start = drag_info.grab_frame;
4673 if (drag_info.current_pointer_y < drag_info.grab_y) {
4674 y1 = drag_info.current_pointer_y;
4675 y2 = drag_info.grab_y;
4678 y2 = drag_info.current_pointer_y;
4679 y1 = drag_info.grab_y;
4683 if (start != end || y1 != y2) {
4685 double x1 = frame_to_pixel (start);
4686 double x2 = frame_to_pixel (end);
4688 rubberband_rect->property_x1() = x1;
4689 rubberband_rect->property_y1() = y1;
4690 rubberband_rect->property_x2() = x2;
4691 rubberband_rect->property_y2() = y2;
4693 rubberband_rect->show();
4694 rubberband_rect->raise_to_top();
4696 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4697 drag_info.first_move = false;
4699 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4704 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4706 if (!drag_info.first_move) {
4708 drag_rubberband_select (item, event);
4711 if (drag_info.current_pointer_y < drag_info.grab_y) {
4712 y1 = drag_info.current_pointer_y;
4713 y2 = drag_info.grab_y;
4716 y2 = drag_info.current_pointer_y;
4717 y1 = drag_info.grab_y;
4721 Selection::Operation op = Keyboard::selection_type (event->button.state);
4724 begin_reversible_command (_("select regions"));
4726 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4727 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4729 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4733 commit_reversible_command ();
4737 selection->clear_regions();
4738 selection->clear_points ();
4739 selection->clear_lines ();
4742 rubberband_rect->hide();
4747 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4749 using namespace Gtkmm2ext;
4751 ArdourPrompter prompter (false);
4753 prompter.set_prompt (_("Name for region:"));
4754 prompter.set_initial_text (clicked_regionview->region()->name());
4755 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4756 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4757 prompter.show_all ();
4758 switch (prompter.run ()) {
4759 case Gtk::RESPONSE_ACCEPT:
4761 prompter.get_result(str);
4763 clicked_regionview->region()->set_name (str);
4771 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4773 drag_info.item = item;
4774 drag_info.motion_callback = &Editor::time_fx_motion;
4775 drag_info.finished_callback = &Editor::end_time_fx;
4779 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4783 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4785 RegionView* rv = clicked_regionview;
4787 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4788 snap_to (drag_info.current_pointer_frame);
4791 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4795 if (drag_info.current_pointer_frame > rv->region()->position()) {
4796 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4799 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4800 drag_info.first_move = false;
4802 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4806 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4808 clicked_regionview->get_time_axis_view().hide_timestretch ();
4810 if (drag_info.first_move) {
4814 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4815 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4817 begin_reversible_command (_("timestretch"));
4819 if (run_timestretch (selection->regions, percentage) == 0) {
4820 session->commit_reversible_command ();
4825 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4827 /* no brushing without a useful snap setting */
4830 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4833 switch (snap_mode) {
4835 return; /* can't work because it allows region to be placed anywhere */
4840 switch (snap_type) {
4843 case SnapToEditCursor:
4850 /* don't brush a copy over the original */
4852 if (pos == rv->region()->position()) {
4856 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4858 if (atv == 0 || !atv->is_track()) {
4862 boost::shared_ptr<Playlist> playlist = atv->playlist();
4863 double speed = atv->get_diskstream()->speed();
4865 XMLNode &before = playlist->get_state();
4866 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4867 XMLNode &after = playlist->get_state();
4868 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4870 // playlist is frozen, so we have to update manually
4872 playlist->Modified(); /* EMIT SIGNAL */
4876 Editor::track_height_step_timeout ()
4879 struct timeval delta;
4881 gettimeofday (&now, 0);
4882 timersub (&now, &last_track_height_step_timestamp, &delta);
4884 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4885 current_stepping_trackview = 0;