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 // and change the initial grab loc/frame if this drag info wants us to
1527 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1528 drag_info.grab_frame = drag_info.current_pointer_frame;
1529 drag_info.grab_x = drag_info.current_pointer_x;
1530 drag_info.grab_y = drag_info.current_pointer_y;
1531 drag_info.last_pointer_frame = drag_info.grab_frame;
1532 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1537 switch (item_type) {
1538 case PlayheadCursorItem:
1539 case EditCursorItem:
1541 case GainControlPointItem:
1542 case RedirectAutomationControlPointItem:
1543 case GainAutomationControlPointItem:
1544 case PanAutomationControlPointItem:
1545 case TempoMarkerItem:
1546 case MeterMarkerItem:
1547 case RegionViewNameHighlight:
1548 case StartSelectionTrimItem:
1549 case EndSelectionTrimItem:
1552 case RedirectAutomationLineItem:
1553 case GainAutomationLineItem:
1554 case PanAutomationLineItem:
1555 case FadeInHandleItem:
1556 case FadeOutHandleItem:
1557 /* <CMT Additions> */
1558 case ImageFrameHandleStartItem:
1559 case ImageFrameHandleEndItem:
1560 case MarkerViewHandleStartItem:
1561 case MarkerViewHandleEndItem:
1562 /* </CMT Additions> */
1563 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1564 (event->motion.state & Gdk::BUTTON2_MASK))) {
1565 if (!from_autoscroll) {
1566 maybe_autoscroll (event);
1568 (this->*(drag_info.motion_callback)) (item, event);
1577 switch (mouse_mode) {
1582 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1583 (event->motion.state & GDK_BUTTON2_MASK))) {
1584 if (!from_autoscroll) {
1585 maybe_autoscroll (event);
1587 (this->*(drag_info.motion_callback)) (item, event);
1598 track_canvas_motion (event);
1599 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1607 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1609 if (drag_info.item == 0) {
1610 fatal << _("programming error: start_grab called without drag item") << endmsg;
1616 cursor = grabber_cursor;
1619 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1621 if (event->button.button == 2) {
1622 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1623 drag_info.y_constrained = true;
1624 drag_info.x_constrained = false;
1626 drag_info.y_constrained = false;
1627 drag_info.x_constrained = true;
1630 drag_info.x_constrained = false;
1631 drag_info.y_constrained = false;
1634 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1635 drag_info.last_pointer_frame = drag_info.grab_frame;
1636 drag_info.current_pointer_frame = drag_info.grab_frame;
1637 drag_info.current_pointer_x = drag_info.grab_x;
1638 drag_info.current_pointer_y = drag_info.grab_y;
1639 drag_info.cumulative_x_drag = 0;
1640 drag_info.cumulative_y_drag = 0;
1641 drag_info.first_move = true;
1642 drag_info.move_threshold_passed = false;
1643 drag_info.want_move_threshold = false;
1644 drag_info.pointer_frame_offset = 0;
1645 drag_info.brushing = false;
1646 drag_info.copied_location = 0;
1648 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1650 event->button.time);
1652 if (session && session->transport_rolling()) {
1653 drag_info.was_rolling = true;
1655 drag_info.was_rolling = false;
1658 switch (snap_type) {
1659 case SnapToRegionStart:
1660 case SnapToRegionEnd:
1661 case SnapToRegionSync:
1662 case SnapToRegionBoundary:
1663 build_region_boundary_cache ();
1671 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1673 drag_info.item->ungrab (0);
1674 drag_info.item = new_item;
1677 cursor = grabber_cursor;
1680 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1684 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1686 bool did_drag = false;
1688 stop_canvas_autoscroll ();
1690 if (drag_info.item == 0) {
1694 drag_info.item->ungrab (event->button.time);
1696 if (drag_info.finished_callback) {
1697 (this->*(drag_info.finished_callback)) (item, event);
1700 did_drag = !drag_info.first_move;
1702 hide_verbose_canvas_cursor();
1705 drag_info.copy = false;
1706 drag_info.motion_callback = 0;
1707 drag_info.finished_callback = 0;
1708 drag_info.last_trackview = 0;
1709 drag_info.last_frame_position = 0;
1710 drag_info.grab_frame = 0;
1711 drag_info.last_pointer_frame = 0;
1712 drag_info.current_pointer_frame = 0;
1713 drag_info.brushing = false;
1715 if (drag_info.copied_location) {
1716 delete drag_info.copied_location;
1717 drag_info.copied_location = 0;
1724 Editor::set_edit_cursor (GdkEvent* event)
1726 nframes_t pointer_frame = event_frame (event);
1728 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1729 if (snap_type != SnapToEditCursor) {
1730 snap_to (pointer_frame);
1734 edit_cursor->set_position (pointer_frame);
1735 edit_cursor_clock.set (pointer_frame);
1739 Editor::set_playhead_cursor (GdkEvent* event)
1741 nframes_t pointer_frame = event_frame (event);
1743 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1744 snap_to (pointer_frame);
1748 session->request_locate (pointer_frame, session->transport_rolling());
1753 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1755 drag_info.item = item;
1756 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1757 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1761 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1762 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1766 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1768 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1772 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1774 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1776 nframes_t fade_length;
1778 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1779 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1785 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1789 if (pos < (arv->region()->position() + 64)) {
1790 fade_length = 64; // this should be a minimum defined somewhere
1791 } else if (pos > arv->region()->last_frame()) {
1792 fade_length = arv->region()->length();
1794 fade_length = pos - arv->region()->position();
1796 /* mapover the region selection */
1798 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1800 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1806 tmp->reset_fade_in_shape_width (fade_length);
1809 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1811 drag_info.first_move = false;
1815 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1817 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1819 nframes_t fade_length;
1821 if (drag_info.first_move) return;
1823 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1824 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1829 if (pos < (arv->region()->position() + 64)) {
1830 fade_length = 64; // this should be a minimum defined somewhere
1831 } else if (pos > arv->region()->last_frame()) {
1832 fade_length = arv->region()->length();
1834 fade_length = pos - arv->region()->position();
1837 begin_reversible_command (_("change fade in length"));
1839 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1841 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1847 AutomationList& alist = tmp->audio_region()->fade_in();
1848 XMLNode &before = alist.get_state();
1850 tmp->audio_region()->set_fade_in_length (fade_length);
1852 XMLNode &after = alist.get_state();
1853 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1856 commit_reversible_command ();
1860 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1862 drag_info.item = item;
1863 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1864 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1868 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1869 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1873 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1875 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1879 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1881 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1883 nframes_t fade_length;
1885 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1886 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1892 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1896 if (pos > (arv->region()->last_frame() - 64)) {
1897 fade_length = 64; // this should really be a minimum fade defined somewhere
1899 else if (pos < arv->region()->position()) {
1900 fade_length = arv->region()->length();
1903 fade_length = arv->region()->last_frame() - pos;
1906 /* mapover the region selection */
1908 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1910 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1916 tmp->reset_fade_out_shape_width (fade_length);
1919 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1921 drag_info.first_move = false;
1925 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1927 if (drag_info.first_move) return;
1929 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1931 nframes_t fade_length;
1933 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1934 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1940 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1944 if (pos > (arv->region()->last_frame() - 64)) {
1945 fade_length = 64; // this should really be a minimum fade defined somewhere
1947 else if (pos < arv->region()->position()) {
1948 fade_length = arv->region()->length();
1951 fade_length = arv->region()->last_frame() - pos;
1954 begin_reversible_command (_("change fade out length"));
1956 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1958 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1964 AutomationList& alist = tmp->audio_region()->fade_out();
1965 XMLNode &before = alist.get_state();
1967 tmp->audio_region()->set_fade_out_length (fade_length);
1969 XMLNode &after = alist.get_state();
1970 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1973 commit_reversible_command ();
1977 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1979 drag_info.item = item;
1980 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1981 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1985 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1986 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1990 Cursor* cursor = (Cursor *) drag_info.data;
1992 if (cursor == playhead_cursor) {
1993 _dragging_playhead = true;
1995 if (session && drag_info.was_rolling) {
1996 session->request_stop ();
2000 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2002 show_verbose_time_cursor (cursor->current_frame, 10);
2006 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2008 Cursor* cursor = (Cursor *) drag_info.data;
2009 nframes_t adjusted_frame;
2011 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2012 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2018 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2019 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
2020 snap_to (adjusted_frame);
2024 if (adjusted_frame == drag_info.last_pointer_frame) return;
2026 cursor->set_position (adjusted_frame);
2028 if (cursor == edit_cursor) {
2029 edit_cursor_clock.set (cursor->current_frame);
2031 UpdateAllTransportClocks (cursor->current_frame);
2034 show_verbose_time_cursor (cursor->current_frame, 10);
2036 drag_info.last_pointer_frame = adjusted_frame;
2037 drag_info.first_move = false;
2041 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2043 if (drag_info.first_move) return;
2045 cursor_drag_motion_callback (item, event);
2047 _dragging_playhead = false;
2049 if (item == &playhead_cursor->canvas_item) {
2051 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2053 } else if (item == &edit_cursor->canvas_item) {
2054 edit_cursor->set_position (edit_cursor->current_frame);
2055 edit_cursor_clock.set (edit_cursor->current_frame);
2060 Editor::update_marker_drag_item (Location *location)
2062 double x1 = frame_to_pixel (location->start());
2063 double x2 = frame_to_pixel (location->end());
2065 if (location->is_mark()) {
2066 marker_drag_line_points.front().set_x(x1);
2067 marker_drag_line_points.back().set_x(x1);
2068 marker_drag_line->property_points() = marker_drag_line_points;
2071 range_marker_drag_rect->property_x1() = x1;
2072 range_marker_drag_rect->property_x2() = x2;
2077 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2081 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2082 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2088 Location *location = find_location_from_marker (marker, is_start);
2090 drag_info.item = item;
2091 drag_info.data = marker;
2092 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2093 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2097 drag_info.copied_location = new Location (*location);
2098 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2100 update_marker_drag_item (location);
2102 if (location->is_mark()) {
2103 marker_drag_line->show();
2104 marker_drag_line->raise_to_top();
2107 range_marker_drag_rect->show();
2108 range_marker_drag_rect->raise_to_top();
2111 if (is_start) show_verbose_time_cursor (location->start(), 10);
2112 else show_verbose_time_cursor (location->end(), 10);
2116 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2119 Marker* marker = (Marker *) drag_info.data;
2120 Location *real_location;
2121 Location *copy_location;
2123 bool move_both = false;
2127 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2128 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2134 nframes_t next = newframe;
2136 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2137 snap_to (newframe, 0, true);
2140 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2144 /* call this to find out if its the start or end */
2146 real_location = find_location_from_marker (marker, is_start);
2148 /* use the copy that we're "dragging" around */
2150 copy_location = drag_info.copied_location;
2152 f_delta = copy_location->end() - copy_location->start();
2154 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2158 if (copy_location->is_mark()) {
2161 copy_location->set_start (newframe);
2165 if (is_start) { // start-of-range marker
2168 copy_location->set_start (newframe);
2169 copy_location->set_end (newframe + f_delta);
2170 } else if (newframe < copy_location->end()) {
2171 copy_location->set_start (newframe);
2173 snap_to (next, 1, true);
2174 copy_location->set_end (next);
2175 copy_location->set_start (newframe);
2178 } else { // end marker
2181 copy_location->set_end (newframe);
2182 copy_location->set_start (newframe - f_delta);
2183 } else if (newframe > copy_location->start()) {
2184 copy_location->set_end (newframe);
2186 } else if (newframe > 0) {
2187 snap_to (next, -1, true);
2188 copy_location->set_start (next);
2189 copy_location->set_end (newframe);
2194 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2195 drag_info.first_move = false;
2197 update_marker_drag_item (copy_location);
2199 LocationMarkers* lm = find_location_markers (real_location);
2200 lm->set_position (copy_location->start(), copy_location->end());
2202 show_verbose_time_cursor (newframe, 10);
2206 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2208 if (drag_info.first_move) {
2209 marker_drag_motion_callback (item, event);
2213 Marker* marker = (Marker *) drag_info.data;
2217 begin_reversible_command ( _("move marker") );
2218 XMLNode &before = session->locations()->get_state();
2220 Location * location = find_location_from_marker (marker, is_start);
2223 if (location->is_mark()) {
2224 location->set_start (drag_info.copied_location->start());
2226 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2230 XMLNode &after = session->locations()->get_state();
2231 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2232 commit_reversible_command ();
2234 marker_drag_line->hide();
2235 range_marker_drag_rect->hide();
2239 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2242 MeterMarker* meter_marker;
2244 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2245 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2249 meter_marker = dynamic_cast<MeterMarker*> (marker);
2251 MetricSection& section (meter_marker->meter());
2253 if (!section.movable()) {
2257 drag_info.item = item;
2258 drag_info.data = marker;
2259 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2260 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2264 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2266 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2270 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2273 MeterMarker* meter_marker;
2275 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2276 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2280 meter_marker = dynamic_cast<MeterMarker*> (marker);
2282 // create a dummy marker for visual representation of moving the copy.
2283 // The actual copying is not done before we reach the finish callback.
2285 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2286 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2287 *new MeterSection(meter_marker->meter()));
2289 drag_info.item = &new_marker->the_item();
2290 drag_info.copy = true;
2291 drag_info.data = new_marker;
2292 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2293 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2297 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2299 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2303 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2305 MeterMarker* marker = (MeterMarker *) drag_info.data;
2306 nframes_t adjusted_frame;
2308 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2309 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2315 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2316 snap_to (adjusted_frame);
2319 if (adjusted_frame == drag_info.last_pointer_frame) return;
2321 marker->set_position (adjusted_frame);
2324 drag_info.last_pointer_frame = adjusted_frame;
2325 drag_info.first_move = false;
2327 show_verbose_time_cursor (adjusted_frame, 10);
2331 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2333 if (drag_info.first_move) return;
2335 meter_marker_drag_motion_callback (drag_info.item, event);
2337 MeterMarker* marker = (MeterMarker *) drag_info.data;
2340 TempoMap& map (session->tempo_map());
2341 map.bbt_time (drag_info.last_pointer_frame, when);
2343 if (drag_info.copy == true) {
2344 begin_reversible_command (_("copy meter mark"));
2345 XMLNode &before = map.get_state();
2346 map.add_meter (marker->meter(), when);
2347 XMLNode &after = map.get_state();
2348 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2349 commit_reversible_command ();
2351 // delete the dummy marker we used for visual representation of copying.
2352 // a new visual marker will show up automatically.
2355 begin_reversible_command (_("move meter mark"));
2356 XMLNode &before = map.get_state();
2357 map.move_meter (marker->meter(), when);
2358 XMLNode &after = map.get_state();
2359 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2360 commit_reversible_command ();
2365 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2368 TempoMarker* tempo_marker;
2370 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2371 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2375 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2376 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2380 MetricSection& section (tempo_marker->tempo());
2382 if (!section.movable()) {
2386 drag_info.item = item;
2387 drag_info.data = marker;
2388 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2389 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2393 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2394 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2398 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2401 TempoMarker* tempo_marker;
2403 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2404 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2408 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2409 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2413 // create a dummy marker for visual representation of moving the copy.
2414 // The actual copying is not done before we reach the finish callback.
2416 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2417 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2418 *new TempoSection(tempo_marker->tempo()));
2420 drag_info.item = &new_marker->the_item();
2421 drag_info.copy = true;
2422 drag_info.data = new_marker;
2423 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2424 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2428 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2430 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2434 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2436 TempoMarker* marker = (TempoMarker *) drag_info.data;
2437 nframes_t adjusted_frame;
2439 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2440 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2446 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2447 snap_to (adjusted_frame);
2450 if (adjusted_frame == drag_info.last_pointer_frame) return;
2452 /* OK, we've moved far enough to make it worth actually move the thing. */
2454 marker->set_position (adjusted_frame);
2456 show_verbose_time_cursor (adjusted_frame, 10);
2458 drag_info.last_pointer_frame = adjusted_frame;
2459 drag_info.first_move = false;
2463 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2465 if (drag_info.first_move) return;
2467 tempo_marker_drag_motion_callback (drag_info.item, event);
2469 TempoMarker* marker = (TempoMarker *) drag_info.data;
2472 TempoMap& map (session->tempo_map());
2473 map.bbt_time (drag_info.last_pointer_frame, when);
2475 if (drag_info.copy == true) {
2476 begin_reversible_command (_("copy tempo mark"));
2477 XMLNode &before = map.get_state();
2478 map.add_tempo (marker->tempo(), when);
2479 XMLNode &after = map.get_state();
2480 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2481 commit_reversible_command ();
2483 // delete the dummy marker we used for visual representation of copying.
2484 // a new visual marker will show up automatically.
2487 begin_reversible_command (_("move tempo mark"));
2488 XMLNode &before = map.get_state();
2489 map.move_tempo (marker->tempo(), when);
2490 XMLNode &after = map.get_state();
2491 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2492 commit_reversible_command ();
2497 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2499 ControlPoint* control_point;
2501 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2502 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2506 // We shouldn't remove the first or last gain point
2507 if (control_point->line.is_last_point(*control_point) ||
2508 control_point->line.is_first_point(*control_point)) {
2512 control_point->line.remove_point (*control_point);
2516 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2518 ControlPoint* control_point;
2520 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2521 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2525 control_point->line.remove_point (*control_point);
2529 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2531 ControlPoint* control_point;
2533 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2534 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2538 drag_info.item = item;
2539 drag_info.data = control_point;
2540 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2541 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2543 start_grab (event, fader_cursor);
2545 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2547 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2548 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2549 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2551 show_verbose_canvas_cursor ();
2555 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2557 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2559 double cx = drag_info.current_pointer_x;
2560 double cy = drag_info.current_pointer_y;
2562 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2563 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2565 if (drag_info.x_constrained) {
2566 cx = drag_info.grab_x;
2568 if (drag_info.y_constrained) {
2569 cy = drag_info.grab_y;
2572 cp->line.parent_group().w2i (cx, cy);
2576 cy = min ((double) cp->line.height(), cy);
2578 //translate cx to frames
2579 nframes_t cx_frames = unit_to_frame (cx);
2581 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2582 snap_to (cx_frames);
2585 float fraction = 1.0 - (cy / cp->line.height());
2589 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2595 cp->line.point_drag (*cp, cx_frames , fraction, push);
2597 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2599 drag_info.first_move = false;
2603 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2605 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2607 if (drag_info.first_move) {
2611 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2612 reset_point_selection ();
2616 control_point_drag_motion_callback (item, event);
2618 cp->line.end_drag (cp);
2622 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2624 switch (mouse_mode) {
2626 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2627 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2635 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2639 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2640 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2644 start_line_grab (al, event);
2648 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2652 nframes_t frame_within_region;
2654 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2658 cx = event->button.x;
2659 cy = event->button.y;
2660 line->parent_group().w2i (cx, cy);
2661 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2663 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2664 current_line_drag_info.after)) {
2665 /* no adjacent points */
2669 drag_info.item = &line->grab_item();
2670 drag_info.data = line;
2671 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2672 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2674 start_grab (event, fader_cursor);
2676 double fraction = 1.0 - (cy / line->height());
2678 line->start_drag (0, drag_info.grab_frame, fraction);
2680 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2681 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2682 show_verbose_canvas_cursor ();
2686 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2688 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2689 double cx = drag_info.current_pointer_x;
2690 double cy = drag_info.current_pointer_y;
2692 line->parent_group().w2i (cx, cy);
2695 fraction = 1.0 - (cy / line->height());
2699 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2705 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2707 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2711 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2713 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2714 line_drag_motion_callback (item, event);
2719 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2721 if (selection->regions.empty() || clicked_regionview == 0) {
2725 drag_info.copy = false;
2726 drag_info.item = item;
2727 drag_info.data = clicked_regionview;
2728 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2729 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2734 TimeAxisView* tvp = clicked_trackview;
2735 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2737 if (tv && tv->is_audio_track()) {
2738 speed = tv->get_diskstream()->speed();
2741 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2742 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2743 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2744 // we want a move threshold
2745 drag_info.want_move_threshold = true;
2747 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2749 begin_reversible_command (_("move region(s)"));
2753 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2755 if (selection->regions.empty() || clicked_regionview == 0) {
2759 drag_info.copy = true;
2760 drag_info.item = item;
2761 drag_info.data = clicked_regionview;
2765 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2766 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2769 if (atv && atv->is_audio_track()) {
2770 speed = atv->get_diskstream()->speed();
2773 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2774 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2775 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2776 // we want a move threshold
2777 drag_info.want_move_threshold = true;
2778 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2779 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2780 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2784 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2786 if (selection->regions.empty() || clicked_regionview == 0) {
2790 drag_info.copy = false;
2791 drag_info.item = item;
2792 drag_info.data = clicked_regionview;
2793 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2794 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2799 TimeAxisView* tvp = clicked_trackview;
2800 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2802 if (tv && tv->is_audio_track()) {
2803 speed = tv->get_diskstream()->speed();
2806 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2807 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2808 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2809 // we want a move threshold
2810 drag_info.want_move_threshold = true;
2811 drag_info.brushing = true;
2813 begin_reversible_command (_("Drag region brush"));
2817 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2821 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2822 nframes_t pending_region_position = 0;
2823 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2824 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2825 bool clamp_y_axis = false;
2826 vector<int32_t> height_list(512) ;
2827 vector<int32_t>::iterator j;
2829 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2831 drag_info.want_move_threshold = false; // don't copy again
2833 /* this is committed in the grab finished callback. */
2835 begin_reversible_command (_("Drag region copy"));
2837 /* duplicate the region(s) */
2839 vector<RegionView*> new_regionviews;
2841 set<boost::shared_ptr<Playlist> > affected_playlists;
2842 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2844 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2849 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
2850 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2852 insert_result = affected_playlists.insert (to_playlist);
2853 if (insert_result.second) {
2854 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
2857 latest_regionview = 0;
2859 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2861 /* create a new region with the same name. */
2863 // FIXME: ew. need a (virtual) Region::duplicate() or something?
2865 boost::shared_ptr<Region> newregion;
2866 boost::shared_ptr<Region> ar;
2868 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
2869 newregion = RegionFactory::create (ar);
2871 assert(newregion != 0);
2873 /* if the original region was locked, we don't care */
2875 newregion->set_locked (false);
2877 to_playlist->add_region (newregion, (nframes_t) (rv->region()->position() * atv->get_diskstream()->speed()));
2881 if (latest_regionview) {
2882 new_regionviews.push_back (latest_regionview);
2888 if (new_regionviews.empty()) {
2892 /* reset selection to new regionviews */
2894 selection->set (new_regionviews);
2896 /* reset drag_info data to reflect the fact that we are dragging the copies */
2898 drag_info.data = new_regionviews.front();
2899 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2902 /* Which trackview is this ? */
2904 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2905 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2907 /* The region motion is only processed if the pointer is over
2911 if (!tv || !tv->is_audio_track()) {
2912 /* To make sure we hide the verbose canvas cursor when the mouse is
2913 not held over and audiotrack.
2915 hide_verbose_canvas_cursor ();
2919 original_pointer_order = drag_info.last_trackview->order;
2921 /************************************************************
2923 ************************************************************/
2925 if (drag_info.brushing) {
2926 clamp_y_axis = true;
2931 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2933 int32_t children = 0, numtracks = 0;
2934 // XXX hard coding track limit, oh my, so very very bad
2935 bitset <1024> tracks (0x00);
2936 /* get a bitmask representing the visible tracks */
2938 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2939 TimeAxisView *tracklist_timeview;
2940 tracklist_timeview = (*i);
2941 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2942 list<TimeAxisView*> children_list;
2944 /* zeroes are audio tracks. ones are other types. */
2946 if (!atv2->hidden()) {
2948 if (visible_y_high < atv2->order) {
2949 visible_y_high = atv2->order;
2951 if (visible_y_low > atv2->order) {
2952 visible_y_low = atv2->order;
2955 if (!atv2->is_audio_track()) {
2956 tracks = tracks |= (0x01 << atv2->order);
2959 height_list[atv2->order] = (*i)->height;
2961 if ((children_list = atv2->get_child_list()).size() > 0) {
2962 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2963 tracks = tracks |= (0x01 << (atv2->order + children));
2964 height_list[atv2->order + children] = (*j)->height;
2972 /* find the actual span according to the canvas */
2974 canvas_pointer_y_span = pointer_y_span;
2975 if (drag_info.last_trackview->order >= tv->order) {
2977 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2978 if (height_list[y] == 0 ) {
2979 canvas_pointer_y_span--;
2984 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2985 if ( height_list[y] == 0 ) {
2986 canvas_pointer_y_span++;
2991 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2992 RegionView* rv2 = (*i);
2993 double ix1, ix2, iy1, iy2;
2996 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2997 rv2->get_canvas_group()->i2w (ix1, iy1);
2998 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2999 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3001 if (atv2->order != original_pointer_order) {
3002 /* this isn't the pointer track */
3004 if (canvas_pointer_y_span > 0) {
3006 /* moving up the canvas */
3007 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3009 int32_t visible_tracks = 0;
3010 while (visible_tracks < canvas_pointer_y_span ) {
3013 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3014 /* we're passing through a hidden track */
3019 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3020 clamp_y_axis = true;
3024 clamp_y_axis = true;
3027 } else if (canvas_pointer_y_span < 0) {
3029 /*moving down the canvas*/
3031 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3034 int32_t visible_tracks = 0;
3036 while (visible_tracks > canvas_pointer_y_span ) {
3039 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3043 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3044 clamp_y_axis = true;
3049 clamp_y_axis = true;
3055 /* this is the pointer's track */
3056 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3057 clamp_y_axis = true;
3058 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3059 clamp_y_axis = true;
3067 } else if (drag_info.last_trackview == tv) {
3068 clamp_y_axis = true;
3072 if (!clamp_y_axis) {
3073 drag_info.last_trackview = tv;
3076 /************************************************************
3078 ************************************************************/
3080 /* compute the amount of pointer motion in frames, and where
3081 the region would be if we moved it by that much.
3084 if (drag_info.move_threshold_passed) {
3086 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3088 nframes_t sync_frame;
3089 nframes_t sync_offset;
3092 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3094 sync_offset = rv->region()->sync_offset (sync_dir);
3095 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3097 /* we snap if the snap modifier is not enabled.
3100 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3101 snap_to (sync_frame);
3104 if (sync_frame - sync_offset <= sync_frame) {
3105 pending_region_position = sync_frame - (sync_dir*sync_offset);
3107 pending_region_position = 0;
3111 pending_region_position = 0;
3114 if (pending_region_position > max_frames - rv->region()->length()) {
3115 pending_region_position = drag_info.last_frame_position;
3118 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3120 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3122 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3123 to make it appear at the new location.
3126 if (pending_region_position > drag_info.last_frame_position) {
3127 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3129 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3132 drag_info.last_frame_position = pending_region_position;
3139 /* threshold not passed */
3144 /*************************************************************
3146 ************************************************************/
3148 if (x_delta == 0 && (pointer_y_span == 0)) {
3149 /* haven't reached next snap point, and we're not switching
3150 trackviews. nothing to do.
3156 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3158 RegionView* rv2 = (*i);
3160 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3162 double ix1, ix2, iy1, iy2;
3163 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3164 rv2->get_canvas_group()->i2w (ix1, iy1);
3173 /*************************************************************
3175 ************************************************************/
3177 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3178 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3180 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3182 RegionView* rv = (*i);
3183 double ix1, ix2, iy1, iy2;
3184 int32_t temp_pointer_y_span = pointer_y_span;
3186 /* get item BBox, which will be relative to parent. so we have
3187 to query on a child, then convert to world coordinates using
3191 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3192 rv->get_canvas_group()->i2w (ix1, iy1);
3193 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3194 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3195 AudioTimeAxisView* temp_atv;
3197 if ((pointer_y_span != 0) && !clamp_y_axis) {
3200 for (j = height_list.begin(); j!= height_list.end(); j++) {
3201 if (x == canvas_atv->order) {
3202 /* we found the track the region is on */
3203 if (x != original_pointer_order) {
3204 /*this isn't from the same track we're dragging from */
3205 temp_pointer_y_span = canvas_pointer_y_span;
3207 while (temp_pointer_y_span > 0) {
3208 /* we're moving up canvas-wise,
3209 so we need to find the next track height
3211 if (j != height_list.begin()) {
3214 if (x != original_pointer_order) {
3215 /* we're not from the dragged track, so ignore hidden tracks. */
3217 temp_pointer_y_span++;
3221 temp_pointer_y_span--;
3223 while (temp_pointer_y_span < 0) {
3225 if (x != original_pointer_order) {
3227 temp_pointer_y_span--;
3231 if (j != height_list.end()) {
3234 temp_pointer_y_span++;
3236 /* find out where we'll be when we move and set height accordingly */
3238 tvp2 = trackview_by_y_position (iy1 + y_delta);
3239 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3240 rv->set_height (temp_atv->height);
3242 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3243 personally, i think this can confuse things, but never mind.
3246 //const GdkColor& col (temp_atv->view->get_region_color());
3247 //rv->set_color (const_cast<GdkColor&>(col));
3254 /* prevent the regionview from being moved to before
3255 the zero position on the canvas.
3260 if (-x_delta > ix1) {
3263 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3264 x_delta = max_frames - rv->region()->last_frame();
3267 if (drag_info.first_move) {
3269 /* hide any dependent views */
3271 rv->get_time_axis_view().hide_dependent_views (*rv);
3273 /* this is subtle. raising the regionview itself won't help,
3274 because raise_to_top() just puts the item on the top of
3275 its parent's stack. so, we need to put the trackview canvas_display group
3276 on the top, since its parent is the whole canvas.
3279 rv->get_canvas_group()->raise_to_top();
3280 rv->get_time_axis_view().canvas_display->raise_to_top();
3281 cursor_group->raise_to_top();
3283 /* freeze the playlists from notifying till
3287 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3288 if (atv && atv->is_audio_track()) {
3289 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>(atv->get_diskstream()->playlist());
3291 /* only freeze and capture state once */
3293 insert_result = motion_frozen_playlists.insert (pl);
3294 if (insert_result.second) {
3296 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
3300 rv->region()->set_opaque(false);
3303 if (drag_info.brushing) {
3304 mouse_brush_insert_region (rv, pending_region_position);
3306 rv->move (x_delta, y_delta);
3310 if (drag_info.first_move) {
3311 cursor_group->raise_to_top();
3314 drag_info.first_move = false;
3316 if (x_delta != 0 && !drag_info.brushing) {
3317 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3323 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3326 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3327 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3328 bool nocommit = true;
3330 RouteTimeAxisView* atv;
3331 bool regionview_y_movement;
3332 bool regionview_x_movement;
3334 /* first_move is set to false if the regionview has been moved in the
3338 if (drag_info.first_move) {
3345 /* The regionview has been moved at some stage during the grab so we need
3346 to account for any mouse movement between this event and the last one.
3349 region_drag_motion_callback (item, event);
3351 if (drag_info.brushing) {
3352 /* all changes were made during motion event handlers */
3356 /* adjust for track speed */
3359 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3360 if (atv && atv->get_diskstream()) {
3361 speed = atv->get_diskstream()->speed();
3364 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3365 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3367 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3368 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3370 if (regionview_y_movement) {
3372 /* motion between tracks */
3374 list<RegionView*> new_selection;
3376 /* moved to a different audio track. */
3378 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3380 RegionView* rv2 = (*i);
3382 /* the region that used to be in the old playlist is not
3383 moved to the new one - we make a copy of it. as a result,
3384 any existing editor for the region should no longer be
3388 if (!drag_info.copy) {
3389 rv2->hide_region_editor();
3391 new_selection.push_back (rv2);
3395 /* first, freeze the target tracks */
3397 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3399 boost::shared_ptr<Playlist> from_playlist;
3400 boost::shared_ptr<Playlist> to_playlist;
3402 double ix1, ix2, iy1, iy2;
3404 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3405 (*i)->get_canvas_group()->i2w (ix1, iy1);
3406 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3407 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3409 (*i)->region()->set_opaque (true);
3411 from_playlist = (*i)->region()->playlist();
3412 to_playlist = atv2->playlist();
3414 /* the from_playlist was frozen in the "first_move" case
3415 of the motion handler. the insert can fail,
3416 but that doesn't matter. it just means
3417 we already have the playlist in the list.
3420 motion_frozen_playlists.insert (from_playlist);
3422 /* only freeze the to_playlist once */
3424 insert_result = motion_frozen_playlists.insert(to_playlist);
3425 if (insert_result.second) {
3426 to_playlist->freeze();
3427 session->add_command(new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3432 /* now do it again with the actual operations */
3434 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3436 boost::shared_ptr<Playlist> from_playlist;
3437 boost::shared_ptr<Playlist> to_playlist;
3439 double ix1, ix2, iy1, iy2;
3441 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3442 (*i)->get_canvas_group()->i2w (ix1, iy1);
3443 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3444 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3446 from_playlist = (*i)->region()->playlist();
3447 to_playlist = atv2->playlist();
3449 latest_regionview = 0;
3451 where = (nframes_t) (unit_to_frame (ix1) * speed);
3452 boost::shared_ptr<Region> new_region (RegionFactory::create ((*i)->region()));
3454 from_playlist->remove_region (((*i)->region()));
3456 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3457 to_playlist->add_region (new_region, where);
3460 if (latest_regionview) {
3461 selection->add (latest_regionview);
3467 /* motion within a single track */
3469 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3473 if (rv->region()->locked()) {
3477 if (regionview_x_movement) {
3478 double ownspeed = 1.0;
3479 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3481 if (atv && atv->get_diskstream()) {
3482 ownspeed = atv->get_diskstream()->speed();
3485 /* base the new region position on the current position of the regionview.*/
3487 double ix1, ix2, iy1, iy2;
3489 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3490 rv->get_canvas_group()->i2w (ix1, iy1);
3491 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3495 where = rv->region()->position();
3498 rv->get_time_axis_view().reveal_dependent_views (*rv);
3500 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3502 rv->region()->set_position (where, (void *) this);
3503 rv->region()->set_opaque (true);
3508 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3510 session->add_command (new MementoCommand<Playlist>(*((*p).get()), 0, & (*p)->get_state()));
3513 motion_frozen_playlists.clear ();
3516 commit_reversible_command ();
3521 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3523 /* Either add to or set the set the region selection, unless
3524 this is an alignment click (control used)
3527 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3528 TimeAxisView* tv = &rv.get_time_axis_view();
3529 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3531 if (atv && atv->is_audio_track()) {
3532 speed = atv->get_diskstream()->speed();
3535 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3537 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3539 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3541 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3545 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3551 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3557 nframes_t frame_rate;
3564 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3565 case AudioClock::BBT:
3566 session->bbt_time (frame, bbt);
3567 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3570 case AudioClock::SMPTE:
3571 session->smpte_time (frame, smpte);
3572 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3575 case AudioClock::MinSec:
3576 /* XXX this is copied from show_verbose_duration_cursor() */
3577 frame_rate = session->frame_rate();
3578 hours = frame / (frame_rate * 3600);
3579 frame = frame % (frame_rate * 3600);
3580 mins = frame / (frame_rate * 60);
3581 frame = frame % (frame_rate * 60);
3582 secs = (float) frame / (float) frame_rate;
3583 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3587 snprintf (buf, sizeof(buf), "%u", frame);
3591 if (xpos >= 0 && ypos >=0) {
3592 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3595 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3597 show_verbose_canvas_cursor ();
3601 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3608 nframes_t distance, frame_rate;
3610 Meter meter_at_start(session->tempo_map().meter_at(start));
3616 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3617 case AudioClock::BBT:
3618 session->bbt_time (start, sbbt);
3619 session->bbt_time (end, ebbt);
3622 /* XXX this computation won't work well if the
3623 user makes a selection that spans any meter changes.
3626 ebbt.bars -= sbbt.bars;
3627 if (ebbt.beats >= sbbt.beats) {
3628 ebbt.beats -= sbbt.beats;
3631 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3633 if (ebbt.ticks >= sbbt.ticks) {
3634 ebbt.ticks -= sbbt.ticks;
3637 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3640 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3643 case AudioClock::SMPTE:
3644 session->smpte_duration (end - start, smpte);
3645 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3648 case AudioClock::MinSec:
3649 /* XXX this stuff should be elsewhere.. */
3650 distance = end - start;
3651 frame_rate = session->frame_rate();
3652 hours = distance / (frame_rate * 3600);
3653 distance = distance % (frame_rate * 3600);
3654 mins = distance / (frame_rate * 60);
3655 distance = distance % (frame_rate * 60);
3656 secs = (float) distance / (float) frame_rate;
3657 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3661 snprintf (buf, sizeof(buf), "%u", end - start);
3665 if (xpos >= 0 && ypos >=0) {
3666 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3669 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3671 show_verbose_canvas_cursor ();
3675 Editor::collect_new_region_view (RegionView* rv)
3677 latest_regionview = rv;
3681 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3683 if (clicked_regionview == 0) {
3687 /* lets try to create new Region for the selection */
3689 vector<boost::shared_ptr<AudioRegion> > new_regions;
3690 create_region_from_selection (new_regions);
3692 if (new_regions.empty()) {
3696 /* XXX fix me one day to use all new regions */
3698 boost::shared_ptr<Region> region (new_regions.front());
3700 /* add it to the current stream/playlist.
3702 tricky: the streamview for the track will add a new regionview. we will
3703 catch the signal it sends when it creates the regionview to
3704 set the regionview we want to then drag.
3707 latest_regionview = 0;
3708 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3710 /* A selection grab currently creates two undo/redo operations, one for
3711 creating the new region and another for moving it.
3714 begin_reversible_command (_("selection grab"));
3716 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3718 XMLNode *before = &(playlist->get_state());
3719 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3720 XMLNode *after = &(playlist->get_state());
3721 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3723 commit_reversible_command ();
3727 if (latest_regionview == 0) {
3728 /* something went wrong */
3732 /* we need to deselect all other regionviews, and select this one
3733 i'm ignoring undo stuff, because the region creation will take care of it */
3734 selection->set (latest_regionview);
3736 drag_info.item = latest_regionview->get_canvas_group();
3737 drag_info.data = latest_regionview;
3738 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3739 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3743 drag_info.last_trackview = clicked_trackview;
3744 drag_info.last_frame_position = latest_regionview->region()->position();
3745 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3747 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3751 Editor::cancel_selection ()
3753 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3754 (*i)->hide_selection ();
3756 begin_reversible_command (_("cancel selection"));
3757 selection->clear ();
3758 clicked_selection = 0;
3759 commit_reversible_command ();
3763 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3765 nframes_t start = 0;
3772 drag_info.item = item;
3773 drag_info.motion_callback = &Editor::drag_selection;
3774 drag_info.finished_callback = &Editor::end_selection_op;
3779 case CreateSelection:
3780 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3781 drag_info.copy = true;
3783 drag_info.copy = false;
3785 start_grab (event, selector_cursor);
3788 case SelectionStartTrim:
3789 if (clicked_trackview) {
3790 clicked_trackview->order_selection_trims (item, true);
3792 start_grab (event, trimmer_cursor);
3793 start = selection->time[clicked_selection].start;
3794 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3797 case SelectionEndTrim:
3798 if (clicked_trackview) {
3799 clicked_trackview->order_selection_trims (item, false);
3801 start_grab (event, trimmer_cursor);
3802 end = selection->time[clicked_selection].end;
3803 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3807 start = selection->time[clicked_selection].start;
3809 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3813 if (selection_op == SelectionMove) {
3814 show_verbose_time_cursor(start, 10);
3816 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3821 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3823 nframes_t start = 0;
3826 nframes_t pending_position;
3828 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3829 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3832 pending_position = 0;
3835 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3836 snap_to (pending_position);
3839 /* only alter selection if the current frame is
3840 different from the last frame position (adjusted)
3843 if (pending_position == drag_info.last_pointer_frame) return;
3845 switch (selection_op) {
3846 case CreateSelection:
3848 if (drag_info.first_move) {
3849 snap_to (drag_info.grab_frame);
3852 if (pending_position < drag_info.grab_frame) {
3853 start = pending_position;
3854 end = drag_info.grab_frame;
3856 end = pending_position;
3857 start = drag_info.grab_frame;
3860 /* first drag: Either add to the selection
3861 or create a new selection->
3864 if (drag_info.first_move) {
3866 begin_reversible_command (_("range selection"));
3868 if (drag_info.copy) {
3869 /* adding to the selection */
3870 clicked_selection = selection->add (start, end);
3871 drag_info.copy = false;
3873 /* new selection-> */
3874 clicked_selection = selection->set (clicked_trackview, start, end);
3879 case SelectionStartTrim:
3881 if (drag_info.first_move) {
3882 begin_reversible_command (_("trim selection start"));
3885 start = selection->time[clicked_selection].start;
3886 end = selection->time[clicked_selection].end;
3888 if (pending_position > end) {
3891 start = pending_position;
3895 case SelectionEndTrim:
3897 if (drag_info.first_move) {
3898 begin_reversible_command (_("trim selection end"));
3901 start = selection->time[clicked_selection].start;
3902 end = selection->time[clicked_selection].end;
3904 if (pending_position < start) {
3907 end = pending_position;
3914 if (drag_info.first_move) {
3915 begin_reversible_command (_("move selection"));
3918 start = selection->time[clicked_selection].start;
3919 end = selection->time[clicked_selection].end;
3921 length = end - start;
3923 start = pending_position;
3926 end = start + length;
3931 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3932 start_canvas_autoscroll (1);
3936 selection->replace (clicked_selection, start, end);
3939 drag_info.last_pointer_frame = pending_position;
3940 drag_info.first_move = false;
3942 if (selection_op == SelectionMove) {
3943 show_verbose_time_cursor(start, 10);
3945 show_verbose_time_cursor(pending_position, 10);
3950 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3952 if (!drag_info.first_move) {
3953 drag_selection (item, event);
3954 /* XXX this is not object-oriented programming at all. ick */
3955 if (selection->time.consolidate()) {
3956 selection->TimeChanged ();
3958 commit_reversible_command ();
3960 /* just a click, no pointer movement.*/
3962 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3964 selection->clear_time();
3969 /* XXX what happens if its a music selection? */
3970 session->set_audio_range (selection->time);
3971 stop_canvas_autoscroll ();
3975 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3978 TimeAxisView* tvp = clicked_trackview;
3979 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3981 if (tv && tv->is_audio_track()) {
3982 speed = tv->get_diskstream()->speed();
3985 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3986 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3987 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3989 motion_frozen_playlists.clear();
3991 //drag_info.item = clicked_regionview->get_name_highlight();
3992 drag_info.item = item;
3993 drag_info.motion_callback = &Editor::trim_motion_callback;
3994 drag_info.finished_callback = &Editor::trim_finished_callback;
3996 start_grab (event, trimmer_cursor);
3998 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3999 trim_op = ContentsTrim;
4001 /* These will get overridden for a point trim.*/
4002 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4003 /* closer to start */
4004 trim_op = StartTrim;
4005 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4013 show_verbose_time_cursor(region_start, 10);
4016 show_verbose_time_cursor(region_end, 10);
4019 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4025 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4027 RegionView* rv = clicked_regionview;
4028 nframes_t frame_delta = 0;
4029 bool left_direction;
4030 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4032 /* snap modifier works differently here..
4033 its' current state has to be passed to the
4034 various trim functions in order to work properly
4038 TimeAxisView* tvp = clicked_trackview;
4039 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4040 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4042 if (tv && tv->is_audio_track()) {
4043 speed = tv->get_diskstream()->speed();
4046 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4047 left_direction = true;
4049 left_direction = false;
4053 snap_to (drag_info.current_pointer_frame);
4056 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4060 if (drag_info.first_move) {
4066 trim_type = "Region start trim";
4069 trim_type = "Region end trim";
4072 trim_type = "Region content trim";
4076 begin_reversible_command (trim_type);
4078 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4079 (*i)->region()->set_opaque(false);
4080 (*i)->region()->freeze ();
4082 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4084 arv->temporarily_hide_envelope ();
4086 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4087 insert_result = motion_frozen_playlists.insert (pl);
4088 if (insert_result.second) {
4089 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4094 if (left_direction) {
4095 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4097 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4102 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4105 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4106 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4112 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4115 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4116 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4123 bool swap_direction = false;
4125 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4126 swap_direction = true;
4129 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4130 i != selection->regions.by_layer().end(); ++i)
4132 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4140 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4143 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4146 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4150 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4151 drag_info.first_move = false;
4155 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4157 boost::shared_ptr<Region> region (rv.region());
4159 if (region->locked()) {
4163 nframes_t new_bound;
4166 TimeAxisView* tvp = clicked_trackview;
4167 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4169 if (tv && tv->is_audio_track()) {
4170 speed = tv->get_diskstream()->speed();
4173 if (left_direction) {
4174 if (swap_direction) {
4175 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4177 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4180 if (swap_direction) {
4181 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4183 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4188 snap_to (new_bound);
4190 region->trim_start ((nframes_t) (new_bound * speed), this);
4191 rv.region_changed (StartChanged);
4195 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4197 boost::shared_ptr<Region> region (rv.region());
4199 if (region->locked()) {
4203 nframes_t new_bound;
4206 TimeAxisView* tvp = clicked_trackview;
4207 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4209 if (tv && tv->is_audio_track()) {
4210 speed = tv->get_diskstream()->speed();
4213 if (left_direction) {
4214 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4216 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4220 snap_to (new_bound, (left_direction ? 0 : 1));
4223 region->trim_front ((nframes_t) (new_bound * speed), this);
4225 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4229 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4231 boost::shared_ptr<Region> region (rv.region());
4233 if (region->locked()) {
4237 nframes_t new_bound;
4240 TimeAxisView* tvp = clicked_trackview;
4241 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4243 if (tv && tv->is_audio_track()) {
4244 speed = tv->get_diskstream()->speed();
4247 if (left_direction) {
4248 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4250 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4254 snap_to (new_bound);
4256 region->trim_end ((nframes_t) (new_bound * speed), this);
4257 rv.region_changed (LengthChanged);
4261 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4263 if (!drag_info.first_move) {
4264 trim_motion_callback (item, event);
4266 if (!clicked_regionview->get_selected()) {
4267 thaw_region_after_trim (*clicked_regionview);
4270 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4271 i != selection->regions.by_layer().end(); ++i)
4273 thaw_region_after_trim (**i);
4274 (*i)->region()->set_opaque(true);
4278 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4280 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4283 motion_frozen_playlists.clear ();
4285 commit_reversible_command();
4287 /* no mouse movement */
4293 Editor::point_trim (GdkEvent* event)
4295 RegionView* rv = clicked_regionview;
4296 nframes_t new_bound = drag_info.current_pointer_frame;
4298 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4299 snap_to (new_bound);
4302 /* Choose action dependant on which button was pressed */
4303 switch (event->button.button) {
4305 trim_op = StartTrim;
4306 begin_reversible_command (_("Start point trim"));
4308 if (rv->get_selected()) {
4310 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4311 i != selection->regions.by_layer().end(); ++i)
4313 if (!(*i)->region()->locked()) {
4314 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4315 XMLNode &before = pl->get_state();
4316 (*i)->region()->trim_front (new_bound, this);
4317 XMLNode &after = pl->get_state();
4318 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4324 if (!rv->region()->locked()) {
4325 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4326 XMLNode &before = pl->get_state();
4327 rv->region()->trim_front (new_bound, this);
4328 XMLNode &after = pl->get_state();
4329 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4333 commit_reversible_command();
4338 begin_reversible_command (_("End point trim"));
4340 if (rv->get_selected()) {
4342 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4344 if (!(*i)->region()->locked()) {
4345 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4346 XMLNode &before = pl->get_state();
4347 (*i)->region()->trim_end (new_bound, this);
4348 XMLNode &after = pl->get_state();
4349 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4355 if (!rv->region()->locked()) {
4356 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4357 XMLNode &before = pl->get_state();
4358 rv->region()->trim_end (new_bound, this);
4359 XMLNode &after = pl->get_state();
4360 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4364 commit_reversible_command();
4373 Editor::thaw_region_after_trim (RegionView& rv)
4375 boost::shared_ptr<Region> region (rv.region());
4377 if (region->locked()) {
4381 region->thaw (_("trimmed region"));
4382 XMLNode &after = region->playlist()->get_state();
4383 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4385 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4387 arv->unhide_envelope ();
4391 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4396 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4397 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4401 Location* location = find_location_from_marker (marker, is_start);
4402 location->set_hidden (true, this);
4407 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4413 drag_info.item = item;
4414 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4415 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4417 range_marker_op = op;
4419 if (!temp_location) {
4420 temp_location = new Location;
4424 case CreateRangeMarker:
4425 case CreateTransportMarker:
4427 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4428 drag_info.copy = true;
4430 drag_info.copy = false;
4432 start_grab (event, selector_cursor);
4436 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4441 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4443 nframes_t start = 0;
4445 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4447 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4448 snap_to (drag_info.current_pointer_frame);
4451 /* only alter selection if the current frame is
4452 different from the last frame position.
4455 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4457 switch (range_marker_op) {
4458 case CreateRangeMarker:
4459 case CreateTransportMarker:
4460 if (drag_info.first_move) {
4461 snap_to (drag_info.grab_frame);
4464 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4465 start = drag_info.current_pointer_frame;
4466 end = drag_info.grab_frame;
4468 end = drag_info.current_pointer_frame;
4469 start = drag_info.grab_frame;
4472 /* first drag: Either add to the selection
4473 or create a new selection.
4476 if (drag_info.first_move) {
4478 temp_location->set (start, end);
4482 update_marker_drag_item (temp_location);
4483 range_marker_drag_rect->show();
4484 range_marker_drag_rect->raise_to_top();
4490 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4491 start_canvas_autoscroll (1);
4495 temp_location->set (start, end);
4497 double x1 = frame_to_pixel (start);
4498 double x2 = frame_to_pixel (end);
4499 crect->property_x1() = x1;
4500 crect->property_x2() = x2;
4502 update_marker_drag_item (temp_location);
4505 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4506 drag_info.first_move = false;
4508 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4513 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4515 Location * newloc = 0;
4518 if (!drag_info.first_move) {
4519 drag_range_markerbar_op (item, event);
4521 switch (range_marker_op) {
4522 case CreateRangeMarker:
4524 begin_reversible_command (_("new range marker"));
4525 XMLNode &before = session->locations()->get_state();
4526 session->locations()->next_available_name(rangename,"unnamed");
4527 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4528 session->locations()->add (newloc, true);
4529 XMLNode &after = session->locations()->get_state();
4530 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4531 commit_reversible_command ();
4533 range_bar_drag_rect->hide();
4534 range_marker_drag_rect->hide();
4538 case CreateTransportMarker:
4539 // popup menu to pick loop or punch
4540 new_transport_marker_context_menu (&event->button, item);
4545 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4547 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4552 start = session->locations()->first_mark_before (drag_info.grab_frame);
4553 end = session->locations()->first_mark_after (drag_info.grab_frame);
4555 if (end == max_frames) {
4556 end = session->current_end_frame ();
4560 start = session->current_start_frame ();
4563 switch (mouse_mode) {
4565 /* find the two markers on either side and then make the selection from it */
4566 cerr << "select between " << start << " .. " << end << endl;
4567 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4571 /* find the two markers on either side of the click and make the range out of it */
4572 selection->set (0, start, end);
4581 stop_canvas_autoscroll ();
4587 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4589 drag_info.item = item;
4590 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4591 drag_info.finished_callback = &Editor::end_mouse_zoom;
4593 start_grab (event, zoom_cursor);
4595 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4599 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4604 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4605 snap_to (drag_info.current_pointer_frame);
4607 if (drag_info.first_move) {
4608 snap_to (drag_info.grab_frame);
4612 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4614 /* base start and end on initial click position */
4615 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4616 start = drag_info.current_pointer_frame;
4617 end = drag_info.grab_frame;
4619 end = drag_info.current_pointer_frame;
4620 start = drag_info.grab_frame;
4625 if (drag_info.first_move) {
4627 zoom_rect->raise_to_top();
4630 reposition_zoom_rect(start, end);
4632 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4633 drag_info.first_move = false;
4635 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4640 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4642 if (!drag_info.first_move) {
4643 drag_mouse_zoom (item, event);
4645 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4646 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4648 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4651 temporal_zoom_to_frame (false, drag_info.grab_frame);
4653 temporal_zoom_step (false);
4654 center_screen (drag_info.grab_frame);
4662 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4664 double x1 = frame_to_pixel (start);
4665 double x2 = frame_to_pixel (end);
4666 double y2 = full_canvas_height - 1.0;
4668 zoom_rect->property_x1() = x1;
4669 zoom_rect->property_y1() = 1.0;
4670 zoom_rect->property_x2() = x2;
4671 zoom_rect->property_y2() = y2;
4675 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4677 drag_info.item = item;
4678 drag_info.motion_callback = &Editor::drag_rubberband_select;
4679 drag_info.finished_callback = &Editor::end_rubberband_select;
4681 start_grab (event, cross_hair_cursor);
4683 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4687 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4694 /* use a bigger drag threshold than the default */
4696 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4700 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4701 if (drag_info.first_move) {
4702 snap_to (drag_info.grab_frame);
4704 snap_to (drag_info.current_pointer_frame);
4707 /* base start and end on initial click position */
4709 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4710 start = drag_info.current_pointer_frame;
4711 end = drag_info.grab_frame;
4713 end = drag_info.current_pointer_frame;
4714 start = drag_info.grab_frame;
4717 if (drag_info.current_pointer_y < drag_info.grab_y) {
4718 y1 = drag_info.current_pointer_y;
4719 y2 = drag_info.grab_y;
4721 y2 = drag_info.current_pointer_y;
4722 y1 = drag_info.grab_y;
4726 if (start != end || y1 != y2) {
4728 double x1 = frame_to_pixel (start);
4729 double x2 = frame_to_pixel (end);
4731 rubberband_rect->property_x1() = x1;
4732 rubberband_rect->property_y1() = y1;
4733 rubberband_rect->property_x2() = x2;
4734 rubberband_rect->property_y2() = y2;
4736 rubberband_rect->show();
4737 rubberband_rect->raise_to_top();
4739 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4740 drag_info.first_move = false;
4742 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4747 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4749 if (!drag_info.first_move) {
4751 drag_rubberband_select (item, event);
4754 if (drag_info.current_pointer_y < drag_info.grab_y) {
4755 y1 = drag_info.current_pointer_y;
4756 y2 = drag_info.grab_y;
4759 y2 = drag_info.current_pointer_y;
4760 y1 = drag_info.grab_y;
4764 Selection::Operation op = Keyboard::selection_type (event->button.state);
4767 begin_reversible_command (_("rubberband selection"));
4769 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4770 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4772 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4776 commit_reversible_command ();
4780 selection->clear_tracks();
4781 selection->clear_regions();
4782 selection->clear_points ();
4783 selection->clear_lines ();
4786 rubberband_rect->hide();
4791 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4793 using namespace Gtkmm2ext;
4795 ArdourPrompter prompter (false);
4797 prompter.set_prompt (_("Name for region:"));
4798 prompter.set_initial_text (clicked_regionview->region()->name());
4799 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4800 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4801 prompter.show_all ();
4802 switch (prompter.run ()) {
4803 case Gtk::RESPONSE_ACCEPT:
4805 prompter.get_result(str);
4807 clicked_regionview->region()->set_name (str);
4815 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4817 drag_info.item = item;
4818 drag_info.motion_callback = &Editor::time_fx_motion;
4819 drag_info.finished_callback = &Editor::end_time_fx;
4823 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4827 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4829 RegionView* rv = clicked_regionview;
4831 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4832 snap_to (drag_info.current_pointer_frame);
4835 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4839 if (drag_info.current_pointer_frame > rv->region()->position()) {
4840 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4843 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4844 drag_info.first_move = false;
4846 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4850 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4852 clicked_regionview->get_time_axis_view().hide_timestretch ();
4854 if (drag_info.first_move) {
4858 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4859 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4861 begin_reversible_command (_("timestretch"));
4863 if (run_timestretch (selection->regions, percentage) == 0) {
4864 session->commit_reversible_command ();
4869 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4871 /* no brushing without a useful snap setting */
4874 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4877 switch (snap_mode) {
4879 return; /* can't work because it allows region to be placed anywhere */
4884 switch (snap_type) {
4887 case SnapToEditCursor:
4894 /* don't brush a copy over the original */
4896 if (pos == rv->region()->position()) {
4900 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4902 if (atv == 0 || !atv->is_audio_track()) {
4906 boost::shared_ptr<Playlist> playlist = atv->playlist();
4907 double speed = atv->get_diskstream()->speed();
4909 XMLNode &before = playlist->get_state();
4910 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4911 XMLNode &after = playlist->get_state();
4912 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4914 // playlist is frozen, so we have to update manually
4916 playlist->Modified(); /* EMIT SIGNAL */
4920 Editor::track_height_step_timeout ()
4923 struct timeval delta;
4925 gettimeofday (&now, 0);
4926 timersub (&now, &last_track_height_step_timestamp, &delta);
4928 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4929 current_stepping_trackview = 0;