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 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2808 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
2809 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2811 insert_result = affected_playlists.insert (to_playlist);
2812 if (insert_result.second) {
2813 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
2816 latest_regionview = 0;
2818 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2820 /* create a new region with the same name. */
2822 boost::shared_ptr<Region> newregion;
2824 newregion = RegionFactory::create (rv->region());
2825 assert(newregion != 0);
2827 /* if the original region was locked, we don't care */
2829 newregion->set_locked (false);
2831 to_playlist->add_region (newregion, (nframes_t) (rv->region()->position() * rtv->get_diskstream()->speed()));
2835 if (latest_regionview) {
2836 new_regionviews.push_back (latest_regionview);
2842 if (new_regionviews.empty()) {
2846 /* reset selection to new regionviews */
2848 selection->set (new_regionviews);
2850 /* reset drag_info data to reflect the fact that we are dragging the copies */
2852 drag_info.data = new_regionviews.front();
2853 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2856 /* Which trackview is this ? */
2858 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2859 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2861 /* The region motion is only processed if the pointer is over
2865 if (!tv || !tv->is_track()) {
2866 /* To make sure we hide the verbose canvas cursor when the mouse is
2867 not held over a track.
2869 hide_verbose_canvas_cursor ();
2873 original_pointer_order = drag_info.last_trackview->order;
2875 /************************************************************
2877 ************************************************************/
2879 if (drag_info.brushing) {
2880 clamp_y_axis = true;
2885 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2887 int32_t children = 0, numtracks = 0;
2888 // XXX hard coding track limit, oh my, so very very bad
2889 bitset <1024> tracks (0x00);
2890 /* get a bitmask representing the visible tracks */
2892 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2893 TimeAxisView *tracklist_timeview;
2894 tracklist_timeview = (*i);
2895 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2896 list<TimeAxisView*> children_list;
2898 /* zeroes are audio tracks. ones are other types. */
2900 if (!rtv2->hidden()) {
2902 if (visible_y_high < rtv2->order) {
2903 visible_y_high = rtv2->order;
2905 if (visible_y_low > rtv2->order) {
2906 visible_y_low = rtv2->order;
2909 if (!rtv2->is_track()) {
2910 tracks = tracks |= (0x01 << rtv2->order);
2913 height_list[rtv2->order] = (*i)->height;
2915 if ((children_list = rtv2->get_child_list()).size() > 0) {
2916 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2917 tracks = tracks |= (0x01 << (rtv2->order + children));
2918 height_list[rtv2->order + children] = (*j)->height;
2926 /* find the actual span according to the canvas */
2928 canvas_pointer_y_span = pointer_y_span;
2929 if (drag_info.last_trackview->order >= tv->order) {
2931 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2932 if (height_list[y] == 0 ) {
2933 canvas_pointer_y_span--;
2938 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2939 if ( height_list[y] == 0 ) {
2940 canvas_pointer_y_span++;
2945 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2946 RegionView* rv2 = (*i);
2947 double ix1, ix2, iy1, iy2;
2950 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2951 rv2->get_canvas_group()->i2w (ix1, iy1);
2952 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2953 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2955 if (rtv2->order != original_pointer_order) {
2956 /* this isn't the pointer track */
2958 if (canvas_pointer_y_span > 0) {
2960 /* moving up the canvas */
2961 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
2963 int32_t visible_tracks = 0;
2964 while (visible_tracks < canvas_pointer_y_span ) {
2967 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2968 /* we're passing through a hidden track */
2973 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2974 clamp_y_axis = true;
2978 clamp_y_axis = true;
2981 } else if (canvas_pointer_y_span < 0) {
2983 /*moving down the canvas*/
2985 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2988 int32_t visible_tracks = 0;
2990 while (visible_tracks > canvas_pointer_y_span ) {
2993 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2997 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2998 clamp_y_axis = true;
3003 clamp_y_axis = true;
3009 /* this is the pointer's track */
3010 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3011 clamp_y_axis = true;
3012 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3013 clamp_y_axis = true;
3021 } else if (drag_info.last_trackview == tv) {
3022 clamp_y_axis = true;
3026 if (!clamp_y_axis) {
3027 drag_info.last_trackview = tv;
3030 /************************************************************
3032 ************************************************************/
3034 /* compute the amount of pointer motion in frames, and where
3035 the region would be if we moved it by that much.
3038 if (drag_info.move_threshold_passed) {
3040 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3042 nframes_t sync_frame;
3043 nframes_t sync_offset;
3046 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3048 sync_offset = rv->region()->sync_offset (sync_dir);
3049 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3051 /* we snap if the snap modifier is not enabled.
3054 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3055 snap_to (sync_frame);
3058 if (sync_frame - sync_offset <= sync_frame) {
3059 pending_region_position = sync_frame - (sync_dir*sync_offset);
3061 pending_region_position = 0;
3065 pending_region_position = 0;
3068 if (pending_region_position > max_frames - rv->region()->length()) {
3069 pending_region_position = drag_info.last_frame_position;
3072 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3074 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3076 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3077 to make it appear at the new location.
3080 if (pending_region_position > drag_info.last_frame_position) {
3081 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3083 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3086 drag_info.last_frame_position = pending_region_position;
3093 /* threshold not passed */
3098 /*************************************************************
3100 ************************************************************/
3102 if (x_delta == 0 && (pointer_y_span == 0)) {
3103 /* haven't reached next snap point, and we're not switching
3104 trackviews. nothing to do.
3110 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3112 RegionView* rv2 = (*i);
3114 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3116 double ix1, ix2, iy1, iy2;
3117 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3118 rv2->get_canvas_group()->i2w (ix1, iy1);
3127 /*************************************************************
3129 ************************************************************/
3131 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3132 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3134 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3136 RegionView* rv = (*i);
3137 double ix1, ix2, iy1, iy2;
3138 int32_t temp_pointer_y_span = pointer_y_span;
3140 /* get item BBox, which will be relative to parent. so we have
3141 to query on a child, then convert to world coordinates using
3145 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3146 rv->get_canvas_group()->i2w (ix1, iy1);
3147 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3148 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3149 RouteTimeAxisView* temp_rtv;
3151 if ((pointer_y_span != 0) && !clamp_y_axis) {
3154 for (j = height_list.begin(); j!= height_list.end(); j++) {
3155 if (x == canvas_rtv->order) {
3156 /* we found the track the region is on */
3157 if (x != original_pointer_order) {
3158 /*this isn't from the same track we're dragging from */
3159 temp_pointer_y_span = canvas_pointer_y_span;
3161 while (temp_pointer_y_span > 0) {
3162 /* we're moving up canvas-wise,
3163 so we need to find the next track height
3165 if (j != height_list.begin()) {
3168 if (x != original_pointer_order) {
3169 /* we're not from the dragged track, so ignore hidden tracks. */
3171 temp_pointer_y_span++;
3175 temp_pointer_y_span--;
3177 while (temp_pointer_y_span < 0) {
3179 if (x != original_pointer_order) {
3181 temp_pointer_y_span--;
3185 if (j != height_list.end()) {
3188 temp_pointer_y_span++;
3190 /* find out where we'll be when we move and set height accordingly */
3192 tvp2 = trackview_by_y_position (iy1 + y_delta);
3193 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3194 rv->set_height (temp_rtv->height);
3196 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3197 personally, i think this can confuse things, but never mind.
3200 //const GdkColor& col (temp_rtv->view->get_region_color());
3201 //rv->set_color (const_cast<GdkColor&>(col));
3208 /* prevent the regionview from being moved to before
3209 the zero position on the canvas.
3214 if (-x_delta > ix1) {
3217 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3218 x_delta = max_frames - rv->region()->last_frame();
3221 if (drag_info.first_move) {
3223 /* hide any dependent views */
3225 rv->get_time_axis_view().hide_dependent_views (*rv);
3227 /* this is subtle. raising the regionview itself won't help,
3228 because raise_to_top() just puts the item on the top of
3229 its parent's stack. so, we need to put the trackview canvas_display group
3230 on the top, since its parent is the whole canvas.
3233 rv->get_canvas_group()->raise_to_top();
3234 rv->get_time_axis_view().canvas_display->raise_to_top();
3235 cursor_group->raise_to_top();
3237 /* freeze the playlists from notifying till
3241 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3242 if (rtv && rtv->is_audio_track()) {
3243 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>(rtv->get_diskstream()->playlist());
3245 /* only freeze and capture state once */
3247 insert_result = motion_frozen_playlists.insert (pl);
3248 if (insert_result.second) {
3250 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
3254 rv->region()->set_opaque(false);
3257 if (drag_info.brushing) {
3258 mouse_brush_insert_region (rv, pending_region_position);
3260 rv->move (x_delta, y_delta);
3264 if (drag_info.first_move) {
3265 cursor_group->raise_to_top();
3268 drag_info.first_move = false;
3270 if (x_delta != 0 && !drag_info.brushing) {
3271 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3277 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3280 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3281 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3282 bool nocommit = true;
3284 RouteTimeAxisView* atv;
3285 bool regionview_y_movement;
3286 bool regionview_x_movement;
3288 /* first_move is set to false if the regionview has been moved in the
3292 if (drag_info.first_move) {
3299 /* The regionview has been moved at some stage during the grab so we need
3300 to account for any mouse movement between this event and the last one.
3303 region_drag_motion_callback (item, event);
3305 if (drag_info.brushing) {
3306 /* all changes were made during motion event handlers */
3310 /* adjust for track speed */
3313 atv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3314 if (atv && atv->get_diskstream()) {
3315 speed = atv->get_diskstream()->speed();
3318 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3319 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3321 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3322 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3324 if (regionview_y_movement) {
3326 /* motion between tracks */
3328 list<RegionView*> new_selection;
3330 /* moved to a different audio track. */
3332 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3334 RegionView* rv2 = (*i);
3336 /* the region that used to be in the old playlist is not
3337 moved to the new one - we make a copy of it. as a result,
3338 any existing editor for the region should no longer be
3342 if (!drag_info.copy) {
3343 rv2->hide_region_editor();
3345 new_selection.push_back (rv2);
3349 /* first, freeze the target tracks */
3351 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3353 boost::shared_ptr<Playlist> from_playlist;
3354 boost::shared_ptr<Playlist> to_playlist;
3356 double ix1, ix2, iy1, iy2;
3358 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3359 (*i)->get_canvas_group()->i2w (ix1, iy1);
3360 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3361 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3363 (*i)->region()->set_opaque (true);
3365 from_playlist = (*i)->region()->playlist();
3366 to_playlist = atv2->playlist();
3368 /* the from_playlist was frozen in the "first_move" case
3369 of the motion handler. the insert can fail,
3370 but that doesn't matter. it just means
3371 we already have the playlist in the list.
3374 motion_frozen_playlists.insert (from_playlist);
3376 /* only freeze the to_playlist once */
3378 insert_result = motion_frozen_playlists.insert(to_playlist);
3379 if (insert_result.second) {
3380 to_playlist->freeze();
3381 session->add_command(new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3386 /* now do it again with the actual operations */
3388 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3390 boost::shared_ptr<Playlist> from_playlist;
3391 boost::shared_ptr<Playlist> to_playlist;
3393 double ix1, ix2, iy1, iy2;
3395 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3396 (*i)->get_canvas_group()->i2w (ix1, iy1);
3397 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3398 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3400 from_playlist = (*i)->region()->playlist();
3401 to_playlist = atv2->playlist();
3403 latest_regionview = 0;
3405 where = (nframes_t) (unit_to_frame (ix1) * speed);
3406 boost::shared_ptr<Region> new_region (RegionFactory::create ((*i)->region()));
3408 from_playlist->remove_region (((*i)->region()));
3410 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3411 to_playlist->add_region (new_region, where);
3414 if (latest_regionview) {
3415 selection->add (latest_regionview);
3421 /* motion within a single track */
3423 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3427 if (rv->region()->locked()) {
3431 if (regionview_x_movement) {
3432 double ownspeed = 1.0;
3433 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3435 if (atv && atv->get_diskstream()) {
3436 ownspeed = atv->get_diskstream()->speed();
3439 /* base the new region position on the current position of the regionview.*/
3441 double ix1, ix2, iy1, iy2;
3443 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3444 rv->get_canvas_group()->i2w (ix1, iy1);
3445 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3449 where = rv->region()->position();
3452 rv->get_time_axis_view().reveal_dependent_views (*rv);
3454 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3456 rv->region()->set_position (where, (void *) this);
3457 rv->region()->set_opaque (true);
3462 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3464 session->add_command (new MementoCommand<Playlist>(*((*p).get()), 0, & (*p)->get_state()));
3467 motion_frozen_playlists.clear ();
3470 commit_reversible_command ();
3475 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3477 /* Either add to or set the set the region selection, unless
3478 this is an alignment click (control used)
3481 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3482 TimeAxisView* tv = &rv.get_time_axis_view();
3483 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
3485 if (atv && atv->is_track()) {
3486 speed = atv->get_diskstream()->speed();
3489 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3491 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3493 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3495 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3499 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3505 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3511 nframes_t frame_rate;
3518 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3519 case AudioClock::BBT:
3520 session->bbt_time (frame, bbt);
3521 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3524 case AudioClock::SMPTE:
3525 session->smpte_time (frame, smpte);
3526 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3529 case AudioClock::MinSec:
3530 /* XXX this is copied from show_verbose_duration_cursor() */
3531 frame_rate = session->frame_rate();
3532 hours = frame / (frame_rate * 3600);
3533 frame = frame % (frame_rate * 3600);
3534 mins = frame / (frame_rate * 60);
3535 frame = frame % (frame_rate * 60);
3536 secs = (float) frame / (float) frame_rate;
3537 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3541 snprintf (buf, sizeof(buf), "%u", frame);
3545 if (xpos >= 0 && ypos >=0) {
3546 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3549 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3551 show_verbose_canvas_cursor ();
3555 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3562 nframes_t distance, frame_rate;
3564 Meter meter_at_start(session->tempo_map().meter_at(start));
3570 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3571 case AudioClock::BBT:
3572 session->bbt_time (start, sbbt);
3573 session->bbt_time (end, ebbt);
3576 /* XXX this computation won't work well if the
3577 user makes a selection that spans any meter changes.
3580 ebbt.bars -= sbbt.bars;
3581 if (ebbt.beats >= sbbt.beats) {
3582 ebbt.beats -= sbbt.beats;
3585 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3587 if (ebbt.ticks >= sbbt.ticks) {
3588 ebbt.ticks -= sbbt.ticks;
3591 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3594 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3597 case AudioClock::SMPTE:
3598 session->smpte_duration (end - start, smpte);
3599 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3602 case AudioClock::MinSec:
3603 /* XXX this stuff should be elsewhere.. */
3604 distance = end - start;
3605 frame_rate = session->frame_rate();
3606 hours = distance / (frame_rate * 3600);
3607 distance = distance % (frame_rate * 3600);
3608 mins = distance / (frame_rate * 60);
3609 distance = distance % (frame_rate * 60);
3610 secs = (float) distance / (float) frame_rate;
3611 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3615 snprintf (buf, sizeof(buf), "%u", end - start);
3619 if (xpos >= 0 && ypos >=0) {
3620 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3623 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3625 show_verbose_canvas_cursor ();
3629 Editor::collect_new_region_view (RegionView* rv)
3631 latest_regionview = rv;
3635 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3637 if (clicked_regionview == 0) {
3641 /* lets try to create new Region for the selection */
3643 vector<boost::shared_ptr<AudioRegion> > new_regions;
3644 create_region_from_selection (new_regions);
3646 if (new_regions.empty()) {
3650 /* XXX fix me one day to use all new regions */
3652 boost::shared_ptr<Region> region (new_regions.front());
3654 /* add it to the current stream/playlist.
3656 tricky: the streamview for the track will add a new regionview. we will
3657 catch the signal it sends when it creates the regionview to
3658 set the regionview we want to then drag.
3661 latest_regionview = 0;
3662 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3664 /* A selection grab currently creates two undo/redo operations, one for
3665 creating the new region and another for moving it.
3668 begin_reversible_command (_("selection grab"));
3670 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3672 XMLNode *before = &(playlist->get_state());
3673 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3674 XMLNode *after = &(playlist->get_state());
3675 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3677 commit_reversible_command ();
3681 if (latest_regionview == 0) {
3682 /* something went wrong */
3686 /* we need to deselect all other regionviews, and select this one
3687 i'm ignoring undo stuff, because the region creation will take care of it */
3688 selection->set (latest_regionview);
3690 drag_info.item = latest_regionview->get_canvas_group();
3691 drag_info.data = latest_regionview;
3692 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3693 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3697 drag_info.last_trackview = clicked_axisview;
3698 drag_info.last_frame_position = latest_regionview->region()->position();
3699 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3701 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3705 Editor::cancel_selection ()
3707 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3708 (*i)->hide_selection ();
3710 begin_reversible_command (_("cancel selection"));
3711 selection->clear ();
3712 clicked_selection = 0;
3713 commit_reversible_command ();
3717 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3719 nframes_t start = 0;
3726 drag_info.item = item;
3727 drag_info.motion_callback = &Editor::drag_selection;
3728 drag_info.finished_callback = &Editor::end_selection_op;
3733 case CreateSelection:
3734 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3735 drag_info.copy = true;
3737 drag_info.copy = false;
3739 start_grab (event, selector_cursor);
3742 case SelectionStartTrim:
3743 if (clicked_axisview) {
3744 clicked_axisview->order_selection_trims (item, true);
3746 start_grab (event, trimmer_cursor);
3747 start = selection->time[clicked_selection].start;
3748 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3751 case SelectionEndTrim:
3752 if (clicked_axisview) {
3753 clicked_axisview->order_selection_trims (item, false);
3755 start_grab (event, trimmer_cursor);
3756 end = selection->time[clicked_selection].end;
3757 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3761 start = selection->time[clicked_selection].start;
3763 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3767 if (selection_op == SelectionMove) {
3768 show_verbose_time_cursor(start, 10);
3770 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3775 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3777 nframes_t start = 0;
3780 nframes_t pending_position;
3782 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3783 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3786 pending_position = 0;
3789 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3790 snap_to (pending_position);
3793 /* only alter selection if the current frame is
3794 different from the last frame position (adjusted)
3797 if (pending_position == drag_info.last_pointer_frame) return;
3799 switch (selection_op) {
3800 case CreateSelection:
3802 if (drag_info.first_move) {
3803 snap_to (drag_info.grab_frame);
3806 if (pending_position < drag_info.grab_frame) {
3807 start = pending_position;
3808 end = drag_info.grab_frame;
3810 end = pending_position;
3811 start = drag_info.grab_frame;
3814 /* first drag: Either add to the selection
3815 or create a new selection->
3818 if (drag_info.first_move) {
3820 begin_reversible_command (_("range selection"));
3822 if (drag_info.copy) {
3823 /* adding to the selection */
3824 clicked_selection = selection->add (start, end);
3825 drag_info.copy = false;
3827 /* new selection-> */
3828 clicked_selection = selection->set (clicked_axisview, start, end);
3833 case SelectionStartTrim:
3835 if (drag_info.first_move) {
3836 begin_reversible_command (_("trim selection start"));
3839 start = selection->time[clicked_selection].start;
3840 end = selection->time[clicked_selection].end;
3842 if (pending_position > end) {
3845 start = pending_position;
3849 case SelectionEndTrim:
3851 if (drag_info.first_move) {
3852 begin_reversible_command (_("trim selection end"));
3855 start = selection->time[clicked_selection].start;
3856 end = selection->time[clicked_selection].end;
3858 if (pending_position < start) {
3861 end = pending_position;
3868 if (drag_info.first_move) {
3869 begin_reversible_command (_("move selection"));
3872 start = selection->time[clicked_selection].start;
3873 end = selection->time[clicked_selection].end;
3875 length = end - start;
3877 start = pending_position;
3880 end = start + length;
3885 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3886 start_canvas_autoscroll (1);
3890 selection->replace (clicked_selection, start, end);
3893 drag_info.last_pointer_frame = pending_position;
3894 drag_info.first_move = false;
3896 if (selection_op == SelectionMove) {
3897 show_verbose_time_cursor(start, 10);
3899 show_verbose_time_cursor(pending_position, 10);
3904 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3906 if (!drag_info.first_move) {
3907 drag_selection (item, event);
3908 /* XXX this is not object-oriented programming at all. ick */
3909 if (selection->time.consolidate()) {
3910 selection->TimeChanged ();
3912 commit_reversible_command ();
3914 /* just a click, no pointer movement.*/
3916 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3918 selection->clear_time();
3923 /* XXX what happens if its a music selection? */
3924 session->set_audio_range (selection->time);
3925 stop_canvas_autoscroll ();
3929 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3932 TimeAxisView* tvp = clicked_axisview;
3933 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3935 if (tv && tv->is_track()) {
3936 speed = tv->get_diskstream()->speed();
3939 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3940 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3941 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3943 motion_frozen_playlists.clear();
3945 //drag_info.item = clicked_regionview->get_name_highlight();
3946 drag_info.item = item;
3947 drag_info.motion_callback = &Editor::trim_motion_callback;
3948 drag_info.finished_callback = &Editor::trim_finished_callback;
3950 start_grab (event, trimmer_cursor);
3952 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3953 trim_op = ContentsTrim;
3955 /* These will get overridden for a point trim.*/
3956 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3957 /* closer to start */
3958 trim_op = StartTrim;
3959 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3967 show_verbose_time_cursor(region_start, 10);
3970 show_verbose_time_cursor(region_end, 10);
3973 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3979 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3981 RegionView* rv = clicked_regionview;
3982 nframes_t frame_delta = 0;
3983 bool left_direction;
3984 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3986 /* snap modifier works differently here..
3987 its' current state has to be passed to the
3988 various trim functions in order to work properly
3992 TimeAxisView* tvp = clicked_axisview;
3993 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3994 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3996 if (tv && tv->is_track()) {
3997 speed = tv->get_diskstream()->speed();
4000 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4001 left_direction = true;
4003 left_direction = false;
4007 snap_to (drag_info.current_pointer_frame);
4010 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4014 if (drag_info.first_move) {
4020 trim_type = "Region start trim";
4023 trim_type = "Region end trim";
4026 trim_type = "Region content trim";
4030 begin_reversible_command (trim_type);
4032 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4033 (*i)->region()->set_opaque(false);
4034 (*i)->region()->freeze ();
4036 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4038 arv->temporarily_hide_envelope ();
4040 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4041 insert_result = motion_frozen_playlists.insert (pl);
4042 if (insert_result.second) {
4043 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4048 if (left_direction) {
4049 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4051 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4056 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4059 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4060 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4066 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4069 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4070 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4077 bool swap_direction = false;
4079 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4080 swap_direction = true;
4083 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4084 i != selection->regions.by_layer().end(); ++i)
4086 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4094 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4097 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4100 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4104 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4105 drag_info.first_move = false;
4109 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4111 boost::shared_ptr<Region> region (rv.region());
4113 if (region->locked()) {
4117 nframes_t new_bound;
4120 TimeAxisView* tvp = clicked_axisview;
4121 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4123 if (tv && tv->is_track()) {
4124 speed = tv->get_diskstream()->speed();
4127 if (left_direction) {
4128 if (swap_direction) {
4129 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4131 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4134 if (swap_direction) {
4135 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4137 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4142 snap_to (new_bound);
4144 region->trim_start ((nframes_t) (new_bound * speed), this);
4145 rv.region_changed (StartChanged);
4149 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4151 boost::shared_ptr<Region> region (rv.region());
4153 if (region->locked()) {
4157 nframes_t new_bound;
4160 TimeAxisView* tvp = clicked_axisview;
4161 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4163 if (tv && tv->is_track()) {
4164 speed = tv->get_diskstream()->speed();
4167 if (left_direction) {
4168 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4170 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4174 snap_to (new_bound, (left_direction ? 0 : 1));
4177 region->trim_front ((nframes_t) (new_bound * speed), this);
4179 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4183 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4185 boost::shared_ptr<Region> region (rv.region());
4187 if (region->locked()) {
4191 nframes_t new_bound;
4194 TimeAxisView* tvp = clicked_axisview;
4195 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4197 if (tv && tv->is_track()) {
4198 speed = tv->get_diskstream()->speed();
4201 if (left_direction) {
4202 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4204 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4208 snap_to (new_bound);
4210 region->trim_end ((nframes_t) (new_bound * speed), this);
4211 rv.region_changed (LengthChanged);
4215 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4217 if (!drag_info.first_move) {
4218 trim_motion_callback (item, event);
4220 if (!clicked_regionview->get_selected()) {
4221 thaw_region_after_trim (*clicked_regionview);
4224 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4225 i != selection->regions.by_layer().end(); ++i)
4227 thaw_region_after_trim (**i);
4228 (*i)->region()->set_opaque(true);
4232 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4234 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4237 motion_frozen_playlists.clear ();
4239 commit_reversible_command();
4241 /* no mouse movement */
4247 Editor::point_trim (GdkEvent* event)
4249 RegionView* rv = clicked_regionview;
4250 nframes_t new_bound = drag_info.current_pointer_frame;
4252 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4253 snap_to (new_bound);
4256 /* Choose action dependant on which button was pressed */
4257 switch (event->button.button) {
4259 trim_op = StartTrim;
4260 begin_reversible_command (_("Start point trim"));
4262 if (rv->get_selected()) {
4264 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4265 i != selection->regions.by_layer().end(); ++i)
4267 if (!(*i)->region()->locked()) {
4268 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4269 XMLNode &before = pl->get_state();
4270 (*i)->region()->trim_front (new_bound, this);
4271 XMLNode &after = pl->get_state();
4272 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4278 if (!rv->region()->locked()) {
4279 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4280 XMLNode &before = pl->get_state();
4281 rv->region()->trim_front (new_bound, this);
4282 XMLNode &after = pl->get_state();
4283 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4287 commit_reversible_command();
4292 begin_reversible_command (_("End point trim"));
4294 if (rv->get_selected()) {
4296 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4298 if (!(*i)->region()->locked()) {
4299 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4300 XMLNode &before = pl->get_state();
4301 (*i)->region()->trim_end (new_bound, this);
4302 XMLNode &after = pl->get_state();
4303 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4309 if (!rv->region()->locked()) {
4310 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4311 XMLNode &before = pl->get_state();
4312 rv->region()->trim_end (new_bound, this);
4313 XMLNode &after = pl->get_state();
4314 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4318 commit_reversible_command();
4327 Editor::thaw_region_after_trim (RegionView& rv)
4329 boost::shared_ptr<Region> region (rv.region());
4331 if (region->locked()) {
4335 region->thaw (_("trimmed region"));
4336 XMLNode &after = region->playlist()->get_state();
4337 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4339 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4341 arv->unhide_envelope ();
4345 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4350 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4351 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4355 Location* location = find_location_from_marker (marker, is_start);
4356 location->set_hidden (true, this);
4361 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4367 drag_info.item = item;
4368 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4369 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4371 range_marker_op = op;
4373 if (!temp_location) {
4374 temp_location = new Location;
4378 case CreateRangeMarker:
4379 case CreateTransportMarker:
4381 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4382 drag_info.copy = true;
4384 drag_info.copy = false;
4386 start_grab (event, selector_cursor);
4390 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4395 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4397 nframes_t start = 0;
4399 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4401 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4402 snap_to (drag_info.current_pointer_frame);
4405 /* only alter selection if the current frame is
4406 different from the last frame position.
4409 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4411 switch (range_marker_op) {
4412 case CreateRangeMarker:
4413 case CreateTransportMarker:
4414 if (drag_info.first_move) {
4415 snap_to (drag_info.grab_frame);
4418 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4419 start = drag_info.current_pointer_frame;
4420 end = drag_info.grab_frame;
4422 end = drag_info.current_pointer_frame;
4423 start = drag_info.grab_frame;
4426 /* first drag: Either add to the selection
4427 or create a new selection.
4430 if (drag_info.first_move) {
4432 temp_location->set (start, end);
4436 update_marker_drag_item (temp_location);
4437 range_marker_drag_rect->show();
4438 range_marker_drag_rect->raise_to_top();
4444 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4445 start_canvas_autoscroll (1);
4449 temp_location->set (start, end);
4451 double x1 = frame_to_pixel (start);
4452 double x2 = frame_to_pixel (end);
4453 crect->property_x1() = x1;
4454 crect->property_x2() = x2;
4456 update_marker_drag_item (temp_location);
4459 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4460 drag_info.first_move = false;
4462 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4467 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4469 Location * newloc = 0;
4472 if (!drag_info.first_move) {
4473 drag_range_markerbar_op (item, event);
4475 switch (range_marker_op) {
4476 case CreateRangeMarker:
4478 begin_reversible_command (_("new range marker"));
4479 XMLNode &before = session->locations()->get_state();
4480 session->locations()->next_available_name(rangename,"unnamed");
4481 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4482 session->locations()->add (newloc, true);
4483 XMLNode &after = session->locations()->get_state();
4484 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4485 commit_reversible_command ();
4487 range_bar_drag_rect->hide();
4488 range_marker_drag_rect->hide();
4492 case CreateTransportMarker:
4493 // popup menu to pick loop or punch
4494 new_transport_marker_context_menu (&event->button, item);
4499 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4501 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4506 start = session->locations()->first_mark_before (drag_info.grab_frame);
4507 end = session->locations()->first_mark_after (drag_info.grab_frame);
4509 if (end == max_frames) {
4510 end = session->current_end_frame ();
4514 start = session->current_start_frame ();
4517 switch (mouse_mode) {
4519 /* find the two markers on either side and then make the selection from it */
4520 cerr << "select between " << start << " .. " << end << endl;
4521 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4525 /* find the two markers on either side of the click and make the range out of it */
4526 selection->set (0, start, end);
4535 stop_canvas_autoscroll ();
4541 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4543 drag_info.item = item;
4544 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4545 drag_info.finished_callback = &Editor::end_mouse_zoom;
4547 start_grab (event, zoom_cursor);
4549 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4553 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4558 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4559 snap_to (drag_info.current_pointer_frame);
4561 if (drag_info.first_move) {
4562 snap_to (drag_info.grab_frame);
4566 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4568 /* base start and end on initial click position */
4569 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4570 start = drag_info.current_pointer_frame;
4571 end = drag_info.grab_frame;
4573 end = drag_info.current_pointer_frame;
4574 start = drag_info.grab_frame;
4579 if (drag_info.first_move) {
4581 zoom_rect->raise_to_top();
4584 reposition_zoom_rect(start, end);
4586 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4587 drag_info.first_move = false;
4589 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4594 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4596 if (!drag_info.first_move) {
4597 drag_mouse_zoom (item, event);
4599 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4600 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4602 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4605 temporal_zoom_to_frame (false, drag_info.grab_frame);
4607 temporal_zoom_step (false);
4608 center_screen (drag_info.grab_frame);
4616 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4618 double x1 = frame_to_pixel (start);
4619 double x2 = frame_to_pixel (end);
4620 double y2 = full_canvas_height - 1.0;
4622 zoom_rect->property_x1() = x1;
4623 zoom_rect->property_y1() = 1.0;
4624 zoom_rect->property_x2() = x2;
4625 zoom_rect->property_y2() = y2;
4629 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4631 drag_info.item = item;
4632 drag_info.motion_callback = &Editor::drag_rubberband_select;
4633 drag_info.finished_callback = &Editor::end_rubberband_select;
4635 start_grab (event, cross_hair_cursor);
4637 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4641 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4648 /* use a bigger drag threshold than the default */
4650 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4654 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4655 // snap_to (drag_info.current_pointer_frame);
4657 // if (drag_info.first_move) {
4658 // snap_to (drag_info.grab_frame);
4663 /* base start and end on initial click position */
4664 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4665 start = drag_info.current_pointer_frame;
4666 end = drag_info.grab_frame;
4668 end = drag_info.current_pointer_frame;
4669 start = drag_info.grab_frame;
4672 if (drag_info.current_pointer_y < drag_info.grab_y) {
4673 y1 = drag_info.current_pointer_y;
4674 y2 = drag_info.grab_y;
4677 y2 = drag_info.current_pointer_y;
4678 y1 = drag_info.grab_y;
4682 if (start != end || y1 != y2) {
4684 double x1 = frame_to_pixel (start);
4685 double x2 = frame_to_pixel (end);
4687 rubberband_rect->property_x1() = x1;
4688 rubberband_rect->property_y1() = y1;
4689 rubberband_rect->property_x2() = x2;
4690 rubberband_rect->property_y2() = y2;
4692 rubberband_rect->show();
4693 rubberband_rect->raise_to_top();
4695 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4696 drag_info.first_move = false;
4698 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4703 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4705 if (!drag_info.first_move) {
4707 drag_rubberband_select (item, event);
4710 if (drag_info.current_pointer_y < drag_info.grab_y) {
4711 y1 = drag_info.current_pointer_y;
4712 y2 = drag_info.grab_y;
4715 y2 = drag_info.current_pointer_y;
4716 y1 = drag_info.grab_y;
4720 Selection::Operation op = Keyboard::selection_type (event->button.state);
4723 begin_reversible_command (_("select regions"));
4725 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4726 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4728 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4732 commit_reversible_command ();
4736 selection->clear_regions();
4737 selection->clear_points ();
4738 selection->clear_lines ();
4741 rubberband_rect->hide();
4746 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4748 using namespace Gtkmm2ext;
4750 ArdourPrompter prompter (false);
4752 prompter.set_prompt (_("Name for region:"));
4753 prompter.set_initial_text (clicked_regionview->region()->name());
4754 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4755 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4756 prompter.show_all ();
4757 switch (prompter.run ()) {
4758 case Gtk::RESPONSE_ACCEPT:
4760 prompter.get_result(str);
4762 clicked_regionview->region()->set_name (str);
4770 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4772 drag_info.item = item;
4773 drag_info.motion_callback = &Editor::time_fx_motion;
4774 drag_info.finished_callback = &Editor::end_time_fx;
4778 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4782 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4784 RegionView* rv = clicked_regionview;
4786 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4787 snap_to (drag_info.current_pointer_frame);
4790 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4794 if (drag_info.current_pointer_frame > rv->region()->position()) {
4795 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4798 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4799 drag_info.first_move = false;
4801 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4805 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4807 clicked_regionview->get_time_axis_view().hide_timestretch ();
4809 if (drag_info.first_move) {
4813 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4814 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4816 begin_reversible_command (_("timestretch"));
4818 if (run_timestretch (selection->regions, percentage) == 0) {
4819 session->commit_reversible_command ();
4824 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4826 /* no brushing without a useful snap setting */
4829 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4832 switch (snap_mode) {
4834 return; /* can't work because it allows region to be placed anywhere */
4839 switch (snap_type) {
4842 case SnapToEditCursor:
4849 /* don't brush a copy over the original */
4851 if (pos == rv->region()->position()) {
4855 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4857 if (atv == 0 || !atv->is_track()) {
4861 boost::shared_ptr<Playlist> playlist = atv->playlist();
4862 double speed = atv->get_diskstream()->speed();
4864 XMLNode &before = playlist->get_state();
4865 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4866 XMLNode &after = playlist->get_state();
4867 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4869 // playlist is frozen, so we have to update manually
4871 playlist->Modified(); /* EMIT SIGNAL */
4875 Editor::track_height_step_timeout ()
4878 struct timeval delta;
4880 gettimeofday (&now, 0);
4881 timersub (&now, &last_track_height_step_timestamp, &delta);
4883 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4884 current_stepping_trackview = 0;