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.
28 #include <pbd/error.h>
29 #include <gtkmm2ext/utils.h>
30 #include <pbd/memento_command.h>
32 #include "ardour_ui.h"
34 #include "time_axis_view.h"
35 #include "audio_time_axis.h"
36 #include "audio_region_view.h"
38 #include "streamview.h"
39 #include "region_gain_line.h"
40 #include "automation_time_axis.h"
43 #include "selection.h"
46 #include "rgb_macros.h"
48 #include <ardour/types.h>
49 #include <ardour/route.h>
50 #include <ardour/audio_track.h>
51 #include <ardour/audio_diskstream.h>
52 #include <ardour/playlist.h>
53 #include <ardour/audioplaylist.h>
54 #include <ardour/audioregion.h>
55 #include <ardour/dB.h>
56 #include <ardour/utils.h>
57 #include <ardour/region_factory.h>
64 using namespace ARDOUR;
68 using namespace Editing;
71 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
85 switch (event->type) {
86 case GDK_BUTTON_RELEASE:
87 case GDK_BUTTON_PRESS:
88 case GDK_2BUTTON_PRESS:
89 case GDK_3BUTTON_PRESS:
90 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
92 case GDK_MOTION_NOTIFY:
93 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
95 case GDK_ENTER_NOTIFY:
96 case GDK_LEAVE_NOTIFY:
97 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
100 case GDK_KEY_RELEASE:
101 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
104 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
108 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
109 position is negative (as can be the case with motion events in particular),
110 the frame location is always positive.
113 return pixel_to_frame (*pcx);
117 Editor::mouse_mode_toggled (MouseMode m)
119 if (ignore_mouse_mode_toggle) {
125 if (mouse_select_button.get_active()) {
131 if (mouse_move_button.get_active()) {
137 if (mouse_gain_button.get_active()) {
143 if (mouse_zoom_button.get_active()) {
149 if (mouse_timefx_button.get_active()) {
155 if (mouse_audition_button.get_active()) {
166 Editor::set_mouse_mode (MouseMode m, bool force)
168 if (drag_info.item) {
172 if (!force && m == mouse_mode) {
180 if (mouse_mode != MouseRange) {
182 /* in all modes except range, hide the range selection,
183 show the object (region) selection.
186 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
187 (*i)->set_should_show_selection (true);
189 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
190 (*i)->hide_selection ();
196 in range mode,show the range selection.
199 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
200 if ((*i)->get_selected()) {
201 (*i)->show_selection (selection->time);
206 /* XXX the hack of unsetting all other buttongs should go
207 away once GTK2 allows us to use regular radio buttons drawn like
208 normal buttons, rather than my silly GroupedButton hack.
211 ignore_mouse_mode_toggle = true;
213 switch (mouse_mode) {
215 mouse_select_button.set_active (true);
216 current_canvas_cursor = selector_cursor;
220 mouse_move_button.set_active (true);
221 current_canvas_cursor = grabber_cursor;
225 mouse_gain_button.set_active (true);
226 current_canvas_cursor = cross_hair_cursor;
230 mouse_zoom_button.set_active (true);
231 current_canvas_cursor = zoom_cursor;
235 mouse_timefx_button.set_active (true);
236 current_canvas_cursor = time_fx_cursor; // just use playhead
240 mouse_audition_button.set_active (true);
241 current_canvas_cursor = speaker_cursor;
245 ignore_mouse_mode_toggle = false;
248 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
253 Editor::step_mouse_mode (bool next)
255 switch (current_mouse_mode()) {
257 if (next) set_mouse_mode (MouseRange);
258 else set_mouse_mode (MouseTimeFX);
262 if (next) set_mouse_mode (MouseZoom);
263 else set_mouse_mode (MouseObject);
267 if (next) set_mouse_mode (MouseGain);
268 else set_mouse_mode (MouseRange);
272 if (next) set_mouse_mode (MouseTimeFX);
273 else set_mouse_mode (MouseZoom);
277 if (next) set_mouse_mode (MouseAudition);
278 else set_mouse_mode (MouseGain);
282 if (next) set_mouse_mode (MouseObject);
283 else set_mouse_mode (MouseTimeFX);
289 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
293 /* in object/audition/timefx mode, any button press sets
294 the selection if the object can be selected. this is a
295 bit of hack, because we want to avoid this if the
296 mouse operation is a region alignment.
298 note: not dbl-click or triple-click
301 if (((mouse_mode != MouseObject) &&
302 (mouse_mode != MouseAudition || item_type != RegionItem) &&
303 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
304 (mouse_mode != MouseRange)) ||
306 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
311 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
313 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
315 /* almost no selection action on modified button-2 or button-3 events */
317 if (item_type != RegionItem && event->button.button != 2) {
323 Selection::Operation op = Keyboard::selection_type (event->button.state);
324 bool press = (event->type == GDK_BUTTON_PRESS);
326 begin_reversible_command (_("select on click"));
330 if (mouse_mode != MouseRange) {
331 commit = set_selected_regionview_from_click (press, op, true);
332 } else if (event->type == GDK_BUTTON_PRESS) {
333 commit = set_selected_track_from_click (press, op, false);
337 case RegionViewNameHighlight:
339 if (mouse_mode != MouseRange) {
340 commit = set_selected_regionview_from_click (press, op, true);
341 } else if (event->type == GDK_BUTTON_PRESS) {
342 commit = set_selected_track_from_click (press, op, false);
346 case FadeInHandleItem:
348 case FadeOutHandleItem:
350 if (mouse_mode != MouseRange) {
351 commit = set_selected_regionview_from_click (press, op, true);
352 } else if (event->type == GDK_BUTTON_PRESS) {
353 commit = set_selected_track_from_click (press, op, false);
357 case GainAutomationControlPointItem:
358 case PanAutomationControlPointItem:
359 case RedirectAutomationControlPointItem:
360 if (mouse_mode != MouseRange) {
361 commit = set_selected_control_point_from_click (op, false);
366 /* for context click or range selection, select track */
367 if (event->button.button == 3) {
368 commit = set_selected_track_from_click (press, op, true);
369 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
370 commit = set_selected_track_from_click (press, op, false);
374 case AutomationTrackItem:
375 commit = set_selected_track_from_click (press, op, true);
383 commit_reversible_command ();
388 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
390 nframes_t where = event_frame (event, 0, 0);
392 track_canvas.grab_focus();
394 if (session && session->actively_recording()) {
398 button_selection (item, event, item_type);
400 if (drag_info.item == 0 &&
401 (Keyboard::is_delete_event (&event->button) ||
402 Keyboard::is_context_menu_event (&event->button) ||
403 Keyboard::is_edit_event (&event->button))) {
405 /* handled by button release */
409 switch (event->button.button) {
412 if (event->type == GDK_BUTTON_PRESS) {
414 if (drag_info.item) {
415 drag_info.item->ungrab (event->button.time);
418 /* single mouse clicks on any of these item types operate
419 independent of mouse mode, mostly because they are
420 not on the main track canvas or because we want
426 case PlayheadCursorItem:
427 start_cursor_grab (item, event);
431 if (Keyboard::modifier_state_equals (event->button.state,
432 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
433 hide_marker (item, event);
435 start_marker_grab (item, event);
439 case TempoMarkerItem:
440 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
441 start_tempo_marker_copy_grab (item, event);
443 start_tempo_marker_grab (item, event);
447 case MeterMarkerItem:
448 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
449 start_meter_marker_copy_grab (item, event);
451 start_meter_marker_grab (item, event);
461 case RangeMarkerBarItem:
462 start_range_markerbar_op (item, event, CreateRangeMarker);
466 case TransportMarkerBarItem:
467 start_range_markerbar_op (item, event, CreateTransportMarker);
476 switch (mouse_mode) {
479 case StartSelectionTrimItem:
480 start_selection_op (item, event, SelectionStartTrim);
483 case EndSelectionTrimItem:
484 start_selection_op (item, event, SelectionEndTrim);
488 if (Keyboard::modifier_state_contains
489 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
490 // contains and not equals because I can't use alt as a modifier alone.
491 start_selection_grab (item, event);
492 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
493 /* grab selection for moving */
494 start_selection_op (item, event, SelectionMove);
497 /* this was debated, but decided the more common action was to
498 make a new selection */
499 start_selection_op (item, event, CreateSelection);
504 start_selection_op (item, event, CreateSelection);
510 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
511 event->type == GDK_BUTTON_PRESS) {
513 start_rubberband_select (item, event);
515 } else if (event->type == GDK_BUTTON_PRESS) {
518 case FadeInHandleItem:
519 start_fade_in_grab (item, event);
522 case FadeOutHandleItem:
523 start_fade_out_grab (item, event);
527 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
528 start_region_copy_grab (item, event);
529 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
530 start_region_brush_grab (item, event);
532 start_region_grab (item, event);
536 case RegionViewNameHighlight:
537 start_trim (item, event);
542 /* rename happens on edit clicks */
543 start_trim (clicked_regionview->get_name_highlight(), event);
547 case GainAutomationControlPointItem:
548 case PanAutomationControlPointItem:
549 case RedirectAutomationControlPointItem:
550 start_control_point_grab (item, event);
554 case GainAutomationLineItem:
555 case PanAutomationLineItem:
556 case RedirectAutomationLineItem:
557 start_line_grab_from_line (item, event);
562 case AutomationTrackItem:
563 start_rubberband_select (item, event);
566 /* <CMT Additions> */
567 case ImageFrameHandleStartItem:
568 imageframe_start_handle_op(item, event) ;
571 case ImageFrameHandleEndItem:
572 imageframe_end_handle_op(item, event) ;
575 case MarkerViewHandleStartItem:
576 markerview_item_start_handle_op(item, event) ;
579 case MarkerViewHandleEndItem:
580 markerview_item_end_handle_op(item, event) ;
583 /* </CMT Additions> */
585 /* <CMT Additions> */
587 start_markerview_grab(item, event) ;
590 start_imageframe_grab(item, event) ;
592 /* </CMT Additions> */
608 // start_line_grab_from_regionview (item, event);
611 case GainControlPointItem:
612 start_control_point_grab (item, event);
616 start_line_grab_from_line (item, event);
619 case GainAutomationControlPointItem:
620 case PanAutomationControlPointItem:
621 case RedirectAutomationControlPointItem:
622 start_control_point_grab (item, event);
633 case GainAutomationControlPointItem:
634 case PanAutomationControlPointItem:
635 case RedirectAutomationControlPointItem:
636 start_control_point_grab (item, event);
639 case GainAutomationLineItem:
640 case PanAutomationLineItem:
641 case RedirectAutomationLineItem:
642 start_line_grab_from_line (item, event);
646 // XXX need automation mode to identify which
648 // start_line_grab_from_regionview (item, event);
658 if (event->type == GDK_BUTTON_PRESS) {
659 start_mouse_zoom (item, event);
666 if (item_type == RegionItem) {
667 start_time_fx (item, event);
672 /* handled in release */
681 switch (mouse_mode) {
683 if (event->type == GDK_BUTTON_PRESS) {
686 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
687 start_region_copy_grab (item, event);
689 start_region_grab (item, event);
693 case GainAutomationControlPointItem:
694 case PanAutomationControlPointItem:
695 case RedirectAutomationControlPointItem:
696 start_control_point_grab (item, event);
707 case RegionViewNameHighlight:
708 start_trim (item, event);
713 start_trim (clicked_regionview->get_name_highlight(), event);
724 if (event->type == GDK_BUTTON_PRESS) {
725 /* relax till release */
732 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
733 temporal_zoom_session();
735 temporal_zoom_to_frame (true, event_frame(event));
750 switch (mouse_mode) {
752 //temporal_zoom_to_frame (true, where);
753 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
754 temporal_zoom_to_frame (true, where);
757 temporal_zoom_step (true);
762 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
763 scroll_backward (0.6f);
766 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
767 scroll_tracks_up_line ();
769 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
770 if (clicked_trackview) {
771 if (!current_stepping_trackview) {
772 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
773 current_stepping_trackview = clicked_trackview;
775 gettimeofday (&last_track_height_step_timestamp, 0);
776 current_stepping_trackview->step_height (true);
779 else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
780 temporal_zoom_to_frame (true, where);
787 switch (mouse_mode) {
789 // temporal_zoom_to_frame (false, where);
790 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
791 temporal_zoom_to_frame (false, where);
794 temporal_zoom_step (false);
799 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
800 scroll_forward (0.6f);
803 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
804 scroll_tracks_down_line ();
806 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
807 if (clicked_trackview) {
808 if (!current_stepping_trackview) {
809 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
810 current_stepping_trackview = clicked_trackview;
812 gettimeofday (&last_track_height_step_timestamp, 0);
813 current_stepping_trackview->step_height (false);
815 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
816 temporal_zoom_to_frame (false, where);
831 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
833 nframes_t where = event_frame (event, 0, 0);
835 /* no action if we're recording */
837 if (session && session->actively_recording()) {
841 /* first, see if we're finishing a drag ... */
843 if (drag_info.item) {
844 if (end_grab (item, event)) {
845 /* grab dragged, so do nothing else */
850 button_selection (item, event, item_type);
852 /* edit events get handled here */
854 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
860 case TempoMarkerItem:
861 edit_tempo_marker (item);
864 case MeterMarkerItem:
865 edit_meter_marker (item);
869 if (clicked_regionview->name_active()) {
870 return mouse_rename_region (item, event);
880 /* context menu events get handled here */
882 if (Keyboard::is_context_menu_event (&event->button)) {
884 if (drag_info.item == 0) {
886 /* no matter which button pops up the context menu, tell the menu
887 widget to use button 1 to drive menu selection.
892 case FadeInHandleItem:
894 case FadeOutHandleItem:
895 popup_fade_context_menu (1, event->button.time, item, item_type);
899 popup_track_context_menu (1, event->button.time, item_type, false, where);
903 case RegionViewNameHighlight:
905 popup_track_context_menu (1, event->button.time, item_type, false, where);
909 popup_track_context_menu (1, event->button.time, item_type, true, where);
912 case AutomationTrackItem:
913 popup_track_context_menu (1, event->button.time, item_type, false, where);
917 case RangeMarkerBarItem:
918 case TransportMarkerBarItem:
921 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
925 marker_context_menu (&event->button, item);
928 case TempoMarkerItem:
929 tm_marker_context_menu (&event->button, item);
932 case MeterMarkerItem:
933 tm_marker_context_menu (&event->button, item);
936 case CrossfadeViewItem:
937 popup_track_context_menu (1, event->button.time, item_type, false, where);
940 /* <CMT Additions> */
942 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
944 case ImageFrameTimeAxisItem:
945 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
948 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
950 case MarkerTimeAxisItem:
951 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
953 /* <CMT Additions> */
964 /* delete events get handled here */
966 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
969 case TempoMarkerItem:
970 remove_tempo_marker (item);
973 case MeterMarkerItem:
974 remove_meter_marker (item);
978 remove_marker (*item, event);
982 if (mouse_mode == MouseObject) {
983 remove_clicked_region ();
987 case GainControlPointItem:
988 if (mouse_mode == MouseGain) {
989 remove_gain_control_point (item, event);
993 case GainAutomationControlPointItem:
994 case PanAutomationControlPointItem:
995 case RedirectAutomationControlPointItem:
996 remove_control_point (item, event);
1005 switch (event->button.button) {
1008 switch (item_type) {
1009 /* see comments in button_press_handler */
1010 case EditCursorItem:
1011 case PlayheadCursorItem:
1014 case GainAutomationLineItem:
1015 case PanAutomationLineItem:
1016 case RedirectAutomationLineItem:
1017 case StartSelectionTrimItem:
1018 case EndSelectionTrimItem:
1022 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1023 snap_to (where, 0, true);
1025 mouse_add_new_marker (where);
1029 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1032 mouse_add_new_tempo_event (where);
1036 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1044 switch (mouse_mode) {
1046 switch (item_type) {
1047 case AutomationTrackItem:
1048 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1062 // Gain only makes sense for audio regions
1063 if ( ! dynamic_cast<AudioRegionView*>(clicked_regionview))
1066 switch (item_type) {
1068 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1072 case AutomationTrackItem:
1073 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1074 add_automation_event (item, event, where, event->button.y);
1083 switch (item_type) {
1085 audition_selected_region ();
1102 switch (mouse_mode) {
1105 switch (item_type) {
1107 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1109 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1112 // Button2 click is unused
1125 // x_style_paste (where, 1.0);
1145 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1151 switch (item_type) {
1152 case GainControlPointItem:
1153 if (mouse_mode == MouseGain) {
1154 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1155 cp->set_visible (true);
1159 at_y = cp->get_y ();
1160 cp->item->i2w (at_x, at_y);
1164 fraction = 1.0 - (cp->get_y() / cp->line.height());
1166 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1167 show_verbose_canvas_cursor ();
1169 if (is_drawable()) {
1170 track_canvas.get_window()->set_cursor (*fader_cursor);
1175 case GainAutomationControlPointItem:
1176 case PanAutomationControlPointItem:
1177 case RedirectAutomationControlPointItem:
1178 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1179 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1180 cp->set_visible (true);
1184 at_y = cp->get_y ();
1185 cp->item->i2w (at_x, at_y);
1189 fraction = 1.0 - (cp->get_y() / cp->line.height());
1191 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1192 show_verbose_canvas_cursor ();
1194 if (is_drawable()) {
1195 track_canvas.get_window()->set_cursor (*fader_cursor);
1201 if (mouse_mode == MouseGain) {
1202 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1204 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1205 if (is_drawable()) {
1206 track_canvas.get_window()->set_cursor (*fader_cursor);
1211 case GainAutomationLineItem:
1212 case RedirectAutomationLineItem:
1213 case PanAutomationLineItem:
1214 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1216 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1218 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1220 if (is_drawable()) {
1221 track_canvas.get_window()->set_cursor (*fader_cursor);
1226 case RegionViewNameHighlight:
1227 if (is_drawable() && mouse_mode == MouseObject) {
1228 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1232 case StartSelectionTrimItem:
1233 case EndSelectionTrimItem:
1234 /* <CMT Additions> */
1235 case ImageFrameHandleStartItem:
1236 case ImageFrameHandleEndItem:
1237 case MarkerViewHandleStartItem:
1238 case MarkerViewHandleEndItem:
1239 /* </CMT Additions> */
1241 if (is_drawable()) {
1242 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1246 case EditCursorItem:
1247 case PlayheadCursorItem:
1248 if (is_drawable()) {
1249 track_canvas.get_window()->set_cursor (*grabber_cursor);
1253 case RegionViewName:
1255 /* when the name is not an active item, the entire name highlight is for trimming */
1257 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1258 if (mouse_mode == MouseObject && is_drawable()) {
1259 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1265 case AutomationTrackItem:
1266 if (is_drawable()) {
1267 Gdk::Cursor *cursor;
1268 switch (mouse_mode) {
1270 cursor = selector_cursor;
1273 cursor = zoom_cursor;
1276 cursor = cross_hair_cursor;
1280 track_canvas.get_window()->set_cursor (*cursor);
1282 AutomationTimeAxisView* atv;
1283 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1284 clear_entered_track = false;
1285 set_entered_track (atv);
1291 case RangeMarkerBarItem:
1292 case TransportMarkerBarItem:
1295 if (is_drawable()) {
1296 time_canvas.get_window()->set_cursor (*timebar_cursor);
1301 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1304 marker->set_color_rgba (color_map[cEnteredMarker]);
1306 case MeterMarkerItem:
1307 case TempoMarkerItem:
1308 if (is_drawable()) {
1309 time_canvas.get_window()->set_cursor (*timebar_cursor);
1312 case FadeInHandleItem:
1313 case FadeOutHandleItem:
1314 if (mouse_mode == MouseObject) {
1315 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1317 rect->property_fill_color_rgba() = 0;
1318 rect->property_outline_pixels() = 1;
1327 /* second pass to handle entered track status in a comprehensible way.
1330 switch (item_type) {
1332 case GainAutomationLineItem:
1333 case RedirectAutomationLineItem:
1334 case PanAutomationLineItem:
1335 case GainControlPointItem:
1336 case GainAutomationControlPointItem:
1337 case PanAutomationControlPointItem:
1338 case RedirectAutomationControlPointItem:
1339 /* these do not affect the current entered track state */
1340 clear_entered_track = false;
1343 case AutomationTrackItem:
1344 /* handled above already */
1348 set_entered_track (0);
1356 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1365 switch (item_type) {
1366 case GainControlPointItem:
1367 case GainAutomationControlPointItem:
1368 case PanAutomationControlPointItem:
1369 case RedirectAutomationControlPointItem:
1370 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1371 if (cp->line.npoints() > 1) {
1372 if (!cp->selected) {
1373 cp->set_visible (false);
1377 if (is_drawable()) {
1378 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1381 hide_verbose_canvas_cursor ();
1384 case RegionViewNameHighlight:
1385 case StartSelectionTrimItem:
1386 case EndSelectionTrimItem:
1387 case EditCursorItem:
1388 case PlayheadCursorItem:
1389 /* <CMT Additions> */
1390 case ImageFrameHandleStartItem:
1391 case ImageFrameHandleEndItem:
1392 case MarkerViewHandleStartItem:
1393 case MarkerViewHandleEndItem:
1394 /* </CMT Additions> */
1395 if (is_drawable()) {
1396 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1401 case GainAutomationLineItem:
1402 case RedirectAutomationLineItem:
1403 case PanAutomationLineItem:
1404 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1406 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1408 line->property_fill_color_rgba() = al->get_line_color();
1410 if (is_drawable()) {
1411 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1415 case RegionViewName:
1416 /* see enter_handler() for notes */
1417 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1418 if (is_drawable() && mouse_mode == MouseObject) {
1419 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1424 case RangeMarkerBarItem:
1425 case TransportMarkerBarItem:
1429 if (is_drawable()) {
1430 time_canvas.get_window()->set_cursor (*timebar_cursor);
1435 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1438 loc = find_location_from_marker (marker, is_start);
1439 if (loc) location_flags_changed (loc, this);
1441 case MeterMarkerItem:
1442 case TempoMarkerItem:
1444 if (is_drawable()) {
1445 time_canvas.get_window()->set_cursor (*timebar_cursor);
1450 case FadeInHandleItem:
1451 case FadeOutHandleItem:
1452 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1454 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1456 rect->property_fill_color_rgba() = rv->get_fill_color();
1457 rect->property_outline_pixels() = 0;
1462 case AutomationTrackItem:
1463 if (is_drawable()) {
1464 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1465 clear_entered_track = true;
1466 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1478 Editor::left_automation_track ()
1480 if (clear_entered_track) {
1481 set_entered_track (0);
1482 clear_entered_track = false;
1488 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1492 /* We call this so that MOTION_NOTIFY events continue to be
1493 delivered to the canvas. We need to do this because we set
1494 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1495 the density of the events, at the expense of a round-trip
1496 to the server. Given that this will mostly occur on cases
1497 where DISPLAY = :0.0, and given the cost of what the motion
1498 event might do, its a good tradeoff.
1501 track_canvas.get_pointer (x, y);
1503 if (current_stepping_trackview) {
1504 /* don't keep the persistent stepped trackview if the mouse moves */
1505 current_stepping_trackview = 0;
1506 step_timeout.disconnect ();
1509 if (session && session->actively_recording()) {
1510 /* Sorry. no dragging stuff around while we record */
1514 drag_info.item_type = item_type;
1515 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1516 &drag_info.current_pointer_y);
1518 if (!from_autoscroll && drag_info.item) {
1519 /* item != 0 is the best test i can think of for dragging.
1521 if (!drag_info.move_threshold_passed) {
1523 bool x_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1524 bool y_threshold_passed = (abs ((int) (drag_info.current_pointer_y - drag_info.grab_y)) > 4);
1526 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1528 // and change the initial grab loc/frame if this drag info wants us to
1530 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1531 drag_info.grab_frame = drag_info.current_pointer_frame;
1532 drag_info.grab_x = drag_info.current_pointer_x;
1533 drag_info.grab_y = drag_info.current_pointer_y;
1534 drag_info.last_pointer_frame = drag_info.grab_frame;
1535 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1540 switch (item_type) {
1541 case PlayheadCursorItem:
1542 case EditCursorItem:
1544 case GainControlPointItem:
1545 case RedirectAutomationControlPointItem:
1546 case GainAutomationControlPointItem:
1547 case PanAutomationControlPointItem:
1548 case TempoMarkerItem:
1549 case MeterMarkerItem:
1550 case RegionViewNameHighlight:
1551 case StartSelectionTrimItem:
1552 case EndSelectionTrimItem:
1555 case RedirectAutomationLineItem:
1556 case GainAutomationLineItem:
1557 case PanAutomationLineItem:
1558 case FadeInHandleItem:
1559 case FadeOutHandleItem:
1560 /* <CMT Additions> */
1561 case ImageFrameHandleStartItem:
1562 case ImageFrameHandleEndItem:
1563 case MarkerViewHandleStartItem:
1564 case MarkerViewHandleEndItem:
1565 /* </CMT Additions> */
1566 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1567 (event->motion.state & Gdk::BUTTON2_MASK))) {
1568 if (!from_autoscroll) {
1569 maybe_autoscroll (event);
1571 (this->*(drag_info.motion_callback)) (item, event);
1580 switch (mouse_mode) {
1585 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1586 (event->motion.state & GDK_BUTTON2_MASK))) {
1587 if (!from_autoscroll) {
1588 maybe_autoscroll (event);
1590 (this->*(drag_info.motion_callback)) (item, event);
1601 track_canvas_motion (event);
1602 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1610 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1612 if (drag_info.item == 0) {
1613 fatal << _("programming error: start_grab called without drag item") << endmsg;
1619 cursor = grabber_cursor;
1622 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1624 if (event->button.button == 2) {
1625 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1626 drag_info.y_constrained = true;
1627 drag_info.x_constrained = false;
1629 drag_info.y_constrained = false;
1630 drag_info.x_constrained = true;
1633 drag_info.x_constrained = false;
1634 drag_info.y_constrained = false;
1637 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1638 drag_info.last_pointer_frame = drag_info.grab_frame;
1639 drag_info.current_pointer_frame = drag_info.grab_frame;
1640 drag_info.current_pointer_x = drag_info.grab_x;
1641 drag_info.current_pointer_y = drag_info.grab_y;
1642 drag_info.cumulative_x_drag = 0;
1643 drag_info.cumulative_y_drag = 0;
1644 drag_info.first_move = true;
1645 drag_info.move_threshold_passed = false;
1646 drag_info.want_move_threshold = false;
1647 drag_info.pointer_frame_offset = 0;
1648 drag_info.brushing = false;
1649 drag_info.copied_location = 0;
1651 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1653 event->button.time);
1655 if (session && session->transport_rolling()) {
1656 drag_info.was_rolling = true;
1658 drag_info.was_rolling = false;
1661 switch (snap_type) {
1662 case SnapToRegionStart:
1663 case SnapToRegionEnd:
1664 case SnapToRegionSync:
1665 case SnapToRegionBoundary:
1666 build_region_boundary_cache ();
1674 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1676 drag_info.item->ungrab (0);
1677 drag_info.item = new_item;
1680 cursor = grabber_cursor;
1683 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1687 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1689 bool did_drag = false;
1691 stop_canvas_autoscroll ();
1693 if (drag_info.item == 0) {
1697 drag_info.item->ungrab (event->button.time);
1699 if (drag_info.finished_callback) {
1700 (this->*(drag_info.finished_callback)) (item, event);
1703 did_drag = !drag_info.first_move;
1705 hide_verbose_canvas_cursor();
1708 drag_info.copy = false;
1709 drag_info.motion_callback = 0;
1710 drag_info.finished_callback = 0;
1711 drag_info.last_trackview = 0;
1712 drag_info.last_frame_position = 0;
1713 drag_info.grab_frame = 0;
1714 drag_info.last_pointer_frame = 0;
1715 drag_info.current_pointer_frame = 0;
1716 drag_info.brushing = false;
1718 if (drag_info.copied_location) {
1719 delete drag_info.copied_location;
1720 drag_info.copied_location = 0;
1727 Editor::set_edit_cursor (GdkEvent* event)
1729 nframes_t pointer_frame = event_frame (event);
1731 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1732 if (snap_type != SnapToEditCursor) {
1733 snap_to (pointer_frame);
1737 edit_cursor->set_position (pointer_frame);
1738 edit_cursor_clock.set (pointer_frame);
1742 Editor::set_playhead_cursor (GdkEvent* event)
1744 nframes_t pointer_frame = event_frame (event);
1746 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1747 snap_to (pointer_frame);
1751 session->request_locate (pointer_frame, session->transport_rolling());
1756 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1758 drag_info.item = item;
1759 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1760 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1764 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1765 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1769 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1771 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1775 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1777 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1779 nframes_t fade_length;
1781 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1782 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1788 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1792 if (pos < (arv->region()->position() + 64)) {
1793 fade_length = 64; // this should be a minimum defined somewhere
1794 } else if (pos > arv->region()->last_frame()) {
1795 fade_length = arv->region()->length();
1797 fade_length = pos - arv->region()->position();
1799 /* mapover the region selection */
1801 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1803 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1809 tmp->reset_fade_in_shape_width (fade_length);
1812 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1814 drag_info.first_move = false;
1818 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1820 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1822 nframes_t fade_length;
1824 if (drag_info.first_move) return;
1826 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1827 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1832 if (pos < (arv->region()->position() + 64)) {
1833 fade_length = 64; // this should be a minimum defined somewhere
1834 } else if (pos > arv->region()->last_frame()) {
1835 fade_length = arv->region()->length();
1837 fade_length = pos - arv->region()->position();
1840 begin_reversible_command (_("change fade in length"));
1842 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1844 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1850 AutomationList& alist = tmp->audio_region()->fade_in();
1851 XMLNode &before = alist.get_state();
1853 tmp->audio_region()->set_fade_in_length (fade_length);
1855 XMLNode &after = alist.get_state();
1856 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1859 commit_reversible_command ();
1863 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1865 drag_info.item = item;
1866 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1867 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1871 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1872 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1876 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1878 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1882 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1884 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1886 nframes_t fade_length;
1888 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1889 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1895 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1899 if (pos > (arv->region()->last_frame() - 64)) {
1900 fade_length = 64; // this should really be a minimum fade defined somewhere
1902 else if (pos < arv->region()->position()) {
1903 fade_length = arv->region()->length();
1906 fade_length = arv->region()->last_frame() - pos;
1909 /* mapover the region selection */
1911 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1913 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1919 tmp->reset_fade_out_shape_width (fade_length);
1922 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1924 drag_info.first_move = false;
1928 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1930 if (drag_info.first_move) return;
1932 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1934 nframes_t fade_length;
1936 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1937 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1943 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1947 if (pos > (arv->region()->last_frame() - 64)) {
1948 fade_length = 64; // this should really be a minimum fade defined somewhere
1950 else if (pos < arv->region()->position()) {
1951 fade_length = arv->region()->length();
1954 fade_length = arv->region()->last_frame() - pos;
1957 begin_reversible_command (_("change fade out length"));
1959 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1961 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1967 AutomationList& alist = tmp->audio_region()->fade_out();
1968 XMLNode &before = alist.get_state();
1970 tmp->audio_region()->set_fade_out_length (fade_length);
1972 XMLNode &after = alist.get_state();
1973 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1976 commit_reversible_command ();
1980 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1982 drag_info.item = item;
1983 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1984 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1988 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1989 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1993 Cursor* cursor = (Cursor *) drag_info.data;
1995 if (cursor == playhead_cursor) {
1996 _dragging_playhead = true;
1998 if (session && drag_info.was_rolling) {
1999 session->request_stop ();
2003 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2005 show_verbose_time_cursor (cursor->current_frame, 10);
2009 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2011 Cursor* cursor = (Cursor *) drag_info.data;
2012 nframes_t adjusted_frame;
2014 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2015 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2021 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2022 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
2023 snap_to (adjusted_frame);
2027 if (adjusted_frame == drag_info.last_pointer_frame) return;
2029 cursor->set_position (adjusted_frame);
2031 if (cursor == edit_cursor) {
2032 edit_cursor_clock.set (cursor->current_frame);
2034 UpdateAllTransportClocks (cursor->current_frame);
2037 show_verbose_time_cursor (cursor->current_frame, 10);
2039 drag_info.last_pointer_frame = adjusted_frame;
2040 drag_info.first_move = false;
2044 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2046 if (drag_info.first_move) return;
2048 cursor_drag_motion_callback (item, event);
2050 _dragging_playhead = false;
2052 if (item == &playhead_cursor->canvas_item) {
2054 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2056 } else if (item == &edit_cursor->canvas_item) {
2057 edit_cursor->set_position (edit_cursor->current_frame);
2058 edit_cursor_clock.set (edit_cursor->current_frame);
2063 Editor::update_marker_drag_item (Location *location)
2065 double x1 = frame_to_pixel (location->start());
2066 double x2 = frame_to_pixel (location->end());
2068 if (location->is_mark()) {
2069 marker_drag_line_points.front().set_x(x1);
2070 marker_drag_line_points.back().set_x(x1);
2071 marker_drag_line->property_points() = marker_drag_line_points;
2074 range_marker_drag_rect->property_x1() = x1;
2075 range_marker_drag_rect->property_x2() = x2;
2080 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2084 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2085 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2091 Location *location = find_location_from_marker (marker, is_start);
2093 drag_info.item = item;
2094 drag_info.data = marker;
2095 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2096 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2100 drag_info.copied_location = new Location (*location);
2101 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2103 update_marker_drag_item (location);
2105 if (location->is_mark()) {
2106 marker_drag_line->show();
2107 marker_drag_line->raise_to_top();
2110 range_marker_drag_rect->show();
2111 range_marker_drag_rect->raise_to_top();
2114 if (is_start) show_verbose_time_cursor (location->start(), 10);
2115 else show_verbose_time_cursor (location->end(), 10);
2119 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2122 Marker* marker = (Marker *) drag_info.data;
2123 Location *real_location;
2124 Location *copy_location;
2126 bool move_both = false;
2130 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2131 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2137 nframes_t next = newframe;
2139 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2140 snap_to (newframe, 0, true);
2143 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2147 /* call this to find out if its the start or end */
2149 real_location = find_location_from_marker (marker, is_start);
2151 /* use the copy that we're "dragging" around */
2153 copy_location = drag_info.copied_location;
2155 f_delta = copy_location->end() - copy_location->start();
2157 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2161 if (copy_location->is_mark()) {
2164 copy_location->set_start (newframe);
2168 if (is_start) { // start-of-range marker
2171 copy_location->set_start (newframe);
2172 copy_location->set_end (newframe + f_delta);
2173 } else if (newframe < copy_location->end()) {
2174 copy_location->set_start (newframe);
2176 snap_to (next, 1, true);
2177 copy_location->set_end (next);
2178 copy_location->set_start (newframe);
2181 } else { // end marker
2184 copy_location->set_end (newframe);
2185 copy_location->set_start (newframe - f_delta);
2186 } else if (newframe > copy_location->start()) {
2187 copy_location->set_end (newframe);
2189 } else if (newframe > 0) {
2190 snap_to (next, -1, true);
2191 copy_location->set_start (next);
2192 copy_location->set_end (newframe);
2197 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2198 drag_info.first_move = false;
2200 update_marker_drag_item (copy_location);
2202 LocationMarkers* lm = find_location_markers (real_location);
2203 lm->set_position (copy_location->start(), copy_location->end());
2205 show_verbose_time_cursor (newframe, 10);
2209 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2211 if (drag_info.first_move) {
2212 marker_drag_motion_callback (item, event);
2216 Marker* marker = (Marker *) drag_info.data;
2220 begin_reversible_command ( _("move marker") );
2221 XMLNode &before = session->locations()->get_state();
2223 Location * location = find_location_from_marker (marker, is_start);
2226 if (location->is_mark()) {
2227 location->set_start (drag_info.copied_location->start());
2229 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2233 XMLNode &after = session->locations()->get_state();
2234 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2235 commit_reversible_command ();
2237 marker_drag_line->hide();
2238 range_marker_drag_rect->hide();
2242 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2245 MeterMarker* meter_marker;
2247 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2248 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2252 meter_marker = dynamic_cast<MeterMarker*> (marker);
2254 MetricSection& section (meter_marker->meter());
2256 if (!section.movable()) {
2260 drag_info.item = item;
2261 drag_info.data = marker;
2262 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2263 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2267 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2269 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2273 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2276 MeterMarker* meter_marker;
2278 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2279 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2283 meter_marker = dynamic_cast<MeterMarker*> (marker);
2285 // create a dummy marker for visual representation of moving the copy.
2286 // The actual copying is not done before we reach the finish callback.
2288 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2289 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2290 *new MeterSection(meter_marker->meter()));
2292 drag_info.item = &new_marker->the_item();
2293 drag_info.copy = true;
2294 drag_info.data = new_marker;
2295 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2296 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2300 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2302 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2306 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2308 MeterMarker* marker = (MeterMarker *) drag_info.data;
2309 nframes_t adjusted_frame;
2311 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2312 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2318 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2319 snap_to (adjusted_frame);
2322 if (adjusted_frame == drag_info.last_pointer_frame) return;
2324 marker->set_position (adjusted_frame);
2327 drag_info.last_pointer_frame = adjusted_frame;
2328 drag_info.first_move = false;
2330 show_verbose_time_cursor (adjusted_frame, 10);
2334 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2336 if (drag_info.first_move) return;
2338 meter_marker_drag_motion_callback (drag_info.item, event);
2340 MeterMarker* marker = (MeterMarker *) drag_info.data;
2343 TempoMap& map (session->tempo_map());
2344 map.bbt_time (drag_info.last_pointer_frame, when);
2346 if (drag_info.copy == true) {
2347 begin_reversible_command (_("copy meter mark"));
2348 XMLNode &before = map.get_state();
2349 map.add_meter (marker->meter(), when);
2350 XMLNode &after = map.get_state();
2351 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2352 commit_reversible_command ();
2354 // delete the dummy marker we used for visual representation of copying.
2355 // a new visual marker will show up automatically.
2358 begin_reversible_command (_("move meter mark"));
2359 XMLNode &before = map.get_state();
2360 map.move_meter (marker->meter(), when);
2361 XMLNode &after = map.get_state();
2362 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2363 commit_reversible_command ();
2368 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2371 TempoMarker* tempo_marker;
2373 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2374 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2378 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2379 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2383 MetricSection& section (tempo_marker->tempo());
2385 if (!section.movable()) {
2389 drag_info.item = item;
2390 drag_info.data = marker;
2391 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2392 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2396 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2397 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2401 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2404 TempoMarker* tempo_marker;
2406 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2407 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2411 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2412 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2416 // create a dummy marker for visual representation of moving the copy.
2417 // The actual copying is not done before we reach the finish callback.
2419 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2420 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2421 *new TempoSection(tempo_marker->tempo()));
2423 drag_info.item = &new_marker->the_item();
2424 drag_info.copy = true;
2425 drag_info.data = new_marker;
2426 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2427 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2431 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2433 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2437 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2439 TempoMarker* marker = (TempoMarker *) drag_info.data;
2440 nframes_t adjusted_frame;
2442 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2443 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2449 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2450 snap_to (adjusted_frame);
2453 if (adjusted_frame == drag_info.last_pointer_frame) return;
2455 /* OK, we've moved far enough to make it worth actually move the thing. */
2457 marker->set_position (adjusted_frame);
2459 show_verbose_time_cursor (adjusted_frame, 10);
2461 drag_info.last_pointer_frame = adjusted_frame;
2462 drag_info.first_move = false;
2466 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2468 if (drag_info.first_move) return;
2470 tempo_marker_drag_motion_callback (drag_info.item, event);
2472 TempoMarker* marker = (TempoMarker *) drag_info.data;
2475 TempoMap& map (session->tempo_map());
2476 map.bbt_time (drag_info.last_pointer_frame, when);
2478 if (drag_info.copy == true) {
2479 begin_reversible_command (_("copy tempo mark"));
2480 XMLNode &before = map.get_state();
2481 map.add_tempo (marker->tempo(), when);
2482 XMLNode &after = map.get_state();
2483 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2484 commit_reversible_command ();
2486 // delete the dummy marker we used for visual representation of copying.
2487 // a new visual marker will show up automatically.
2490 begin_reversible_command (_("move tempo mark"));
2491 XMLNode &before = map.get_state();
2492 map.move_tempo (marker->tempo(), when);
2493 XMLNode &after = map.get_state();
2494 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2495 commit_reversible_command ();
2500 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2502 ControlPoint* control_point;
2504 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2505 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2509 // We shouldn't remove the first or last gain point
2510 if (control_point->line.is_last_point(*control_point) ||
2511 control_point->line.is_first_point(*control_point)) {
2515 control_point->line.remove_point (*control_point);
2519 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2521 ControlPoint* control_point;
2523 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2524 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2528 control_point->line.remove_point (*control_point);
2532 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2534 ControlPoint* control_point;
2536 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2537 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2541 drag_info.item = item;
2542 drag_info.data = control_point;
2543 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2544 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2546 start_grab (event, fader_cursor);
2548 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2550 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2551 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2552 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2554 show_verbose_canvas_cursor ();
2558 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2560 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2562 double cx = drag_info.current_pointer_x;
2563 double cy = drag_info.current_pointer_y;
2565 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2566 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2568 if (drag_info.x_constrained) {
2569 cx = drag_info.grab_x;
2571 if (drag_info.y_constrained) {
2572 cy = drag_info.grab_y;
2575 cp->line.parent_group().w2i (cx, cy);
2579 cy = min ((double) cp->line.height(), cy);
2581 //translate cx to frames
2582 nframes_t cx_frames = unit_to_frame (cx);
2584 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2585 snap_to (cx_frames);
2588 float fraction = 1.0 - (cy / cp->line.height());
2592 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2598 cp->line.point_drag (*cp, cx_frames , fraction, push);
2600 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2602 drag_info.first_move = false;
2606 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2608 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2610 if (drag_info.first_move) {
2614 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2615 reset_point_selection ();
2619 control_point_drag_motion_callback (item, event);
2621 cp->line.end_drag (cp);
2625 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2627 switch (mouse_mode) {
2629 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2630 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2638 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2642 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2643 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2647 start_line_grab (al, event);
2651 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2655 nframes_t frame_within_region;
2657 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2661 cx = event->button.x;
2662 cy = event->button.y;
2663 line->parent_group().w2i (cx, cy);
2664 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2666 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2667 current_line_drag_info.after)) {
2668 /* no adjacent points */
2672 drag_info.item = &line->grab_item();
2673 drag_info.data = line;
2674 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2675 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2677 start_grab (event, fader_cursor);
2679 double fraction = 1.0 - (cy / line->height());
2681 line->start_drag (0, drag_info.grab_frame, fraction);
2683 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2684 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2685 show_verbose_canvas_cursor ();
2689 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2691 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2692 double cx = drag_info.current_pointer_x;
2693 double cy = drag_info.current_pointer_y;
2695 line->parent_group().w2i (cx, cy);
2698 fraction = 1.0 - (cy / line->height());
2702 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2708 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2710 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2714 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2716 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2717 line_drag_motion_callback (item, event);
2722 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2724 if (selection->regions.empty() || clicked_regionview == 0) {
2728 drag_info.copy = false;
2729 drag_info.item = item;
2730 drag_info.data = clicked_regionview;
2731 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2732 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2737 TimeAxisView* tvp = clicked_trackview;
2738 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2740 if (tv && tv->is_audio_track()) {
2741 speed = tv->get_diskstream()->speed();
2744 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2745 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2746 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2747 // we want a move threshold
2748 drag_info.want_move_threshold = true;
2750 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2752 begin_reversible_command (_("move region(s)"));
2756 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2758 if (selection->regions.empty() || clicked_regionview == 0) {
2762 drag_info.copy = true;
2763 drag_info.item = item;
2764 drag_info.data = clicked_regionview;
2768 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2769 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2772 if (atv && atv->is_audio_track()) {
2773 speed = atv->get_diskstream()->speed();
2776 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2777 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2778 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2779 // we want a move threshold
2780 drag_info.want_move_threshold = true;
2781 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2782 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2783 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2787 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2789 if (selection->regions.empty() || clicked_regionview == 0) {
2793 drag_info.copy = false;
2794 drag_info.item = item;
2795 drag_info.data = clicked_regionview;
2796 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2797 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2802 TimeAxisView* tvp = clicked_trackview;
2803 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2805 if (tv && tv->is_audio_track()) {
2806 speed = tv->get_diskstream()->speed();
2809 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2810 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2811 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2812 // we want a move threshold
2813 drag_info.want_move_threshold = true;
2814 drag_info.brushing = true;
2816 begin_reversible_command (_("Drag region brush"));
2820 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2824 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2825 nframes_t pending_region_position = 0;
2826 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2827 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2828 bool clamp_y_axis = false;
2829 vector<int32_t> height_list(512) ;
2830 vector<int32_t>::iterator j;
2832 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2834 drag_info.want_move_threshold = false; // don't copy again
2836 /* duplicate the region(s) */
2838 vector<RegionView*> new_regionviews;
2840 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2843 AudioRegionView* arv;
2848 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
2849 /* XXX handle MIDI here */
2853 nrv = new AudioRegionView (*arv);
2854 nrv->get_canvas_group()->show ();
2856 new_regionviews.push_back (nrv);
2859 if (new_regionviews.empty()) {
2863 /* reset selection to new regionviews */
2865 selection->set (new_regionviews);
2867 /* reset drag_info data to reflect the fact that we are dragging the copies */
2869 drag_info.data = new_regionviews.front();
2871 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2874 /* Which trackview is this ? */
2876 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2877 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2879 /* The region motion is only processed if the pointer is over
2883 if (!tv || !tv->is_audio_track()) {
2884 /* To make sure we hide the verbose canvas cursor when the mouse is
2885 not held over and audiotrack.
2887 hide_verbose_canvas_cursor ();
2891 original_pointer_order = drag_info.last_trackview->order;
2893 /************************************************************
2895 ************************************************************/
2897 if (drag_info.brushing) {
2898 clamp_y_axis = true;
2903 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2905 int32_t children = 0, numtracks = 0;
2906 // XXX hard coding track limit, oh my, so very very bad
2907 bitset <1024> tracks (0x00);
2908 /* get a bitmask representing the visible tracks */
2910 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2911 TimeAxisView *tracklist_timeview;
2912 tracklist_timeview = (*i);
2913 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2914 list<TimeAxisView*> children_list;
2916 /* zeroes are audio tracks. ones are other types. */
2918 if (!atv2->hidden()) {
2920 if (visible_y_high < atv2->order) {
2921 visible_y_high = atv2->order;
2923 if (visible_y_low > atv2->order) {
2924 visible_y_low = atv2->order;
2927 if (!atv2->is_audio_track()) {
2928 tracks = tracks |= (0x01 << atv2->order);
2931 height_list[atv2->order] = (*i)->height;
2933 if ((children_list = atv2->get_child_list()).size() > 0) {
2934 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2935 tracks = tracks |= (0x01 << (atv2->order + children));
2936 height_list[atv2->order + children] = (*j)->height;
2944 /* find the actual span according to the canvas */
2946 canvas_pointer_y_span = pointer_y_span;
2947 if (drag_info.last_trackview->order >= tv->order) {
2949 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2950 if (height_list[y] == 0 ) {
2951 canvas_pointer_y_span--;
2956 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2957 if ( height_list[y] == 0 ) {
2958 canvas_pointer_y_span++;
2963 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2964 RegionView* rv2 = (*i);
2965 double ix1, ix2, iy1, iy2;
2968 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2969 rv2->get_canvas_group()->i2w (ix1, iy1);
2970 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2971 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2973 if (atv2->order != original_pointer_order) {
2974 /* this isn't the pointer track */
2976 if (canvas_pointer_y_span > 0) {
2978 /* moving up the canvas */
2979 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2981 int32_t visible_tracks = 0;
2982 while (visible_tracks < canvas_pointer_y_span ) {
2985 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2986 /* we're passing through a hidden track */
2991 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2992 clamp_y_axis = true;
2996 clamp_y_axis = true;
2999 } else if (canvas_pointer_y_span < 0) {
3001 /*moving down the canvas*/
3003 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3006 int32_t visible_tracks = 0;
3008 while (visible_tracks > canvas_pointer_y_span ) {
3011 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3015 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3016 clamp_y_axis = true;
3021 clamp_y_axis = true;
3027 /* this is the pointer's track */
3028 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3029 clamp_y_axis = true;
3030 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3031 clamp_y_axis = true;
3039 } else if (drag_info.last_trackview == tv) {
3040 clamp_y_axis = true;
3044 if (!clamp_y_axis) {
3045 drag_info.last_trackview = tv;
3048 /************************************************************
3050 ************************************************************/
3052 /* compute the amount of pointer motion in frames, and where
3053 the region would be if we moved it by that much.
3056 if (drag_info.move_threshold_passed) {
3058 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3060 nframes_t sync_frame;
3061 nframes_t sync_offset;
3064 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3066 sync_offset = rv->region()->sync_offset (sync_dir);
3067 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3069 /* we snap if the snap modifier is not enabled.
3072 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3073 snap_to (sync_frame);
3076 if (sync_frame - sync_offset <= sync_frame) {
3077 pending_region_position = sync_frame - (sync_dir*sync_offset);
3079 pending_region_position = 0;
3083 pending_region_position = 0;
3086 if (pending_region_position > max_frames - rv->region()->length()) {
3087 pending_region_position = drag_info.last_frame_position;
3090 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3092 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3094 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3095 to make it appear at the new location.
3098 if (pending_region_position > drag_info.last_frame_position) {
3099 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3101 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3104 drag_info.last_frame_position = pending_region_position;
3111 /* threshold not passed */
3116 /*************************************************************
3118 ************************************************************/
3120 if (x_delta == 0 && (pointer_y_span == 0)) {
3121 /* haven't reached next snap point, and we're not switching
3122 trackviews. nothing to do.
3128 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3130 RegionView* rv2 = (*i);
3132 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3134 double ix1, ix2, iy1, iy2;
3135 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3136 rv2->get_canvas_group()->i2w (ix1, iy1);
3145 /*************************************************************
3147 ************************************************************/
3151 if (drag_info.first_move) {
3152 if (drag_info.move_threshold_passed) {
3163 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3164 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3166 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3168 RegionView* rv = (*i);
3169 double ix1, ix2, iy1, iy2;
3170 int32_t temp_pointer_y_span = pointer_y_span;
3172 /* get item BBox, which will be relative to parent. so we have
3173 to query on a child, then convert to world coordinates using
3177 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3178 rv->get_canvas_group()->i2w (ix1, iy1);
3179 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3180 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3181 AudioTimeAxisView* temp_atv;
3183 if ((pointer_y_span != 0) && !clamp_y_axis) {
3186 for (j = height_list.begin(); j!= height_list.end(); j++) {
3187 if (x == canvas_atv->order) {
3188 /* we found the track the region is on */
3189 if (x != original_pointer_order) {
3190 /*this isn't from the same track we're dragging from */
3191 temp_pointer_y_span = canvas_pointer_y_span;
3193 while (temp_pointer_y_span > 0) {
3194 /* we're moving up canvas-wise,
3195 so we need to find the next track height
3197 if (j != height_list.begin()) {
3200 if (x != original_pointer_order) {
3201 /* we're not from the dragged track, so ignore hidden tracks. */
3203 temp_pointer_y_span++;
3207 temp_pointer_y_span--;
3209 while (temp_pointer_y_span < 0) {
3211 if (x != original_pointer_order) {
3213 temp_pointer_y_span--;
3217 if (j != height_list.end()) {
3220 temp_pointer_y_span++;
3222 /* find out where we'll be when we move and set height accordingly */
3224 tvp2 = trackview_by_y_position (iy1 + y_delta);
3225 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3226 rv->set_height (temp_atv->height);
3228 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3229 personally, i think this can confuse things, but never mind.
3232 //const GdkColor& col (temp_atv->view->get_region_color());
3233 //rv->set_color (const_cast<GdkColor&>(col));
3240 /* prevent the regionview from being moved to before
3241 the zero position on the canvas.
3246 if (-x_delta > ix1) {
3249 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3250 x_delta = max_frames - rv->region()->last_frame();
3253 if (drag_info.first_move) {
3255 /* hide any dependent views */
3257 rv->get_time_axis_view().hide_dependent_views (*rv);
3259 /* this is subtle. raising the regionview itself won't help,
3260 because raise_to_top() just puts the item on the top of
3261 its parent's stack. so, we need to put the trackview canvas_display group
3262 on the top, since its parent is the whole canvas.
3265 rv->get_canvas_group()->raise_to_top();
3266 rv->get_time_axis_view().canvas_display->raise_to_top();
3267 cursor_group->raise_to_top();
3269 rv->fake_set_opaque (true);
3272 if (drag_info.brushing) {
3273 mouse_brush_insert_region (rv, pending_region_position);
3275 rv->move (x_delta, y_delta);
3278 } /* foreach region */
3282 if (drag_info.first_move && drag_info.move_threshold_passed) {
3283 cursor_group->raise_to_top();
3284 drag_info.first_move = false;
3287 if (x_delta != 0 && !drag_info.brushing) {
3288 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3293 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3296 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3297 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3298 bool nocommit = true;
3300 RouteTimeAxisView* atv;
3301 bool regionview_y_movement;
3302 bool regionview_x_movement;
3304 /* first_move is set to false if the regionview has been moved in the
3308 if (drag_info.first_move) {
3315 /* The regionview has been moved at some stage during the grab so we need
3316 to account for any mouse movement between this event and the last one.
3319 region_drag_motion_callback (item, event);
3321 if (drag_info.brushing) {
3322 /* all changes were made during motion event handlers */
3326 /* adjust for track speed */
3329 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3330 if (atv && atv->get_diskstream()) {
3331 speed = atv->get_diskstream()->speed();
3334 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3335 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3337 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3338 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3342 if (drag_info.copy) {
3343 if (drag_info.x_constrained) {
3344 op_string = _("fixed time region copy");
3346 op_string = _("region copy");
3349 if (drag_info.x_constrained) {
3350 op_string = _("fixed time region drag");
3352 op_string = _("region drag");
3356 begin_reversible_command (op_string);
3358 if (regionview_y_movement) {
3360 /* moved to a different audio track. */
3362 list<RegionView*> new_selection;
3363 new_selection = selection->regions.by_layer();
3364 selection->clear_regions ();
3366 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
3368 RegionView* rv = (*i);
3370 double ix1, ix2, iy1, iy2;
3372 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3373 rv->get_canvas_group()->i2w (ix1, iy1);
3374 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3375 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3377 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3378 boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
3380 where = (nframes_t) (unit_to_frame (ix1) * speed);
3381 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3383 if (!drag_info.copy) {
3385 /* the region that used to be in the old playlist is not
3386 moved to the new one - we make a copy of it. as a result,
3387 any existing editor for the region should no longer be
3391 rv->hide_region_editor();
3392 rv->fake_set_opaque (false);
3394 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3395 from_playlist->remove_region ((rv->region()));
3396 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3399 latest_regionview = 0;
3401 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3402 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3403 to_playlist->add_region (new_region, where);
3404 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3407 if (latest_regionview) {
3408 selection->add (latest_regionview);
3411 if (drag_info.copy) {
3412 // get rid of the copy
3419 /* motion within a single track */
3421 list<RegionView*> regions = selection->regions.by_layer();
3423 if (drag_info.copy) {
3424 selection->clear_regions();
3427 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3431 if (rv->region()->locked()) {
3436 if (regionview_x_movement) {
3437 double ownspeed = 1.0;
3438 atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3440 if (atv && atv->get_diskstream()) {
3441 ownspeed = atv->get_diskstream()->speed();
3444 /* base the new region position on the current position of the regionview.*/
3446 double ix1, ix2, iy1, iy2;
3448 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3449 rv->get_canvas_group()->i2w (ix1, iy1);
3450 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3454 where = rv->region()->position();
3457 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3461 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3463 if (drag_info.copy) {
3465 boost::shared_ptr<Region> newregion;
3466 boost::shared_ptr<Region> ar;
3468 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3469 newregion = RegionFactory::create (ar);
3471 /* XXX MIDI HERE drobilla */
3477 latest_regionview = 0;
3478 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3479 to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
3482 if (latest_regionview) {
3483 atv->reveal_dependent_views (*latest_regionview);
3484 selection->add (latest_regionview);
3487 /* if the original region was locked, we don't care for the new one */
3489 newregion->set_locked (false);
3493 /* just change the model */
3495 rv->region()->set_position (where, (void*) this);
3501 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3503 /* get rid of the copy */
3505 if (drag_info.copy) {
3514 commit_reversible_command ();
3519 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3521 /* Either add to or set the set the region selection, unless
3522 this is an alignment click (control used)
3525 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3526 TimeAxisView* tv = &rv.get_time_axis_view();
3527 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3529 if (atv && atv->is_audio_track()) {
3530 speed = atv->get_diskstream()->speed();
3533 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3535 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3537 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3539 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3543 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3549 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3555 nframes_t frame_rate;
3562 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3563 case AudioClock::BBT:
3564 session->bbt_time (frame, bbt);
3565 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3568 case AudioClock::SMPTE:
3569 session->smpte_time (frame, smpte);
3570 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3573 case AudioClock::MinSec:
3574 /* XXX this is copied from show_verbose_duration_cursor() */
3575 frame_rate = session->frame_rate();
3576 hours = frame / (frame_rate * 3600);
3577 frame = frame % (frame_rate * 3600);
3578 mins = frame / (frame_rate * 60);
3579 frame = frame % (frame_rate * 60);
3580 secs = (float) frame / (float) frame_rate;
3581 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3585 snprintf (buf, sizeof(buf), "%u", frame);
3589 if (xpos >= 0 && ypos >=0) {
3590 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3593 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3595 show_verbose_canvas_cursor ();
3599 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3606 nframes_t distance, frame_rate;
3608 Meter meter_at_start(session->tempo_map().meter_at(start));
3614 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3615 case AudioClock::BBT:
3616 session->bbt_time (start, sbbt);
3617 session->bbt_time (end, ebbt);
3620 /* XXX this computation won't work well if the
3621 user makes a selection that spans any meter changes.
3624 ebbt.bars -= sbbt.bars;
3625 if (ebbt.beats >= sbbt.beats) {
3626 ebbt.beats -= sbbt.beats;
3629 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3631 if (ebbt.ticks >= sbbt.ticks) {
3632 ebbt.ticks -= sbbt.ticks;
3635 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3638 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3641 case AudioClock::SMPTE:
3642 session->smpte_duration (end - start, smpte);
3643 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3646 case AudioClock::MinSec:
3647 /* XXX this stuff should be elsewhere.. */
3648 distance = end - start;
3649 frame_rate = session->frame_rate();
3650 hours = distance / (frame_rate * 3600);
3651 distance = distance % (frame_rate * 3600);
3652 mins = distance / (frame_rate * 60);
3653 distance = distance % (frame_rate * 60);
3654 secs = (float) distance / (float) frame_rate;
3655 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3659 snprintf (buf, sizeof(buf), "%u", end - start);
3663 if (xpos >= 0 && ypos >=0) {
3664 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3667 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3669 show_verbose_canvas_cursor ();
3673 Editor::collect_new_region_view (RegionView* rv)
3675 latest_regionview = rv;
3679 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3681 if (clicked_regionview == 0) {
3685 /* lets try to create new Region for the selection */
3687 vector<boost::shared_ptr<AudioRegion> > new_regions;
3688 create_region_from_selection (new_regions);
3690 if (new_regions.empty()) {
3694 /* XXX fix me one day to use all new regions */
3696 boost::shared_ptr<Region> region (new_regions.front());
3698 /* add it to the current stream/playlist.
3700 tricky: the streamview for the track will add a new regionview. we will
3701 catch the signal it sends when it creates the regionview to
3702 set the regionview we want to then drag.
3705 latest_regionview = 0;
3706 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3708 /* A selection grab currently creates two undo/redo operations, one for
3709 creating the new region and another for moving it.
3712 begin_reversible_command (_("selection grab"));
3714 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3716 XMLNode *before = &(playlist->get_state());
3717 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3718 XMLNode *after = &(playlist->get_state());
3719 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3721 commit_reversible_command ();
3725 if (latest_regionview == 0) {
3726 /* something went wrong */
3730 /* we need to deselect all other regionviews, and select this one
3731 i'm ignoring undo stuff, because the region creation will take care of it */
3732 selection->set (latest_regionview);
3734 drag_info.item = latest_regionview->get_canvas_group();
3735 drag_info.data = latest_regionview;
3736 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3737 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3741 drag_info.last_trackview = clicked_trackview;
3742 drag_info.last_frame_position = latest_regionview->region()->position();
3743 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3745 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3749 Editor::cancel_selection ()
3751 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3752 (*i)->hide_selection ();
3754 begin_reversible_command (_("cancel selection"));
3755 selection->clear ();
3756 clicked_selection = 0;
3757 commit_reversible_command ();
3761 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3763 nframes_t start = 0;
3770 drag_info.item = item;
3771 drag_info.motion_callback = &Editor::drag_selection;
3772 drag_info.finished_callback = &Editor::end_selection_op;
3777 case CreateSelection:
3778 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3779 drag_info.copy = true;
3781 drag_info.copy = false;
3783 start_grab (event, selector_cursor);
3786 case SelectionStartTrim:
3787 if (clicked_trackview) {
3788 clicked_trackview->order_selection_trims (item, true);
3790 start_grab (event, trimmer_cursor);
3791 start = selection->time[clicked_selection].start;
3792 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3795 case SelectionEndTrim:
3796 if (clicked_trackview) {
3797 clicked_trackview->order_selection_trims (item, false);
3799 start_grab (event, trimmer_cursor);
3800 end = selection->time[clicked_selection].end;
3801 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3805 start = selection->time[clicked_selection].start;
3807 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3811 if (selection_op == SelectionMove) {
3812 show_verbose_time_cursor(start, 10);
3814 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3819 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3821 nframes_t start = 0;
3824 nframes_t pending_position;
3826 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3827 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3830 pending_position = 0;
3833 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3834 snap_to (pending_position);
3837 /* only alter selection if the current frame is
3838 different from the last frame position (adjusted)
3841 if (pending_position == drag_info.last_pointer_frame) return;
3843 switch (selection_op) {
3844 case CreateSelection:
3846 if (drag_info.first_move) {
3847 snap_to (drag_info.grab_frame);
3850 if (pending_position < drag_info.grab_frame) {
3851 start = pending_position;
3852 end = drag_info.grab_frame;
3854 end = pending_position;
3855 start = drag_info.grab_frame;
3858 /* first drag: Either add to the selection
3859 or create a new selection->
3862 if (drag_info.first_move) {
3864 begin_reversible_command (_("range selection"));
3866 if (drag_info.copy) {
3867 /* adding to the selection */
3868 clicked_selection = selection->add (start, end);
3869 drag_info.copy = false;
3871 /* new selection-> */
3872 clicked_selection = selection->set (clicked_trackview, start, end);
3877 case SelectionStartTrim:
3879 if (drag_info.first_move) {
3880 begin_reversible_command (_("trim selection start"));
3883 start = selection->time[clicked_selection].start;
3884 end = selection->time[clicked_selection].end;
3886 if (pending_position > end) {
3889 start = pending_position;
3893 case SelectionEndTrim:
3895 if (drag_info.first_move) {
3896 begin_reversible_command (_("trim selection end"));
3899 start = selection->time[clicked_selection].start;
3900 end = selection->time[clicked_selection].end;
3902 if (pending_position < start) {
3905 end = pending_position;
3912 if (drag_info.first_move) {
3913 begin_reversible_command (_("move selection"));
3916 start = selection->time[clicked_selection].start;
3917 end = selection->time[clicked_selection].end;
3919 length = end - start;
3921 start = pending_position;
3924 end = start + length;
3929 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3930 start_canvas_autoscroll (1);
3934 selection->replace (clicked_selection, start, end);
3937 drag_info.last_pointer_frame = pending_position;
3938 drag_info.first_move = false;
3940 if (selection_op == SelectionMove) {
3941 show_verbose_time_cursor(start, 10);
3943 show_verbose_time_cursor(pending_position, 10);
3948 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3950 if (!drag_info.first_move) {
3951 drag_selection (item, event);
3952 /* XXX this is not object-oriented programming at all. ick */
3953 if (selection->time.consolidate()) {
3954 selection->TimeChanged ();
3956 commit_reversible_command ();
3958 /* just a click, no pointer movement.*/
3960 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3962 selection->clear_time();
3967 /* XXX what happens if its a music selection? */
3968 session->set_audio_range (selection->time);
3969 stop_canvas_autoscroll ();
3973 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3976 TimeAxisView* tvp = clicked_trackview;
3977 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3979 if (tv && tv->is_audio_track()) {
3980 speed = tv->get_diskstream()->speed();
3983 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3984 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3985 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3987 //drag_info.item = clicked_regionview->get_name_highlight();
3988 drag_info.item = item;
3989 drag_info.motion_callback = &Editor::trim_motion_callback;
3990 drag_info.finished_callback = &Editor::trim_finished_callback;
3992 start_grab (event, trimmer_cursor);
3994 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3995 trim_op = ContentsTrim;
3997 /* These will get overridden for a point trim.*/
3998 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3999 /* closer to start */
4000 trim_op = StartTrim;
4001 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4009 show_verbose_time_cursor(region_start, 10);
4012 show_verbose_time_cursor(region_end, 10);
4015 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4021 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4023 RegionView* rv = clicked_regionview;
4024 nframes_t frame_delta = 0;
4025 bool left_direction;
4026 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4028 /* snap modifier works differently here..
4029 its' current state has to be passed to the
4030 various trim functions in order to work properly
4034 TimeAxisView* tvp = clicked_trackview;
4035 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4036 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4038 if (tv && tv->is_audio_track()) {
4039 speed = tv->get_diskstream()->speed();
4042 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4043 left_direction = true;
4045 left_direction = false;
4049 snap_to (drag_info.current_pointer_frame);
4052 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4056 if (drag_info.first_move) {
4062 trim_type = "Region start trim";
4065 trim_type = "Region end trim";
4068 trim_type = "Region content trim";
4072 begin_reversible_command (trim_type);
4074 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4075 (*i)->fake_set_opaque(false);
4076 (*i)->region()->freeze ();
4078 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4080 arv->temporarily_hide_envelope ();
4082 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4083 insert_result = motion_frozen_playlists.insert (pl);
4084 if (insert_result.second) {
4085 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4090 if (left_direction) {
4091 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4093 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4098 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4101 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4102 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4108 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4111 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4112 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4119 bool swap_direction = false;
4121 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4122 swap_direction = true;
4125 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4126 i != selection->regions.by_layer().end(); ++i)
4128 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4136 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4139 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4142 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4146 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4147 drag_info.first_move = false;
4151 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4153 boost::shared_ptr<Region> region (rv.region());
4155 if (region->locked()) {
4159 nframes_t new_bound;
4162 TimeAxisView* tvp = clicked_trackview;
4163 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4165 if (tv && tv->is_audio_track()) {
4166 speed = tv->get_diskstream()->speed();
4169 if (left_direction) {
4170 if (swap_direction) {
4171 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4173 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4176 if (swap_direction) {
4177 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4179 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4184 snap_to (new_bound);
4186 region->trim_start ((nframes_t) (new_bound * speed), this);
4187 rv.region_changed (StartChanged);
4191 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4193 boost::shared_ptr<Region> region (rv.region());
4195 if (region->locked()) {
4199 nframes_t new_bound;
4202 TimeAxisView* tvp = clicked_trackview;
4203 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4205 if (tv && tv->is_audio_track()) {
4206 speed = tv->get_diskstream()->speed();
4209 if (left_direction) {
4210 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4212 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4216 snap_to (new_bound, (left_direction ? 0 : 1));
4219 region->trim_front ((nframes_t) (new_bound * speed), this);
4221 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4225 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4227 boost::shared_ptr<Region> region (rv.region());
4229 if (region->locked()) {
4233 nframes_t new_bound;
4236 TimeAxisView* tvp = clicked_trackview;
4237 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4239 if (tv && tv->is_audio_track()) {
4240 speed = tv->get_diskstream()->speed();
4243 if (left_direction) {
4244 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4246 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4250 snap_to (new_bound);
4252 region->trim_end ((nframes_t) (new_bound * speed), this);
4253 rv.region_changed (LengthChanged);
4257 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4259 if (!drag_info.first_move) {
4260 trim_motion_callback (item, event);
4262 if (!clicked_regionview->get_selected()) {
4263 thaw_region_after_trim (*clicked_regionview);
4266 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4267 i != selection->regions.by_layer().end(); ++i)
4269 thaw_region_after_trim (**i);
4270 (*i)->fake_set_opaque (true);
4274 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4276 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4279 motion_frozen_playlists.clear ();
4281 commit_reversible_command();
4283 /* no mouse movement */
4289 Editor::point_trim (GdkEvent* event)
4291 RegionView* rv = clicked_regionview;
4292 nframes_t new_bound = drag_info.current_pointer_frame;
4294 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4295 snap_to (new_bound);
4298 /* Choose action dependant on which button was pressed */
4299 switch (event->button.button) {
4301 trim_op = StartTrim;
4302 begin_reversible_command (_("Start point trim"));
4304 if (rv->get_selected()) {
4306 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4307 i != selection->regions.by_layer().end(); ++i)
4309 if (!(*i)->region()->locked()) {
4310 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4311 XMLNode &before = pl->get_state();
4312 (*i)->region()->trim_front (new_bound, this);
4313 XMLNode &after = pl->get_state();
4314 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4320 if (!rv->region()->locked()) {
4321 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4322 XMLNode &before = pl->get_state();
4323 rv->region()->trim_front (new_bound, this);
4324 XMLNode &after = pl->get_state();
4325 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4329 commit_reversible_command();
4334 begin_reversible_command (_("End point trim"));
4336 if (rv->get_selected()) {
4338 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4340 if (!(*i)->region()->locked()) {
4341 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4342 XMLNode &before = pl->get_state();
4343 (*i)->region()->trim_end (new_bound, this);
4344 XMLNode &after = pl->get_state();
4345 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4351 if (!rv->region()->locked()) {
4352 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4353 XMLNode &before = pl->get_state();
4354 rv->region()->trim_end (new_bound, this);
4355 XMLNode &after = pl->get_state();
4356 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4360 commit_reversible_command();
4369 Editor::thaw_region_after_trim (RegionView& rv)
4371 boost::shared_ptr<Region> region (rv.region());
4373 if (region->locked()) {
4377 region->thaw (_("trimmed region"));
4378 XMLNode &after = region->playlist()->get_state();
4379 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4381 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4383 arv->unhide_envelope ();
4387 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4392 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4393 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4397 Location* location = find_location_from_marker (marker, is_start);
4398 location->set_hidden (true, this);
4403 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4409 drag_info.item = item;
4410 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4411 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4413 range_marker_op = op;
4415 if (!temp_location) {
4416 temp_location = new Location;
4420 case CreateRangeMarker:
4421 case CreateTransportMarker:
4423 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4424 drag_info.copy = true;
4426 drag_info.copy = false;
4428 start_grab (event, selector_cursor);
4432 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4437 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4439 nframes_t start = 0;
4441 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4443 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4444 snap_to (drag_info.current_pointer_frame);
4447 /* only alter selection if the current frame is
4448 different from the last frame position.
4451 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4453 switch (range_marker_op) {
4454 case CreateRangeMarker:
4455 case CreateTransportMarker:
4456 if (drag_info.first_move) {
4457 snap_to (drag_info.grab_frame);
4460 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4461 start = drag_info.current_pointer_frame;
4462 end = drag_info.grab_frame;
4464 end = drag_info.current_pointer_frame;
4465 start = drag_info.grab_frame;
4468 /* first drag: Either add to the selection
4469 or create a new selection.
4472 if (drag_info.first_move) {
4474 temp_location->set (start, end);
4478 update_marker_drag_item (temp_location);
4479 range_marker_drag_rect->show();
4480 range_marker_drag_rect->raise_to_top();
4486 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4487 start_canvas_autoscroll (1);
4491 temp_location->set (start, end);
4493 double x1 = frame_to_pixel (start);
4494 double x2 = frame_to_pixel (end);
4495 crect->property_x1() = x1;
4496 crect->property_x2() = x2;
4498 update_marker_drag_item (temp_location);
4501 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4502 drag_info.first_move = false;
4504 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4509 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4511 Location * newloc = 0;
4514 if (!drag_info.first_move) {
4515 drag_range_markerbar_op (item, event);
4517 switch (range_marker_op) {
4518 case CreateRangeMarker:
4520 begin_reversible_command (_("new range marker"));
4521 XMLNode &before = session->locations()->get_state();
4522 session->locations()->next_available_name(rangename,"unnamed");
4523 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4524 session->locations()->add (newloc, true);
4525 XMLNode &after = session->locations()->get_state();
4526 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4527 commit_reversible_command ();
4529 range_bar_drag_rect->hide();
4530 range_marker_drag_rect->hide();
4534 case CreateTransportMarker:
4535 // popup menu to pick loop or punch
4536 new_transport_marker_context_menu (&event->button, item);
4541 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4543 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4548 start = session->locations()->first_mark_before (drag_info.grab_frame);
4549 end = session->locations()->first_mark_after (drag_info.grab_frame);
4551 if (end == max_frames) {
4552 end = session->current_end_frame ();
4556 start = session->current_start_frame ();
4559 switch (mouse_mode) {
4561 /* find the two markers on either side and then make the selection from it */
4562 cerr << "select between " << start << " .. " << end << endl;
4563 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4567 /* find the two markers on either side of the click and make the range out of it */
4568 selection->set (0, start, end);
4577 stop_canvas_autoscroll ();
4583 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4585 drag_info.item = item;
4586 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4587 drag_info.finished_callback = &Editor::end_mouse_zoom;
4589 start_grab (event, zoom_cursor);
4591 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4595 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4600 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4601 snap_to (drag_info.current_pointer_frame);
4603 if (drag_info.first_move) {
4604 snap_to (drag_info.grab_frame);
4608 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4610 /* base start and end on initial click position */
4611 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4612 start = drag_info.current_pointer_frame;
4613 end = drag_info.grab_frame;
4615 end = drag_info.current_pointer_frame;
4616 start = drag_info.grab_frame;
4621 if (drag_info.first_move) {
4623 zoom_rect->raise_to_top();
4626 reposition_zoom_rect(start, end);
4628 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4629 drag_info.first_move = false;
4631 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4636 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4638 if (!drag_info.first_move) {
4639 drag_mouse_zoom (item, event);
4641 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4642 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4644 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4647 temporal_zoom_to_frame (false, drag_info.grab_frame);
4649 temporal_zoom_step (false);
4650 center_screen (drag_info.grab_frame);
4658 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4660 double x1 = frame_to_pixel (start);
4661 double x2 = frame_to_pixel (end);
4662 double y2 = full_canvas_height - 1.0;
4664 zoom_rect->property_x1() = x1;
4665 zoom_rect->property_y1() = 1.0;
4666 zoom_rect->property_x2() = x2;
4667 zoom_rect->property_y2() = y2;
4671 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4673 drag_info.item = item;
4674 drag_info.motion_callback = &Editor::drag_rubberband_select;
4675 drag_info.finished_callback = &Editor::end_rubberband_select;
4677 start_grab (event, cross_hair_cursor);
4679 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4683 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4690 /* use a bigger drag threshold than the default */
4692 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4696 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4697 if (drag_info.first_move) {
4698 snap_to (drag_info.grab_frame);
4700 snap_to (drag_info.current_pointer_frame);
4703 /* base start and end on initial click position */
4705 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4706 start = drag_info.current_pointer_frame;
4707 end = drag_info.grab_frame;
4709 end = drag_info.current_pointer_frame;
4710 start = drag_info.grab_frame;
4713 if (drag_info.current_pointer_y < drag_info.grab_y) {
4714 y1 = drag_info.current_pointer_y;
4715 y2 = drag_info.grab_y;
4717 y2 = drag_info.current_pointer_y;
4718 y1 = drag_info.grab_y;
4722 if (start != end || y1 != y2) {
4724 double x1 = frame_to_pixel (start);
4725 double x2 = frame_to_pixel (end);
4727 rubberband_rect->property_x1() = x1;
4728 rubberband_rect->property_y1() = y1;
4729 rubberband_rect->property_x2() = x2;
4730 rubberband_rect->property_y2() = y2;
4732 rubberband_rect->show();
4733 rubberband_rect->raise_to_top();
4735 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4736 drag_info.first_move = false;
4738 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4743 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4745 if (!drag_info.first_move) {
4747 drag_rubberband_select (item, event);
4750 if (drag_info.current_pointer_y < drag_info.grab_y) {
4751 y1 = drag_info.current_pointer_y;
4752 y2 = drag_info.grab_y;
4755 y2 = drag_info.current_pointer_y;
4756 y1 = drag_info.grab_y;
4760 Selection::Operation op = Keyboard::selection_type (event->button.state);
4763 begin_reversible_command (_("rubberband selection"));
4765 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4766 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4768 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4772 commit_reversible_command ();
4776 selection->clear_tracks();
4777 selection->clear_regions();
4778 selection->clear_points ();
4779 selection->clear_lines ();
4782 rubberband_rect->hide();
4787 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4789 using namespace Gtkmm2ext;
4791 ArdourPrompter prompter (false);
4793 prompter.set_prompt (_("Name for region:"));
4794 prompter.set_initial_text (clicked_regionview->region()->name());
4795 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4796 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4797 prompter.show_all ();
4798 switch (prompter.run ()) {
4799 case Gtk::RESPONSE_ACCEPT:
4801 prompter.get_result(str);
4803 clicked_regionview->region()->set_name (str);
4811 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4813 drag_info.item = item;
4814 drag_info.motion_callback = &Editor::time_fx_motion;
4815 drag_info.finished_callback = &Editor::end_time_fx;
4819 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4823 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4825 RegionView* rv = clicked_regionview;
4827 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4828 snap_to (drag_info.current_pointer_frame);
4831 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4835 if (drag_info.current_pointer_frame > rv->region()->position()) {
4836 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4839 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4840 drag_info.first_move = false;
4842 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4846 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4848 clicked_regionview->get_time_axis_view().hide_timestretch ();
4850 if (drag_info.first_move) {
4854 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4855 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4857 begin_reversible_command (_("timestretch"));
4859 if (run_timestretch (selection->regions, percentage) == 0) {
4860 session->commit_reversible_command ();
4865 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4867 /* no brushing without a useful snap setting */
4870 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4873 switch (snap_mode) {
4875 return; /* can't work because it allows region to be placed anywhere */
4880 switch (snap_type) {
4883 case SnapToEditCursor:
4890 /* don't brush a copy over the original */
4892 if (pos == rv->region()->position()) {
4896 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4898 if (atv == 0 || !atv->is_audio_track()) {
4902 boost::shared_ptr<Playlist> playlist = atv->playlist();
4903 double speed = atv->get_diskstream()->speed();
4905 XMLNode &before = playlist->get_state();
4906 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4907 XMLNode &after = playlist->get_state();
4908 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4910 // playlist is frozen, so we have to update manually
4912 playlist->Modified(); /* EMIT SIGNAL */
4916 Editor::track_height_step_timeout ()
4919 struct timeval delta;
4921 gettimeofday (&now, 0);
4922 timersub (&now, &last_track_height_step_timestamp, &delta);
4924 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4925 current_stepping_trackview = 0;