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 drag_info.move_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1525 if (drag_info.copy && !drag_info.move_threshold_passed) {
1526 drag_info.move_threshold_passed = (abs ((int) (drag_info.current_pointer_y - drag_info.grab_y)) > 4);
1529 // and change the initial grab loc/frame if this drag info wants us to
1531 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1532 drag_info.grab_frame = drag_info.current_pointer_frame;
1533 drag_info.grab_x = drag_info.current_pointer_x;
1534 drag_info.grab_y = drag_info.current_pointer_y;
1535 drag_info.last_pointer_frame = drag_info.grab_frame;
1536 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1541 switch (item_type) {
1542 case PlayheadCursorItem:
1543 case EditCursorItem:
1545 case GainControlPointItem:
1546 case RedirectAutomationControlPointItem:
1547 case GainAutomationControlPointItem:
1548 case PanAutomationControlPointItem:
1549 case TempoMarkerItem:
1550 case MeterMarkerItem:
1551 case RegionViewNameHighlight:
1552 case StartSelectionTrimItem:
1553 case EndSelectionTrimItem:
1556 case RedirectAutomationLineItem:
1557 case GainAutomationLineItem:
1558 case PanAutomationLineItem:
1559 case FadeInHandleItem:
1560 case FadeOutHandleItem:
1561 /* <CMT Additions> */
1562 case ImageFrameHandleStartItem:
1563 case ImageFrameHandleEndItem:
1564 case MarkerViewHandleStartItem:
1565 case MarkerViewHandleEndItem:
1566 /* </CMT Additions> */
1567 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1568 (event->motion.state & Gdk::BUTTON2_MASK))) {
1569 if (!from_autoscroll) {
1570 maybe_autoscroll (event);
1572 (this->*(drag_info.motion_callback)) (item, event);
1581 switch (mouse_mode) {
1586 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1587 (event->motion.state & GDK_BUTTON2_MASK))) {
1588 if (!from_autoscroll) {
1589 maybe_autoscroll (event);
1591 (this->*(drag_info.motion_callback)) (item, event);
1602 track_canvas_motion (event);
1603 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1611 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1613 if (drag_info.item == 0) {
1614 fatal << _("programming error: start_grab called without drag item") << endmsg;
1620 cursor = grabber_cursor;
1623 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1625 if (event->button.button == 2) {
1626 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1627 drag_info.y_constrained = true;
1628 drag_info.x_constrained = false;
1630 drag_info.y_constrained = false;
1631 drag_info.x_constrained = true;
1634 drag_info.x_constrained = false;
1635 drag_info.y_constrained = false;
1638 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1639 drag_info.last_pointer_frame = drag_info.grab_frame;
1640 drag_info.current_pointer_frame = drag_info.grab_frame;
1641 drag_info.current_pointer_x = drag_info.grab_x;
1642 drag_info.current_pointer_y = drag_info.grab_y;
1643 drag_info.cumulative_x_drag = 0;
1644 drag_info.cumulative_y_drag = 0;
1645 drag_info.first_move = true;
1646 drag_info.move_threshold_passed = false;
1647 drag_info.want_move_threshold = false;
1648 drag_info.pointer_frame_offset = 0;
1649 drag_info.brushing = false;
1650 drag_info.copied_location = 0;
1652 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1654 event->button.time);
1656 if (session && session->transport_rolling()) {
1657 drag_info.was_rolling = true;
1659 drag_info.was_rolling = false;
1662 switch (snap_type) {
1663 case SnapToRegionStart:
1664 case SnapToRegionEnd:
1665 case SnapToRegionSync:
1666 case SnapToRegionBoundary:
1667 build_region_boundary_cache ();
1675 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1677 drag_info.item->ungrab (0);
1678 drag_info.item = new_item;
1681 cursor = grabber_cursor;
1684 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1688 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1690 bool did_drag = false;
1692 stop_canvas_autoscroll ();
1694 if (drag_info.item == 0) {
1698 drag_info.item->ungrab (event->button.time);
1700 if (drag_info.finished_callback) {
1701 (this->*(drag_info.finished_callback)) (item, event);
1704 did_drag = !drag_info.first_move;
1706 hide_verbose_canvas_cursor();
1709 drag_info.copy = false;
1710 drag_info.motion_callback = 0;
1711 drag_info.finished_callback = 0;
1712 drag_info.last_trackview = 0;
1713 drag_info.last_frame_position = 0;
1714 drag_info.grab_frame = 0;
1715 drag_info.last_pointer_frame = 0;
1716 drag_info.current_pointer_frame = 0;
1717 drag_info.brushing = false;
1719 if (drag_info.copied_location) {
1720 delete drag_info.copied_location;
1721 drag_info.copied_location = 0;
1728 Editor::set_edit_cursor (GdkEvent* event)
1730 nframes_t pointer_frame = event_frame (event);
1732 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1733 if (snap_type != SnapToEditCursor) {
1734 snap_to (pointer_frame);
1738 edit_cursor->set_position (pointer_frame);
1739 edit_cursor_clock.set (pointer_frame);
1743 Editor::set_playhead_cursor (GdkEvent* event)
1745 nframes_t pointer_frame = event_frame (event);
1747 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1748 snap_to (pointer_frame);
1752 session->request_locate (pointer_frame, session->transport_rolling());
1757 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1759 drag_info.item = item;
1760 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1761 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1765 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1766 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1770 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1772 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1776 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1778 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1780 nframes_t fade_length;
1782 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1783 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1789 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1793 if (pos < (arv->region()->position() + 64)) {
1794 fade_length = 64; // this should be a minimum defined somewhere
1795 } else if (pos > arv->region()->last_frame()) {
1796 fade_length = arv->region()->length();
1798 fade_length = pos - arv->region()->position();
1800 /* mapover the region selection */
1802 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1804 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1810 tmp->reset_fade_in_shape_width (fade_length);
1813 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1815 drag_info.first_move = false;
1819 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1821 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1823 nframes_t fade_length;
1825 if (drag_info.first_move) return;
1827 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1828 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1833 if (pos < (arv->region()->position() + 64)) {
1834 fade_length = 64; // this should be a minimum defined somewhere
1835 } else if (pos > arv->region()->last_frame()) {
1836 fade_length = arv->region()->length();
1838 fade_length = pos - arv->region()->position();
1841 begin_reversible_command (_("change fade in length"));
1843 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1845 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1851 AutomationList& alist = tmp->audio_region()->fade_in();
1852 XMLNode &before = alist.get_state();
1854 tmp->audio_region()->set_fade_in_length (fade_length);
1856 XMLNode &after = alist.get_state();
1857 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1860 commit_reversible_command ();
1864 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1866 drag_info.item = item;
1867 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1868 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1872 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1873 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1877 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1879 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1883 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1885 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1887 nframes_t fade_length;
1889 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1890 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1896 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1900 if (pos > (arv->region()->last_frame() - 64)) {
1901 fade_length = 64; // this should really be a minimum fade defined somewhere
1903 else if (pos < arv->region()->position()) {
1904 fade_length = arv->region()->length();
1907 fade_length = arv->region()->last_frame() - pos;
1910 /* mapover the region selection */
1912 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1914 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1920 tmp->reset_fade_out_shape_width (fade_length);
1923 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1925 drag_info.first_move = false;
1929 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1931 if (drag_info.first_move) return;
1933 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1935 nframes_t fade_length;
1937 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1938 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1944 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1948 if (pos > (arv->region()->last_frame() - 64)) {
1949 fade_length = 64; // this should really be a minimum fade defined somewhere
1951 else if (pos < arv->region()->position()) {
1952 fade_length = arv->region()->length();
1955 fade_length = arv->region()->last_frame() - pos;
1958 begin_reversible_command (_("change fade out length"));
1960 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1962 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1968 AutomationList& alist = tmp->audio_region()->fade_out();
1969 XMLNode &before = alist.get_state();
1971 tmp->audio_region()->set_fade_out_length (fade_length);
1973 XMLNode &after = alist.get_state();
1974 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1977 commit_reversible_command ();
1981 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1983 drag_info.item = item;
1984 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1985 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1989 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1990 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1994 Cursor* cursor = (Cursor *) drag_info.data;
1996 if (cursor == playhead_cursor) {
1997 _dragging_playhead = true;
1999 if (session && drag_info.was_rolling) {
2000 session->request_stop ();
2004 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2006 show_verbose_time_cursor (cursor->current_frame, 10);
2010 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2012 Cursor* cursor = (Cursor *) drag_info.data;
2013 nframes_t adjusted_frame;
2015 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2016 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2022 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2023 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
2024 snap_to (adjusted_frame);
2028 if (adjusted_frame == drag_info.last_pointer_frame) return;
2030 cursor->set_position (adjusted_frame);
2032 if (cursor == edit_cursor) {
2033 edit_cursor_clock.set (cursor->current_frame);
2035 UpdateAllTransportClocks (cursor->current_frame);
2038 show_verbose_time_cursor (cursor->current_frame, 10);
2040 drag_info.last_pointer_frame = adjusted_frame;
2041 drag_info.first_move = false;
2045 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2047 if (drag_info.first_move) return;
2049 cursor_drag_motion_callback (item, event);
2051 _dragging_playhead = false;
2053 if (item == &playhead_cursor->canvas_item) {
2055 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2057 } else if (item == &edit_cursor->canvas_item) {
2058 edit_cursor->set_position (edit_cursor->current_frame);
2059 edit_cursor_clock.set (edit_cursor->current_frame);
2064 Editor::update_marker_drag_item (Location *location)
2066 double x1 = frame_to_pixel (location->start());
2067 double x2 = frame_to_pixel (location->end());
2069 if (location->is_mark()) {
2070 marker_drag_line_points.front().set_x(x1);
2071 marker_drag_line_points.back().set_x(x1);
2072 marker_drag_line->property_points() = marker_drag_line_points;
2075 range_marker_drag_rect->property_x1() = x1;
2076 range_marker_drag_rect->property_x2() = x2;
2081 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2085 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2086 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2092 Location *location = find_location_from_marker (marker, is_start);
2094 drag_info.item = item;
2095 drag_info.data = marker;
2096 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2097 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2101 drag_info.copied_location = new Location (*location);
2102 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2104 update_marker_drag_item (location);
2106 if (location->is_mark()) {
2107 marker_drag_line->show();
2108 marker_drag_line->raise_to_top();
2111 range_marker_drag_rect->show();
2112 range_marker_drag_rect->raise_to_top();
2115 if (is_start) show_verbose_time_cursor (location->start(), 10);
2116 else show_verbose_time_cursor (location->end(), 10);
2120 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2123 Marker* marker = (Marker *) drag_info.data;
2124 Location *real_location;
2125 Location *copy_location;
2127 bool move_both = false;
2131 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2132 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2138 nframes_t next = newframe;
2140 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2141 snap_to (newframe, 0, true);
2144 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2148 /* call this to find out if its the start or end */
2150 real_location = find_location_from_marker (marker, is_start);
2152 /* use the copy that we're "dragging" around */
2154 copy_location = drag_info.copied_location;
2156 f_delta = copy_location->end() - copy_location->start();
2158 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2162 if (copy_location->is_mark()) {
2165 copy_location->set_start (newframe);
2169 if (is_start) { // start-of-range marker
2172 copy_location->set_start (newframe);
2173 copy_location->set_end (newframe + f_delta);
2174 } else if (newframe < copy_location->end()) {
2175 copy_location->set_start (newframe);
2177 snap_to (next, 1, true);
2178 copy_location->set_end (next);
2179 copy_location->set_start (newframe);
2182 } else { // end marker
2185 copy_location->set_end (newframe);
2186 copy_location->set_start (newframe - f_delta);
2187 } else if (newframe > copy_location->start()) {
2188 copy_location->set_end (newframe);
2190 } else if (newframe > 0) {
2191 snap_to (next, -1, true);
2192 copy_location->set_start (next);
2193 copy_location->set_end (newframe);
2198 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2199 drag_info.first_move = false;
2201 update_marker_drag_item (copy_location);
2203 LocationMarkers* lm = find_location_markers (real_location);
2204 lm->set_position (copy_location->start(), copy_location->end());
2206 show_verbose_time_cursor (newframe, 10);
2210 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2212 if (drag_info.first_move) {
2213 marker_drag_motion_callback (item, event);
2217 Marker* marker = (Marker *) drag_info.data;
2221 begin_reversible_command ( _("move marker") );
2222 XMLNode &before = session->locations()->get_state();
2224 Location * location = find_location_from_marker (marker, is_start);
2227 if (location->is_mark()) {
2228 location->set_start (drag_info.copied_location->start());
2230 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2234 XMLNode &after = session->locations()->get_state();
2235 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2236 commit_reversible_command ();
2238 marker_drag_line->hide();
2239 range_marker_drag_rect->hide();
2243 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2246 MeterMarker* meter_marker;
2248 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2249 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2253 meter_marker = dynamic_cast<MeterMarker*> (marker);
2255 MetricSection& section (meter_marker->meter());
2257 if (!section.movable()) {
2261 drag_info.item = item;
2262 drag_info.data = marker;
2263 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2264 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2268 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2270 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2274 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2277 MeterMarker* meter_marker;
2279 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2280 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2284 meter_marker = dynamic_cast<MeterMarker*> (marker);
2286 // create a dummy marker for visual representation of moving the copy.
2287 // The actual copying is not done before we reach the finish callback.
2289 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2290 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2291 *new MeterSection(meter_marker->meter()));
2293 drag_info.item = &new_marker->the_item();
2294 drag_info.copy = true;
2295 drag_info.data = new_marker;
2296 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2297 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2301 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2303 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2307 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2309 MeterMarker* marker = (MeterMarker *) drag_info.data;
2310 nframes_t adjusted_frame;
2312 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2313 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2319 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2320 snap_to (adjusted_frame);
2323 if (adjusted_frame == drag_info.last_pointer_frame) return;
2325 marker->set_position (adjusted_frame);
2328 drag_info.last_pointer_frame = adjusted_frame;
2329 drag_info.first_move = false;
2331 show_verbose_time_cursor (adjusted_frame, 10);
2335 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2337 if (drag_info.first_move) return;
2339 meter_marker_drag_motion_callback (drag_info.item, event);
2341 MeterMarker* marker = (MeterMarker *) drag_info.data;
2344 TempoMap& map (session->tempo_map());
2345 map.bbt_time (drag_info.last_pointer_frame, when);
2347 if (drag_info.copy == true) {
2348 begin_reversible_command (_("copy meter mark"));
2349 XMLNode &before = map.get_state();
2350 map.add_meter (marker->meter(), when);
2351 XMLNode &after = map.get_state();
2352 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2353 commit_reversible_command ();
2355 // delete the dummy marker we used for visual representation of copying.
2356 // a new visual marker will show up automatically.
2359 begin_reversible_command (_("move meter mark"));
2360 XMLNode &before = map.get_state();
2361 map.move_meter (marker->meter(), when);
2362 XMLNode &after = map.get_state();
2363 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2364 commit_reversible_command ();
2369 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2372 TempoMarker* tempo_marker;
2374 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2375 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2379 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2380 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2384 MetricSection& section (tempo_marker->tempo());
2386 if (!section.movable()) {
2390 drag_info.item = item;
2391 drag_info.data = marker;
2392 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2393 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2397 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2398 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2402 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2405 TempoMarker* tempo_marker;
2407 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2408 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2412 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2413 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2417 // create a dummy marker for visual representation of moving the copy.
2418 // The actual copying is not done before we reach the finish callback.
2420 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2421 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2422 *new TempoSection(tempo_marker->tempo()));
2424 drag_info.item = &new_marker->the_item();
2425 drag_info.copy = true;
2426 drag_info.data = new_marker;
2427 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2428 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2432 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2434 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2438 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2440 TempoMarker* marker = (TempoMarker *) drag_info.data;
2441 nframes_t adjusted_frame;
2443 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2444 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2450 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2451 snap_to (adjusted_frame);
2454 if (adjusted_frame == drag_info.last_pointer_frame) return;
2456 /* OK, we've moved far enough to make it worth actually move the thing. */
2458 marker->set_position (adjusted_frame);
2460 show_verbose_time_cursor (adjusted_frame, 10);
2462 drag_info.last_pointer_frame = adjusted_frame;
2463 drag_info.first_move = false;
2467 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2469 if (drag_info.first_move) return;
2471 tempo_marker_drag_motion_callback (drag_info.item, event);
2473 TempoMarker* marker = (TempoMarker *) drag_info.data;
2476 TempoMap& map (session->tempo_map());
2477 map.bbt_time (drag_info.last_pointer_frame, when);
2479 if (drag_info.copy == true) {
2480 begin_reversible_command (_("copy tempo mark"));
2481 XMLNode &before = map.get_state();
2482 map.add_tempo (marker->tempo(), when);
2483 XMLNode &after = map.get_state();
2484 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2485 commit_reversible_command ();
2487 // delete the dummy marker we used for visual representation of copying.
2488 // a new visual marker will show up automatically.
2491 begin_reversible_command (_("move tempo mark"));
2492 XMLNode &before = map.get_state();
2493 map.move_tempo (marker->tempo(), when);
2494 XMLNode &after = map.get_state();
2495 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2496 commit_reversible_command ();
2501 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2503 ControlPoint* control_point;
2505 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2506 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2510 // We shouldn't remove the first or last gain point
2511 if (control_point->line.is_last_point(*control_point) ||
2512 control_point->line.is_first_point(*control_point)) {
2516 control_point->line.remove_point (*control_point);
2520 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2522 ControlPoint* control_point;
2524 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2525 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2529 control_point->line.remove_point (*control_point);
2533 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2535 ControlPoint* control_point;
2537 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2538 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2542 drag_info.item = item;
2543 drag_info.data = control_point;
2544 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2545 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2547 start_grab (event, fader_cursor);
2549 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2551 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2552 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2553 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2555 show_verbose_canvas_cursor ();
2559 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2561 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2563 double cx = drag_info.current_pointer_x;
2564 double cy = drag_info.current_pointer_y;
2566 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2567 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2569 if (drag_info.x_constrained) {
2570 cx = drag_info.grab_x;
2572 if (drag_info.y_constrained) {
2573 cy = drag_info.grab_y;
2576 cp->line.parent_group().w2i (cx, cy);
2580 cy = min ((double) cp->line.height(), cy);
2582 //translate cx to frames
2583 nframes_t cx_frames = unit_to_frame (cx);
2585 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2586 snap_to (cx_frames);
2589 float fraction = 1.0 - (cy / cp->line.height());
2593 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2599 cp->line.point_drag (*cp, cx_frames , fraction, push);
2601 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2603 drag_info.first_move = false;
2607 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2609 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2611 if (drag_info.first_move) {
2615 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2616 reset_point_selection ();
2620 control_point_drag_motion_callback (item, event);
2622 cp->line.end_drag (cp);
2626 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2628 switch (mouse_mode) {
2630 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2631 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2639 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2643 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2644 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2648 start_line_grab (al, event);
2652 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2656 nframes_t frame_within_region;
2658 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2662 cx = event->button.x;
2663 cy = event->button.y;
2664 line->parent_group().w2i (cx, cy);
2665 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2667 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2668 current_line_drag_info.after)) {
2669 /* no adjacent points */
2673 drag_info.item = &line->grab_item();
2674 drag_info.data = line;
2675 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2676 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2678 start_grab (event, fader_cursor);
2680 double fraction = 1.0 - (cy / line->height());
2682 line->start_drag (0, drag_info.grab_frame, fraction);
2684 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2685 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2686 show_verbose_canvas_cursor ();
2690 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2692 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2693 double cx = drag_info.current_pointer_x;
2694 double cy = drag_info.current_pointer_y;
2696 line->parent_group().w2i (cx, cy);
2699 fraction = 1.0 - (cy / line->height());
2703 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2709 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2711 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2715 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2717 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2718 line_drag_motion_callback (item, event);
2723 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2725 if (selection->regions.empty() || clicked_regionview == 0) {
2729 drag_info.copy = false;
2730 drag_info.item = item;
2731 drag_info.data = clicked_regionview;
2732 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2733 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2738 TimeAxisView* tvp = clicked_trackview;
2739 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2741 if (tv && tv->is_audio_track()) {
2742 speed = tv->get_diskstream()->speed();
2745 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2746 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2747 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2748 // we want a move threshold
2749 drag_info.want_move_threshold = true;
2751 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2753 begin_reversible_command (_("move region(s)"));
2757 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2759 if (selection->regions.empty() || clicked_regionview == 0) {
2763 drag_info.copy = true;
2764 drag_info.item = item;
2765 drag_info.data = clicked_regionview;
2769 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2770 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2773 if (atv && atv->is_audio_track()) {
2774 speed = atv->get_diskstream()->speed();
2777 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2778 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2779 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2780 // we want a move threshold
2781 drag_info.want_move_threshold = true;
2782 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2783 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2784 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2788 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2790 if (selection->regions.empty() || clicked_regionview == 0) {
2794 drag_info.copy = false;
2795 drag_info.item = item;
2796 drag_info.data = clicked_regionview;
2797 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2798 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2803 TimeAxisView* tvp = clicked_trackview;
2804 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2806 if (tv && tv->is_audio_track()) {
2807 speed = tv->get_diskstream()->speed();
2810 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2811 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2812 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2813 // we want a move threshold
2814 drag_info.want_move_threshold = true;
2815 drag_info.brushing = true;
2817 begin_reversible_command (_("Drag region brush"));
2821 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2825 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2826 nframes_t pending_region_position = 0;
2827 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2828 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2829 bool clamp_y_axis = false;
2830 vector<int32_t> height_list(512) ;
2831 vector<int32_t>::iterator j;
2833 /* don't copy regions if we're doing x-constrained and we're in the same track, or
2834 if we haven't passed the move threshold yet
2837 if ((!drag_info.x_constrained || (drag_info.last_trackview != &rv->get_time_axis_view())) &&
2838 drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2840 cerr << "COPY, xcons = " << drag_info.x_constrained << " last = " << drag_info.last_trackview->name() << " rv = " << rv->get_time_axis_view().name() << endl;
2842 drag_info.want_move_threshold = false; // don't copy again
2844 /* this is committed in the grab finished callback. */
2846 begin_reversible_command (_("Drag region copy"));
2848 /* duplicate the region(s) */
2850 vector<RegionView*> new_regionviews;
2852 set<boost::shared_ptr<Playlist> > affected_playlists;
2853 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2855 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2860 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
2861 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2863 insert_result = affected_playlists.insert (to_playlist);
2864 if (insert_result.second) {
2865 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
2868 latest_regionview = 0;
2870 /* create a new region with the same name. */
2872 // FIXME: ew. need a (virtual) Region::duplicate() or something?
2874 boost::shared_ptr<Region> newregion;
2875 boost::shared_ptr<Region> ar;
2877 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
2878 newregion = RegionFactory::create (ar);
2880 assert(newregion != 0);
2882 /* if the original region was locked, we don't care */
2884 newregion->set_locked (false);
2886 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2887 to_playlist->add_region (newregion, (nframes_t) (rv->region()->position() * atv->get_diskstream()->speed()));
2891 if (latest_regionview) {
2892 new_regionviews.push_back (latest_regionview);
2896 if (new_regionviews.empty()) {
2900 /* reset selection to new regionviews */
2902 selection->set (new_regionviews);
2904 /* reset drag_info data to reflect the fact that we are dragging the copies */
2906 drag_info.data = new_regionviews.front();
2907 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2910 /* Which trackview is this ? */
2912 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2913 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2915 /* The region motion is only processed if the pointer is over
2919 if (!tv || !tv->is_audio_track()) {
2920 /* To make sure we hide the verbose canvas cursor when the mouse is
2921 not held over and audiotrack.
2923 hide_verbose_canvas_cursor ();
2927 original_pointer_order = drag_info.last_trackview->order;
2929 /************************************************************
2931 ************************************************************/
2933 if (drag_info.brushing) {
2934 clamp_y_axis = true;
2939 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2941 int32_t children = 0, numtracks = 0;
2942 // XXX hard coding track limit, oh my, so very very bad
2943 bitset <1024> tracks (0x00);
2944 /* get a bitmask representing the visible tracks */
2946 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2947 TimeAxisView *tracklist_timeview;
2948 tracklist_timeview = (*i);
2949 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2950 list<TimeAxisView*> children_list;
2952 /* zeroes are audio tracks. ones are other types. */
2954 if (!atv2->hidden()) {
2956 if (visible_y_high < atv2->order) {
2957 visible_y_high = atv2->order;
2959 if (visible_y_low > atv2->order) {
2960 visible_y_low = atv2->order;
2963 if (!atv2->is_audio_track()) {
2964 tracks = tracks |= (0x01 << atv2->order);
2967 height_list[atv2->order] = (*i)->height;
2969 if ((children_list = atv2->get_child_list()).size() > 0) {
2970 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2971 tracks = tracks |= (0x01 << (atv2->order + children));
2972 height_list[atv2->order + children] = (*j)->height;
2980 /* find the actual span according to the canvas */
2982 canvas_pointer_y_span = pointer_y_span;
2983 if (drag_info.last_trackview->order >= tv->order) {
2985 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2986 if (height_list[y] == 0 ) {
2987 canvas_pointer_y_span--;
2992 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2993 if ( height_list[y] == 0 ) {
2994 canvas_pointer_y_span++;
2999 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3000 RegionView* rv2 = (*i);
3001 double ix1, ix2, iy1, iy2;
3004 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3005 rv2->get_canvas_group()->i2w (ix1, iy1);
3006 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3007 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3009 if (atv2->order != original_pointer_order) {
3010 /* this isn't the pointer track */
3012 if (canvas_pointer_y_span > 0) {
3014 /* moving up the canvas */
3015 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3017 int32_t visible_tracks = 0;
3018 while (visible_tracks < canvas_pointer_y_span ) {
3021 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3022 /* we're passing through a hidden track */
3027 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3028 clamp_y_axis = true;
3032 clamp_y_axis = true;
3035 } else if (canvas_pointer_y_span < 0) {
3037 /*moving down the canvas*/
3039 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3042 int32_t visible_tracks = 0;
3044 while (visible_tracks > canvas_pointer_y_span ) {
3047 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3051 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3052 clamp_y_axis = true;
3057 clamp_y_axis = true;
3063 /* this is the pointer's track */
3064 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3065 clamp_y_axis = true;
3066 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3067 clamp_y_axis = true;
3075 } else if (drag_info.last_trackview == tv) {
3076 clamp_y_axis = true;
3080 if (!clamp_y_axis) {
3081 drag_info.last_trackview = tv;
3084 /************************************************************
3086 ************************************************************/
3088 /* compute the amount of pointer motion in frames, and where
3089 the region would be if we moved it by that much.
3092 if (drag_info.move_threshold_passed) {
3094 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3096 nframes_t sync_frame;
3097 nframes_t sync_offset;
3100 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3102 sync_offset = rv->region()->sync_offset (sync_dir);
3103 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3105 /* we snap if the snap modifier is not enabled.
3108 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3109 snap_to (sync_frame);
3112 if (sync_frame - sync_offset <= sync_frame) {
3113 pending_region_position = sync_frame - (sync_dir*sync_offset);
3115 pending_region_position = 0;
3119 pending_region_position = 0;
3122 if (pending_region_position > max_frames - rv->region()->length()) {
3123 pending_region_position = drag_info.last_frame_position;
3126 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3128 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3130 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3131 to make it appear at the new location.
3134 if (pending_region_position > drag_info.last_frame_position) {
3135 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3137 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3140 drag_info.last_frame_position = pending_region_position;
3147 /* threshold not passed */
3152 /*************************************************************
3154 ************************************************************/
3156 if (x_delta == 0 && (pointer_y_span == 0)) {
3157 /* haven't reached next snap point, and we're not switching
3158 trackviews. nothing to do.
3164 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3166 RegionView* rv2 = (*i);
3168 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3170 double ix1, ix2, iy1, iy2;
3171 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3172 rv2->get_canvas_group()->i2w (ix1, iy1);
3181 /*************************************************************
3183 ************************************************************/
3187 if (drag_info.first_move) {
3188 if (drag_info.move_threshold_passed) {
3199 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3200 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3202 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3204 RegionView* rv = (*i);
3205 double ix1, ix2, iy1, iy2;
3206 int32_t temp_pointer_y_span = pointer_y_span;
3208 /* get item BBox, which will be relative to parent. so we have
3209 to query on a child, then convert to world coordinates using
3213 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3214 rv->get_canvas_group()->i2w (ix1, iy1);
3215 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3216 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3217 AudioTimeAxisView* temp_atv;
3219 if ((pointer_y_span != 0) && !clamp_y_axis) {
3222 for (j = height_list.begin(); j!= height_list.end(); j++) {
3223 if (x == canvas_atv->order) {
3224 /* we found the track the region is on */
3225 if (x != original_pointer_order) {
3226 /*this isn't from the same track we're dragging from */
3227 temp_pointer_y_span = canvas_pointer_y_span;
3229 while (temp_pointer_y_span > 0) {
3230 /* we're moving up canvas-wise,
3231 so we need to find the next track height
3233 if (j != height_list.begin()) {
3236 if (x != original_pointer_order) {
3237 /* we're not from the dragged track, so ignore hidden tracks. */
3239 temp_pointer_y_span++;
3243 temp_pointer_y_span--;
3245 while (temp_pointer_y_span < 0) {
3247 if (x != original_pointer_order) {
3249 temp_pointer_y_span--;
3253 if (j != height_list.end()) {
3256 temp_pointer_y_span++;
3258 /* find out where we'll be when we move and set height accordingly */
3260 tvp2 = trackview_by_y_position (iy1 + y_delta);
3261 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3262 rv->set_height (temp_atv->height);
3264 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3265 personally, i think this can confuse things, but never mind.
3268 //const GdkColor& col (temp_atv->view->get_region_color());
3269 //rv->set_color (const_cast<GdkColor&>(col));
3276 /* prevent the regionview from being moved to before
3277 the zero position on the canvas.
3282 if (-x_delta > ix1) {
3285 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3286 x_delta = max_frames - rv->region()->last_frame();
3289 if (drag_info.first_move) {
3291 /* hide any dependent views */
3293 rv->get_time_axis_view().hide_dependent_views (*rv);
3295 /* this is subtle. raising the regionview itself won't help,
3296 because raise_to_top() just puts the item on the top of
3297 its parent's stack. so, we need to put the trackview canvas_display group
3298 on the top, since its parent is the whole canvas.
3301 rv->get_canvas_group()->raise_to_top();
3302 rv->get_time_axis_view().canvas_display->raise_to_top();
3303 cursor_group->raise_to_top();
3305 /* freeze the playlists from notifying till
3309 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3310 if (atv && atv->is_audio_track()) {
3311 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>(atv->get_diskstream()->playlist());
3313 /* only freeze and capture state once */
3315 insert_result = motion_frozen_playlists.insert (pl);
3316 if (insert_result.second) {
3318 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
3323 rv->fake_set_opaque (false);
3326 if (drag_info.brushing) {
3327 mouse_brush_insert_region (rv, pending_region_position);
3329 rv->move (x_delta, y_delta);
3332 } /* foreach region */
3336 if (drag_info.first_move && drag_info.move_threshold_passed) {
3337 cursor_group->raise_to_top();
3338 drag_info.first_move = false;
3341 if (x_delta != 0 && !drag_info.brushing) {
3342 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3347 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3350 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3351 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3352 bool nocommit = true;
3354 RouteTimeAxisView* atv;
3355 bool regionview_y_movement;
3356 bool regionview_x_movement;
3358 /* first_move is set to false if the regionview has been moved in the
3362 if (drag_info.first_move) {
3369 /* The regionview has been moved at some stage during the grab so we need
3370 to account for any mouse movement between this event and the last one.
3373 region_drag_motion_callback (item, event);
3375 if (drag_info.brushing) {
3376 /* all changes were made during motion event handlers */
3380 /* adjust for track speed */
3383 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3384 if (atv && atv->get_diskstream()) {
3385 speed = atv->get_diskstream()->speed();
3388 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3389 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3391 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3392 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3394 if (regionview_y_movement) {
3396 /* motion between tracks */
3398 list<RegionView*> new_selection;
3400 /* moved to a different audio track. */
3402 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3404 RegionView* rv2 = (*i);
3406 /* the region that used to be in the old playlist is not
3407 moved to the new one - we make a copy of it. as a result,
3408 any existing editor for the region should no longer be
3412 if (!drag_info.copy) {
3413 rv2->hide_region_editor();
3415 new_selection.push_back (rv2);
3419 /* first, freeze the target tracks */
3421 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3423 boost::shared_ptr<Playlist> from_playlist;
3424 boost::shared_ptr<Playlist> to_playlist;
3426 double ix1, ix2, iy1, iy2;
3428 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3429 (*i)->get_canvas_group()->i2w (ix1, iy1);
3430 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3431 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3433 (*i)->fake_set_opaque (true);
3435 from_playlist = (*i)->region()->playlist();
3436 to_playlist = atv2->playlist();
3438 /* the from_playlist was frozen in the "first_move" case
3439 of the motion handler. the insert can fail,
3440 but that doesn't matter. it just means
3441 we already have the playlist in the list.
3444 motion_frozen_playlists.insert (from_playlist);
3446 /* only freeze the to_playlist once */
3448 insert_result = motion_frozen_playlists.insert(to_playlist);
3449 if (insert_result.second) {
3450 to_playlist->freeze();
3451 session->add_command(new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3456 /* now do it again with the actual operations */
3458 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3460 boost::shared_ptr<Playlist> from_playlist;
3461 boost::shared_ptr<Playlist> to_playlist;
3463 double ix1, ix2, iy1, iy2;
3465 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3466 (*i)->get_canvas_group()->i2w (ix1, iy1);
3467 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3468 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3470 from_playlist = (*i)->region()->playlist();
3471 to_playlist = atv2->playlist();
3473 latest_regionview = 0;
3475 where = (nframes_t) (unit_to_frame (ix1) * speed);
3476 boost::shared_ptr<Region> new_region (RegionFactory::create ((*i)->region()));
3478 from_playlist->remove_region (((*i)->region()));
3480 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3481 to_playlist->add_region (new_region, where);
3484 if (latest_regionview) {
3485 selection->add (latest_regionview);
3491 /* motion within a single track */
3493 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3497 if (rv->region()->locked()) {
3501 if (regionview_x_movement) {
3502 double ownspeed = 1.0;
3503 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3505 if (atv && atv->get_diskstream()) {
3506 ownspeed = atv->get_diskstream()->speed();
3509 /* base the new region position on the current position of the regionview.*/
3511 double ix1, ix2, iy1, iy2;
3513 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3514 rv->get_canvas_group()->i2w (ix1, iy1);
3515 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3519 where = rv->region()->position();
3522 rv->get_time_axis_view().reveal_dependent_views (*rv);
3524 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3526 rv->region()->set_position (where, (void *) this);
3527 rv->fake_set_opaque (true);
3532 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3534 session->add_command (new MementoCommand<Playlist>(*((*p).get()), 0, & (*p)->get_state()));
3537 motion_frozen_playlists.clear ();
3540 commit_reversible_command ();
3545 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3547 /* Either add to or set the set the region selection, unless
3548 this is an alignment click (control used)
3551 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3552 TimeAxisView* tv = &rv.get_time_axis_view();
3553 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3555 if (atv && atv->is_audio_track()) {
3556 speed = atv->get_diskstream()->speed();
3559 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3561 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3563 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3565 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3569 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3575 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3581 nframes_t frame_rate;
3588 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3589 case AudioClock::BBT:
3590 session->bbt_time (frame, bbt);
3591 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3594 case AudioClock::SMPTE:
3595 session->smpte_time (frame, smpte);
3596 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3599 case AudioClock::MinSec:
3600 /* XXX this is copied from show_verbose_duration_cursor() */
3601 frame_rate = session->frame_rate();
3602 hours = frame / (frame_rate * 3600);
3603 frame = frame % (frame_rate * 3600);
3604 mins = frame / (frame_rate * 60);
3605 frame = frame % (frame_rate * 60);
3606 secs = (float) frame / (float) frame_rate;
3607 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3611 snprintf (buf, sizeof(buf), "%u", frame);
3615 if (xpos >= 0 && ypos >=0) {
3616 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3619 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3621 show_verbose_canvas_cursor ();
3625 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3632 nframes_t distance, frame_rate;
3634 Meter meter_at_start(session->tempo_map().meter_at(start));
3640 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3641 case AudioClock::BBT:
3642 session->bbt_time (start, sbbt);
3643 session->bbt_time (end, ebbt);
3646 /* XXX this computation won't work well if the
3647 user makes a selection that spans any meter changes.
3650 ebbt.bars -= sbbt.bars;
3651 if (ebbt.beats >= sbbt.beats) {
3652 ebbt.beats -= sbbt.beats;
3655 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3657 if (ebbt.ticks >= sbbt.ticks) {
3658 ebbt.ticks -= sbbt.ticks;
3661 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3664 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3667 case AudioClock::SMPTE:
3668 session->smpte_duration (end - start, smpte);
3669 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3672 case AudioClock::MinSec:
3673 /* XXX this stuff should be elsewhere.. */
3674 distance = end - start;
3675 frame_rate = session->frame_rate();
3676 hours = distance / (frame_rate * 3600);
3677 distance = distance % (frame_rate * 3600);
3678 mins = distance / (frame_rate * 60);
3679 distance = distance % (frame_rate * 60);
3680 secs = (float) distance / (float) frame_rate;
3681 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3685 snprintf (buf, sizeof(buf), "%u", end - start);
3689 if (xpos >= 0 && ypos >=0) {
3690 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3693 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3695 show_verbose_canvas_cursor ();
3699 Editor::collect_new_region_view (RegionView* rv)
3701 latest_regionview = rv;
3705 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3707 if (clicked_regionview == 0) {
3711 /* lets try to create new Region for the selection */
3713 vector<boost::shared_ptr<AudioRegion> > new_regions;
3714 create_region_from_selection (new_regions);
3716 if (new_regions.empty()) {
3720 /* XXX fix me one day to use all new regions */
3722 boost::shared_ptr<Region> region (new_regions.front());
3724 /* add it to the current stream/playlist.
3726 tricky: the streamview for the track will add a new regionview. we will
3727 catch the signal it sends when it creates the regionview to
3728 set the regionview we want to then drag.
3731 latest_regionview = 0;
3732 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3734 /* A selection grab currently creates two undo/redo operations, one for
3735 creating the new region and another for moving it.
3738 begin_reversible_command (_("selection grab"));
3740 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3742 XMLNode *before = &(playlist->get_state());
3743 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3744 XMLNode *after = &(playlist->get_state());
3745 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3747 commit_reversible_command ();
3751 if (latest_regionview == 0) {
3752 /* something went wrong */
3756 /* we need to deselect all other regionviews, and select this one
3757 i'm ignoring undo stuff, because the region creation will take care of it */
3758 selection->set (latest_regionview);
3760 drag_info.item = latest_regionview->get_canvas_group();
3761 drag_info.data = latest_regionview;
3762 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3763 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3767 drag_info.last_trackview = clicked_trackview;
3768 drag_info.last_frame_position = latest_regionview->region()->position();
3769 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3771 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3775 Editor::cancel_selection ()
3777 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3778 (*i)->hide_selection ();
3780 begin_reversible_command (_("cancel selection"));
3781 selection->clear ();
3782 clicked_selection = 0;
3783 commit_reversible_command ();
3787 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3789 nframes_t start = 0;
3796 drag_info.item = item;
3797 drag_info.motion_callback = &Editor::drag_selection;
3798 drag_info.finished_callback = &Editor::end_selection_op;
3803 case CreateSelection:
3804 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3805 drag_info.copy = true;
3807 drag_info.copy = false;
3809 start_grab (event, selector_cursor);
3812 case SelectionStartTrim:
3813 if (clicked_trackview) {
3814 clicked_trackview->order_selection_trims (item, true);
3816 start_grab (event, trimmer_cursor);
3817 start = selection->time[clicked_selection].start;
3818 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3821 case SelectionEndTrim:
3822 if (clicked_trackview) {
3823 clicked_trackview->order_selection_trims (item, false);
3825 start_grab (event, trimmer_cursor);
3826 end = selection->time[clicked_selection].end;
3827 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3831 start = selection->time[clicked_selection].start;
3833 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3837 if (selection_op == SelectionMove) {
3838 show_verbose_time_cursor(start, 10);
3840 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3845 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3847 nframes_t start = 0;
3850 nframes_t pending_position;
3852 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3853 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3856 pending_position = 0;
3859 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3860 snap_to (pending_position);
3863 /* only alter selection if the current frame is
3864 different from the last frame position (adjusted)
3867 if (pending_position == drag_info.last_pointer_frame) return;
3869 switch (selection_op) {
3870 case CreateSelection:
3872 if (drag_info.first_move) {
3873 snap_to (drag_info.grab_frame);
3876 if (pending_position < drag_info.grab_frame) {
3877 start = pending_position;
3878 end = drag_info.grab_frame;
3880 end = pending_position;
3881 start = drag_info.grab_frame;
3884 /* first drag: Either add to the selection
3885 or create a new selection->
3888 if (drag_info.first_move) {
3890 begin_reversible_command (_("range selection"));
3892 if (drag_info.copy) {
3893 /* adding to the selection */
3894 clicked_selection = selection->add (start, end);
3895 drag_info.copy = false;
3897 /* new selection-> */
3898 clicked_selection = selection->set (clicked_trackview, start, end);
3903 case SelectionStartTrim:
3905 if (drag_info.first_move) {
3906 begin_reversible_command (_("trim selection start"));
3909 start = selection->time[clicked_selection].start;
3910 end = selection->time[clicked_selection].end;
3912 if (pending_position > end) {
3915 start = pending_position;
3919 case SelectionEndTrim:
3921 if (drag_info.first_move) {
3922 begin_reversible_command (_("trim selection end"));
3925 start = selection->time[clicked_selection].start;
3926 end = selection->time[clicked_selection].end;
3928 if (pending_position < start) {
3931 end = pending_position;
3938 if (drag_info.first_move) {
3939 begin_reversible_command (_("move selection"));
3942 start = selection->time[clicked_selection].start;
3943 end = selection->time[clicked_selection].end;
3945 length = end - start;
3947 start = pending_position;
3950 end = start + length;
3955 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3956 start_canvas_autoscroll (1);
3960 selection->replace (clicked_selection, start, end);
3963 drag_info.last_pointer_frame = pending_position;
3964 drag_info.first_move = false;
3966 if (selection_op == SelectionMove) {
3967 show_verbose_time_cursor(start, 10);
3969 show_verbose_time_cursor(pending_position, 10);
3974 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3976 if (!drag_info.first_move) {
3977 drag_selection (item, event);
3978 /* XXX this is not object-oriented programming at all. ick */
3979 if (selection->time.consolidate()) {
3980 selection->TimeChanged ();
3982 commit_reversible_command ();
3984 /* just a click, no pointer movement.*/
3986 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3988 selection->clear_time();
3993 /* XXX what happens if its a music selection? */
3994 session->set_audio_range (selection->time);
3995 stop_canvas_autoscroll ();
3999 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4002 TimeAxisView* tvp = clicked_trackview;
4003 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4005 if (tv && tv->is_audio_track()) {
4006 speed = tv->get_diskstream()->speed();
4009 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4010 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4011 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4013 motion_frozen_playlists.clear();
4015 //drag_info.item = clicked_regionview->get_name_highlight();
4016 drag_info.item = item;
4017 drag_info.motion_callback = &Editor::trim_motion_callback;
4018 drag_info.finished_callback = &Editor::trim_finished_callback;
4020 start_grab (event, trimmer_cursor);
4022 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4023 trim_op = ContentsTrim;
4025 /* These will get overridden for a point trim.*/
4026 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4027 /* closer to start */
4028 trim_op = StartTrim;
4029 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4037 show_verbose_time_cursor(region_start, 10);
4040 show_verbose_time_cursor(region_end, 10);
4043 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4049 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4051 RegionView* rv = clicked_regionview;
4052 nframes_t frame_delta = 0;
4053 bool left_direction;
4054 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4056 /* snap modifier works differently here..
4057 its' current state has to be passed to the
4058 various trim functions in order to work properly
4062 TimeAxisView* tvp = clicked_trackview;
4063 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4064 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4066 if (tv && tv->is_audio_track()) {
4067 speed = tv->get_diskstream()->speed();
4070 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4071 left_direction = true;
4073 left_direction = false;
4077 snap_to (drag_info.current_pointer_frame);
4080 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4084 if (drag_info.first_move) {
4090 trim_type = "Region start trim";
4093 trim_type = "Region end trim";
4096 trim_type = "Region content trim";
4100 begin_reversible_command (trim_type);
4102 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4103 (*i)->fake_set_opaque(false);
4104 (*i)->region()->freeze ();
4106 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4108 arv->temporarily_hide_envelope ();
4110 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4111 insert_result = motion_frozen_playlists.insert (pl);
4112 if (insert_result.second) {
4113 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4118 if (left_direction) {
4119 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4121 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4126 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4129 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4130 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4136 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4139 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4140 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4147 bool swap_direction = false;
4149 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4150 swap_direction = true;
4153 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4154 i != selection->regions.by_layer().end(); ++i)
4156 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4164 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4167 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4170 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4174 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4175 drag_info.first_move = false;
4179 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4181 boost::shared_ptr<Region> region (rv.region());
4183 if (region->locked()) {
4187 nframes_t new_bound;
4190 TimeAxisView* tvp = clicked_trackview;
4191 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4193 if (tv && tv->is_audio_track()) {
4194 speed = tv->get_diskstream()->speed();
4197 if (left_direction) {
4198 if (swap_direction) {
4199 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4201 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4204 if (swap_direction) {
4205 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4207 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4212 snap_to (new_bound);
4214 region->trim_start ((nframes_t) (new_bound * speed), this);
4215 rv.region_changed (StartChanged);
4219 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4221 boost::shared_ptr<Region> region (rv.region());
4223 if (region->locked()) {
4227 nframes_t new_bound;
4230 TimeAxisView* tvp = clicked_trackview;
4231 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4233 if (tv && tv->is_audio_track()) {
4234 speed = tv->get_diskstream()->speed();
4237 if (left_direction) {
4238 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4240 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4244 snap_to (new_bound, (left_direction ? 0 : 1));
4247 region->trim_front ((nframes_t) (new_bound * speed), this);
4249 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4253 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4255 boost::shared_ptr<Region> region (rv.region());
4257 if (region->locked()) {
4261 nframes_t new_bound;
4264 TimeAxisView* tvp = clicked_trackview;
4265 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4267 if (tv && tv->is_audio_track()) {
4268 speed = tv->get_diskstream()->speed();
4271 if (left_direction) {
4272 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4274 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4278 snap_to (new_bound);
4280 region->trim_end ((nframes_t) (new_bound * speed), this);
4281 rv.region_changed (LengthChanged);
4285 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4287 if (!drag_info.first_move) {
4288 trim_motion_callback (item, event);
4290 if (!clicked_regionview->get_selected()) {
4291 thaw_region_after_trim (*clicked_regionview);
4294 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4295 i != selection->regions.by_layer().end(); ++i)
4297 thaw_region_after_trim (**i);
4298 (*i)->fake_set_opaque (true);
4302 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4304 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4307 motion_frozen_playlists.clear ();
4309 commit_reversible_command();
4311 /* no mouse movement */
4317 Editor::point_trim (GdkEvent* event)
4319 RegionView* rv = clicked_regionview;
4320 nframes_t new_bound = drag_info.current_pointer_frame;
4322 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4323 snap_to (new_bound);
4326 /* Choose action dependant on which button was pressed */
4327 switch (event->button.button) {
4329 trim_op = StartTrim;
4330 begin_reversible_command (_("Start point trim"));
4332 if (rv->get_selected()) {
4334 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4335 i != selection->regions.by_layer().end(); ++i)
4337 if (!(*i)->region()->locked()) {
4338 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4339 XMLNode &before = pl->get_state();
4340 (*i)->region()->trim_front (new_bound, this);
4341 XMLNode &after = pl->get_state();
4342 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4348 if (!rv->region()->locked()) {
4349 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4350 XMLNode &before = pl->get_state();
4351 rv->region()->trim_front (new_bound, this);
4352 XMLNode &after = pl->get_state();
4353 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4357 commit_reversible_command();
4362 begin_reversible_command (_("End point trim"));
4364 if (rv->get_selected()) {
4366 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4368 if (!(*i)->region()->locked()) {
4369 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4370 XMLNode &before = pl->get_state();
4371 (*i)->region()->trim_end (new_bound, this);
4372 XMLNode &after = pl->get_state();
4373 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4379 if (!rv->region()->locked()) {
4380 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4381 XMLNode &before = pl->get_state();
4382 rv->region()->trim_end (new_bound, this);
4383 XMLNode &after = pl->get_state();
4384 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4388 commit_reversible_command();
4397 Editor::thaw_region_after_trim (RegionView& rv)
4399 boost::shared_ptr<Region> region (rv.region());
4401 if (region->locked()) {
4405 region->thaw (_("trimmed region"));
4406 XMLNode &after = region->playlist()->get_state();
4407 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4409 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4411 arv->unhide_envelope ();
4415 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4420 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4421 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4425 Location* location = find_location_from_marker (marker, is_start);
4426 location->set_hidden (true, this);
4431 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4437 drag_info.item = item;
4438 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4439 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4441 range_marker_op = op;
4443 if (!temp_location) {
4444 temp_location = new Location;
4448 case CreateRangeMarker:
4449 case CreateTransportMarker:
4451 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4452 drag_info.copy = true;
4454 drag_info.copy = false;
4456 start_grab (event, selector_cursor);
4460 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4465 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4467 nframes_t start = 0;
4469 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4471 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4472 snap_to (drag_info.current_pointer_frame);
4475 /* only alter selection if the current frame is
4476 different from the last frame position.
4479 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4481 switch (range_marker_op) {
4482 case CreateRangeMarker:
4483 case CreateTransportMarker:
4484 if (drag_info.first_move) {
4485 snap_to (drag_info.grab_frame);
4488 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4489 start = drag_info.current_pointer_frame;
4490 end = drag_info.grab_frame;
4492 end = drag_info.current_pointer_frame;
4493 start = drag_info.grab_frame;
4496 /* first drag: Either add to the selection
4497 or create a new selection.
4500 if (drag_info.first_move) {
4502 temp_location->set (start, end);
4506 update_marker_drag_item (temp_location);
4507 range_marker_drag_rect->show();
4508 range_marker_drag_rect->raise_to_top();
4514 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4515 start_canvas_autoscroll (1);
4519 temp_location->set (start, end);
4521 double x1 = frame_to_pixel (start);
4522 double x2 = frame_to_pixel (end);
4523 crect->property_x1() = x1;
4524 crect->property_x2() = x2;
4526 update_marker_drag_item (temp_location);
4529 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4530 drag_info.first_move = false;
4532 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4537 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4539 Location * newloc = 0;
4542 if (!drag_info.first_move) {
4543 drag_range_markerbar_op (item, event);
4545 switch (range_marker_op) {
4546 case CreateRangeMarker:
4548 begin_reversible_command (_("new range marker"));
4549 XMLNode &before = session->locations()->get_state();
4550 session->locations()->next_available_name(rangename,"unnamed");
4551 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4552 session->locations()->add (newloc, true);
4553 XMLNode &after = session->locations()->get_state();
4554 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4555 commit_reversible_command ();
4557 range_bar_drag_rect->hide();
4558 range_marker_drag_rect->hide();
4562 case CreateTransportMarker:
4563 // popup menu to pick loop or punch
4564 new_transport_marker_context_menu (&event->button, item);
4569 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4571 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4576 start = session->locations()->first_mark_before (drag_info.grab_frame);
4577 end = session->locations()->first_mark_after (drag_info.grab_frame);
4579 if (end == max_frames) {
4580 end = session->current_end_frame ();
4584 start = session->current_start_frame ();
4587 switch (mouse_mode) {
4589 /* find the two markers on either side and then make the selection from it */
4590 cerr << "select between " << start << " .. " << end << endl;
4591 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4595 /* find the two markers on either side of the click and make the range out of it */
4596 selection->set (0, start, end);
4605 stop_canvas_autoscroll ();
4611 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4613 drag_info.item = item;
4614 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4615 drag_info.finished_callback = &Editor::end_mouse_zoom;
4617 start_grab (event, zoom_cursor);
4619 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4623 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4628 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4629 snap_to (drag_info.current_pointer_frame);
4631 if (drag_info.first_move) {
4632 snap_to (drag_info.grab_frame);
4636 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4638 /* base start and end on initial click position */
4639 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4640 start = drag_info.current_pointer_frame;
4641 end = drag_info.grab_frame;
4643 end = drag_info.current_pointer_frame;
4644 start = drag_info.grab_frame;
4649 if (drag_info.first_move) {
4651 zoom_rect->raise_to_top();
4654 reposition_zoom_rect(start, end);
4656 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4657 drag_info.first_move = false;
4659 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4664 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4666 if (!drag_info.first_move) {
4667 drag_mouse_zoom (item, event);
4669 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4670 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4672 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4675 temporal_zoom_to_frame (false, drag_info.grab_frame);
4677 temporal_zoom_step (false);
4678 center_screen (drag_info.grab_frame);
4686 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4688 double x1 = frame_to_pixel (start);
4689 double x2 = frame_to_pixel (end);
4690 double y2 = full_canvas_height - 1.0;
4692 zoom_rect->property_x1() = x1;
4693 zoom_rect->property_y1() = 1.0;
4694 zoom_rect->property_x2() = x2;
4695 zoom_rect->property_y2() = y2;
4699 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4701 drag_info.item = item;
4702 drag_info.motion_callback = &Editor::drag_rubberband_select;
4703 drag_info.finished_callback = &Editor::end_rubberband_select;
4705 start_grab (event, cross_hair_cursor);
4707 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4711 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4718 /* use a bigger drag threshold than the default */
4720 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4724 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4725 if (drag_info.first_move) {
4726 snap_to (drag_info.grab_frame);
4728 snap_to (drag_info.current_pointer_frame);
4731 /* base start and end on initial click position */
4733 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4734 start = drag_info.current_pointer_frame;
4735 end = drag_info.grab_frame;
4737 end = drag_info.current_pointer_frame;
4738 start = drag_info.grab_frame;
4741 if (drag_info.current_pointer_y < drag_info.grab_y) {
4742 y1 = drag_info.current_pointer_y;
4743 y2 = drag_info.grab_y;
4745 y2 = drag_info.current_pointer_y;
4746 y1 = drag_info.grab_y;
4750 if (start != end || y1 != y2) {
4752 double x1 = frame_to_pixel (start);
4753 double x2 = frame_to_pixel (end);
4755 rubberband_rect->property_x1() = x1;
4756 rubberband_rect->property_y1() = y1;
4757 rubberband_rect->property_x2() = x2;
4758 rubberband_rect->property_y2() = y2;
4760 rubberband_rect->show();
4761 rubberband_rect->raise_to_top();
4763 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4764 drag_info.first_move = false;
4766 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4771 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4773 if (!drag_info.first_move) {
4775 drag_rubberband_select (item, event);
4778 if (drag_info.current_pointer_y < drag_info.grab_y) {
4779 y1 = drag_info.current_pointer_y;
4780 y2 = drag_info.grab_y;
4783 y2 = drag_info.current_pointer_y;
4784 y1 = drag_info.grab_y;
4788 Selection::Operation op = Keyboard::selection_type (event->button.state);
4791 begin_reversible_command (_("rubberband selection"));
4793 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4794 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4796 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4800 commit_reversible_command ();
4804 selection->clear_tracks();
4805 selection->clear_regions();
4806 selection->clear_points ();
4807 selection->clear_lines ();
4810 rubberband_rect->hide();
4815 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4817 using namespace Gtkmm2ext;
4819 ArdourPrompter prompter (false);
4821 prompter.set_prompt (_("Name for region:"));
4822 prompter.set_initial_text (clicked_regionview->region()->name());
4823 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4824 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4825 prompter.show_all ();
4826 switch (prompter.run ()) {
4827 case Gtk::RESPONSE_ACCEPT:
4829 prompter.get_result(str);
4831 clicked_regionview->region()->set_name (str);
4839 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4841 drag_info.item = item;
4842 drag_info.motion_callback = &Editor::time_fx_motion;
4843 drag_info.finished_callback = &Editor::end_time_fx;
4847 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4851 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4853 RegionView* rv = clicked_regionview;
4855 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4856 snap_to (drag_info.current_pointer_frame);
4859 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4863 if (drag_info.current_pointer_frame > rv->region()->position()) {
4864 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4867 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4868 drag_info.first_move = false;
4870 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4874 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4876 clicked_regionview->get_time_axis_view().hide_timestretch ();
4878 if (drag_info.first_move) {
4882 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4883 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4885 begin_reversible_command (_("timestretch"));
4887 if (run_timestretch (selection->regions, percentage) == 0) {
4888 session->commit_reversible_command ();
4893 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4895 /* no brushing without a useful snap setting */
4898 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4901 switch (snap_mode) {
4903 return; /* can't work because it allows region to be placed anywhere */
4908 switch (snap_type) {
4911 case SnapToEditCursor:
4918 /* don't brush a copy over the original */
4920 if (pos == rv->region()->position()) {
4924 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4926 if (atv == 0 || !atv->is_audio_track()) {
4930 boost::shared_ptr<Playlist> playlist = atv->playlist();
4931 double speed = atv->get_diskstream()->speed();
4933 XMLNode &before = playlist->get_state();
4934 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4935 XMLNode &after = playlist->get_state();
4936 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4938 // playlist is frozen, so we have to update manually
4940 playlist->Modified(); /* EMIT SIGNAL */
4944 Editor::track_height_step_timeout ()
4947 struct timeval delta;
4949 gettimeofday (&now, 0);
4950 timersub (&now, &last_track_height_step_timestamp, &delta);
4952 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4953 current_stepping_trackview = 0;