2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <pbd/error.h>
30 #include <gtkmm2ext/utils.h>
31 #include <pbd/memento_command.h>
33 #include "ardour_ui.h"
35 #include "time_axis_view.h"
36 #include "audio_time_axis.h"
37 #include "audio_region_view.h"
39 #include "streamview.h"
40 #include "region_gain_line.h"
41 #include "automation_time_axis.h"
44 #include "selection.h"
47 #include "rgb_macros.h"
49 #include <ardour/types.h>
50 #include <ardour/route.h>
51 #include <ardour/audio_track.h>
52 #include <ardour/audio_diskstream.h>
53 #include <ardour/playlist.h>
54 #include <ardour/audioplaylist.h>
55 #include <ardour/audioregion.h>
56 #include <ardour/dB.h>
57 #include <ardour/utils.h>
58 #include <ardour/region_factory.h>
65 using namespace ARDOUR;
69 using namespace Editing;
72 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
86 switch (event->type) {
87 case GDK_BUTTON_RELEASE:
88 case GDK_BUTTON_PRESS:
89 case GDK_2BUTTON_PRESS:
90 case GDK_3BUTTON_PRESS:
91 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
93 case GDK_MOTION_NOTIFY:
94 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
96 case GDK_ENTER_NOTIFY:
97 case GDK_LEAVE_NOTIFY:
98 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
101 case GDK_KEY_RELEASE:
102 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
105 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
109 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
110 position is negative (as can be the case with motion events in particular),
111 the frame location is always positive.
114 return pixel_to_frame (*pcx);
118 Editor::mouse_mode_toggled (MouseMode m)
120 if (ignore_mouse_mode_toggle) {
126 if (mouse_select_button.get_active()) {
132 if (mouse_move_button.get_active()) {
138 if (mouse_gain_button.get_active()) {
144 if (mouse_zoom_button.get_active()) {
150 if (mouse_timefx_button.get_active()) {
156 if (mouse_audition_button.get_active()) {
167 Editor::set_mouse_mode (MouseMode m, bool force)
169 if (drag_info.item) {
173 if (!force && m == mouse_mode) {
181 if (mouse_mode != MouseRange) {
183 /* in all modes except range, hide the range selection,
184 show the object (region) selection.
187 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
188 (*i)->set_should_show_selection (true);
190 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
191 (*i)->hide_selection ();
197 in range mode,show the range selection.
200 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
201 if ((*i)->get_selected()) {
202 (*i)->show_selection (selection->time);
207 /* XXX the hack of unsetting all other buttongs should go
208 away once GTK2 allows us to use regular radio buttons drawn like
209 normal buttons, rather than my silly GroupedButton hack.
212 ignore_mouse_mode_toggle = true;
214 switch (mouse_mode) {
216 mouse_select_button.set_active (true);
217 current_canvas_cursor = selector_cursor;
221 mouse_move_button.set_active (true);
222 current_canvas_cursor = grabber_cursor;
226 mouse_gain_button.set_active (true);
227 current_canvas_cursor = cross_hair_cursor;
231 mouse_zoom_button.set_active (true);
232 current_canvas_cursor = zoom_cursor;
236 mouse_timefx_button.set_active (true);
237 current_canvas_cursor = time_fx_cursor; // just use playhead
241 mouse_audition_button.set_active (true);
242 current_canvas_cursor = speaker_cursor;
246 ignore_mouse_mode_toggle = false;
249 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
254 Editor::step_mouse_mode (bool next)
256 switch (current_mouse_mode()) {
258 if (next) set_mouse_mode (MouseRange);
259 else set_mouse_mode (MouseTimeFX);
263 if (next) set_mouse_mode (MouseZoom);
264 else set_mouse_mode (MouseObject);
268 if (next) set_mouse_mode (MouseGain);
269 else set_mouse_mode (MouseRange);
273 if (next) set_mouse_mode (MouseTimeFX);
274 else set_mouse_mode (MouseZoom);
278 if (next) set_mouse_mode (MouseAudition);
279 else set_mouse_mode (MouseGain);
283 if (next) set_mouse_mode (MouseObject);
284 else set_mouse_mode (MouseTimeFX);
290 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
296 /* in object/audition/timefx mode, any button press sets
297 the selection if the object can be selected. this is a
298 bit of hack, because we want to avoid this if the
299 mouse operation is a region alignment.
301 note: not dbl-click or triple-click
304 if (((mouse_mode != MouseObject) &&
305 (mouse_mode != MouseAudition || item_type != RegionItem) &&
306 (mouse_mode != MouseTimeFX || item_type != RegionItem)) ||
307 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
312 Selection::Operation op = Keyboard::selection_type (event->button.state);
313 bool press = (event->type == GDK_BUTTON_PRESS);
315 begin_reversible_command (_("select on click"));
319 c1 = set_selected_track_from_click (op, true);
320 c2 = set_selected_regionview_from_click (press, op, true);
324 case RegionViewNameHighlight:
326 c1 = set_selected_track_from_click (op, true);
327 c2 = set_selected_regionview_from_click (press, op, true);
331 case GainAutomationControlPointItem:
332 case PanAutomationControlPointItem:
333 case RedirectAutomationControlPointItem:
334 c1 = set_selected_track_from_click (op, true);
335 c2 = set_selected_control_point_from_click (op, false);
340 commit = set_selected_track_from_click (op, true);
343 case AutomationTrackItem:
344 commit = set_selected_track_from_click (op, true);
351 #define SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
352 #ifdef SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
353 /* in range mode, button 1/2/3 press potentially selects a track */
355 if (mouse_mode == MouseRange &&
356 event->type == GDK_BUTTON_PRESS &&
357 event->button.button <= 3) {
362 case AutomationTrackItem:
363 commit = set_selected_track_from_click (op, true);
372 commit_reversible_command ();
377 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
379 nframes_t where = event_frame (event, 0, 0);
381 track_canvas.grab_focus();
383 if (session && session->actively_recording()) {
387 button_selection (item, event, item_type);
389 if (drag_info.item == 0 &&
390 (Keyboard::is_delete_event (&event->button) ||
391 Keyboard::is_context_menu_event (&event->button) ||
392 Keyboard::is_edit_event (&event->button))) {
394 /* handled by button release */
398 switch (event->button.button) {
401 if (event->type == GDK_BUTTON_PRESS) {
403 if (drag_info.item) {
404 drag_info.item->ungrab (event->button.time);
407 /* single mouse clicks on any of these item types operate
408 independent of mouse mode, mostly because they are
409 not on the main track canvas or because we want
415 case PlayheadCursorItem:
416 start_cursor_grab (item, event);
420 if (Keyboard::modifier_state_equals (event->button.state,
421 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
422 hide_marker (item, event);
424 start_marker_grab (item, event);
428 case TempoMarkerItem:
429 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
430 start_tempo_marker_copy_grab (item, event);
432 start_tempo_marker_grab (item, event);
436 case MeterMarkerItem:
437 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
438 start_meter_marker_copy_grab (item, event);
440 start_meter_marker_grab (item, event);
450 case RangeMarkerBarItem:
451 start_range_markerbar_op (item, event, CreateRangeMarker);
455 case TransportMarkerBarItem:
456 start_range_markerbar_op (item, event, CreateTransportMarker);
465 switch (mouse_mode) {
468 case StartSelectionTrimItem:
469 start_selection_op (item, event, SelectionStartTrim);
472 case EndSelectionTrimItem:
473 start_selection_op (item, event, SelectionEndTrim);
477 if (Keyboard::modifier_state_contains
478 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
479 // contains and not equals because I can't use alt as a modifier alone.
480 start_selection_grab (item, event);
481 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
482 /* grab selection for moving */
483 start_selection_op (item, event, SelectionMove);
486 /* this was debated, but decided the more common action was to
487 make a new selection */
488 start_selection_op (item, event, CreateSelection);
493 start_selection_op (item, event, CreateSelection);
499 if (Keyboard::modifier_state_contains (event->button.state,
500 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt))
501 && event->type == GDK_BUTTON_PRESS) {
503 start_rubberband_select (item, event);
505 } else if (event->type == GDK_BUTTON_PRESS) {
508 case FadeInHandleItem:
509 start_fade_in_grab (item, event);
512 case FadeOutHandleItem:
513 start_fade_out_grab (item, event);
517 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
518 start_region_copy_grab (item, event);
519 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
520 start_region_brush_grab (item, event);
522 start_region_grab (item, event);
526 case RegionViewNameHighlight:
527 start_trim (item, event);
532 /* rename happens on edit clicks */
533 start_trim (clicked_regionview->get_name_highlight(), event);
537 case GainAutomationControlPointItem:
538 case PanAutomationControlPointItem:
539 case RedirectAutomationControlPointItem:
540 start_control_point_grab (item, event);
544 case GainAutomationLineItem:
545 case PanAutomationLineItem:
546 case RedirectAutomationLineItem:
547 start_line_grab_from_line (item, event);
552 case AutomationTrackItem:
553 start_rubberband_select (item, event);
556 /* <CMT Additions> */
557 case ImageFrameHandleStartItem:
558 imageframe_start_handle_op(item, event) ;
561 case ImageFrameHandleEndItem:
562 imageframe_end_handle_op(item, event) ;
565 case MarkerViewHandleStartItem:
566 markerview_item_start_handle_op(item, event) ;
569 case MarkerViewHandleEndItem:
570 markerview_item_end_handle_op(item, event) ;
573 /* </CMT Additions> */
575 /* <CMT Additions> */
577 start_markerview_grab(item, event) ;
580 start_imageframe_grab(item, event) ;
582 /* </CMT Additions> */
598 // start_line_grab_from_regionview (item, event);
601 case GainControlPointItem:
602 start_control_point_grab (item, event);
606 start_line_grab_from_line (item, event);
609 case GainAutomationControlPointItem:
610 case PanAutomationControlPointItem:
611 case RedirectAutomationControlPointItem:
612 start_control_point_grab (item, event);
623 case GainAutomationControlPointItem:
624 case PanAutomationControlPointItem:
625 case RedirectAutomationControlPointItem:
626 start_control_point_grab (item, event);
629 case GainAutomationLineItem:
630 case PanAutomationLineItem:
631 case RedirectAutomationLineItem:
632 start_line_grab_from_line (item, event);
636 // XXX need automation mode to identify which
638 // start_line_grab_from_regionview (item, event);
648 if (event->type == GDK_BUTTON_PRESS) {
649 start_mouse_zoom (item, event);
656 if (item_type == RegionItem) {
657 start_time_fx (item, event);
662 /* handled in release */
671 switch (mouse_mode) {
673 if (event->type == GDK_BUTTON_PRESS) {
676 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
677 start_region_copy_grab (item, event);
679 start_region_grab (item, event);
683 case GainAutomationControlPointItem:
684 case PanAutomationControlPointItem:
685 case RedirectAutomationControlPointItem:
686 start_control_point_grab (item, event);
697 case RegionViewNameHighlight:
698 start_trim (item, event);
703 start_trim (clicked_regionview->get_name_highlight(), event);
714 if (event->type == GDK_BUTTON_PRESS) {
715 /* relax till release */
722 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
723 temporal_zoom_session();
725 temporal_zoom_to_frame (true, event_frame(event));
740 switch (mouse_mode) {
742 //temporal_zoom_to_frame (true, where);
743 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
744 temporal_zoom_to_frame (true, where);
747 temporal_zoom_step (true);
752 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
753 scroll_backward (0.6f);
756 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
757 scroll_tracks_up_line ();
759 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
760 if (clicked_trackview) {
761 if (!current_stepping_trackview) {
762 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
763 current_stepping_trackview = clicked_trackview;
765 gettimeofday (&last_track_height_step_timestamp, 0);
766 current_stepping_trackview->step_height (true);
769 else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
770 temporal_zoom_to_frame (true, where);
777 switch (mouse_mode) {
779 // temporal_zoom_to_frame (false, where);
780 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
781 temporal_zoom_to_frame (false, where);
784 temporal_zoom_step (false);
789 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
790 scroll_forward (0.6f);
793 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
794 scroll_tracks_down_line ();
796 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
797 if (clicked_trackview) {
798 if (!current_stepping_trackview) {
799 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
800 current_stepping_trackview = clicked_trackview;
802 gettimeofday (&last_track_height_step_timestamp, 0);
803 current_stepping_trackview->step_height (false);
805 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
806 temporal_zoom_to_frame (false, where);
821 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
823 nframes_t where = event_frame (event, 0, 0);
825 /* no action if we're recording */
827 if (session && session->actively_recording()) {
831 /* first, see if we're finishing a drag ... */
833 if (drag_info.item) {
834 if (end_grab (item, event)) {
835 /* grab dragged, so do nothing else */
840 button_selection (item, event, item_type);
842 /* edit events get handled here */
844 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
850 case TempoMarkerItem:
851 edit_tempo_marker (item);
854 case MeterMarkerItem:
855 edit_meter_marker (item);
859 if (clicked_regionview->name_active()) {
860 return mouse_rename_region (item, event);
870 /* context menu events get handled here */
872 if (Keyboard::is_context_menu_event (&event->button)) {
874 if (drag_info.item == 0) {
876 /* no matter which button pops up the context menu, tell the menu
877 widget to use button 1 to drive menu selection.
882 case FadeInHandleItem:
884 case FadeOutHandleItem:
885 popup_fade_context_menu (1, event->button.time, item, item_type);
889 popup_track_context_menu (1, event->button.time, item_type, false, where);
893 case RegionViewNameHighlight:
895 popup_track_context_menu (1, event->button.time, item_type, false, where);
899 popup_track_context_menu (1, event->button.time, item_type, true, where);
902 case AutomationTrackItem:
903 popup_track_context_menu (1, event->button.time, item_type, false, where);
907 case RangeMarkerBarItem:
908 case TransportMarkerBarItem:
911 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
915 marker_context_menu (&event->button, item);
918 case TempoMarkerItem:
919 tm_marker_context_menu (&event->button, item);
922 case MeterMarkerItem:
923 tm_marker_context_menu (&event->button, item);
926 case CrossfadeViewItem:
927 popup_track_context_menu (1, event->button.time, item_type, false, where);
930 /* <CMT Additions> */
932 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
934 case ImageFrameTimeAxisItem:
935 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
938 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
940 case MarkerTimeAxisItem:
941 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
943 /* <CMT Additions> */
954 /* delete events get handled here */
956 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
959 case TempoMarkerItem:
960 remove_tempo_marker (item);
963 case MeterMarkerItem:
964 remove_meter_marker (item);
968 remove_marker (*item, event);
972 if (mouse_mode == MouseObject) {
973 remove_clicked_region ();
977 case GainControlPointItem:
978 if (mouse_mode == MouseGain) {
979 remove_gain_control_point (item, event);
983 case GainAutomationControlPointItem:
984 case PanAutomationControlPointItem:
985 case RedirectAutomationControlPointItem:
986 remove_control_point (item, event);
995 switch (event->button.button) {
999 /* see comments in button_press_handler */
1000 case EditCursorItem:
1001 case PlayheadCursorItem:
1004 case GainAutomationLineItem:
1005 case PanAutomationLineItem:
1006 case RedirectAutomationLineItem:
1007 case StartSelectionTrimItem:
1008 case EndSelectionTrimItem:
1012 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1013 snap_to (where, 0, true);
1015 mouse_add_new_marker (where);
1019 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1022 mouse_add_new_tempo_event (where);
1026 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1034 switch (mouse_mode) {
1036 switch (item_type) {
1037 case AutomationTrackItem:
1038 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1052 // Gain only makes sense for audio regions
1053 if ( ! dynamic_cast<AudioRegionView*>(clicked_regionview))
1056 switch (item_type) {
1058 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1062 case AutomationTrackItem:
1063 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1064 add_automation_event (item, event, where, event->button.y);
1073 switch (item_type) {
1075 audition_selected_region ();
1092 switch (mouse_mode) {
1095 switch (item_type) {
1097 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1099 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1102 // Button2 click is unused
1115 // x_style_paste (where, 1.0);
1135 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1141 switch (item_type) {
1142 case GainControlPointItem:
1143 if (mouse_mode == MouseGain) {
1144 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1145 cp->set_visible (true);
1149 at_y = cp->get_y ();
1150 cp->item->i2w (at_x, at_y);
1154 fraction = 1.0 - (cp->get_y() / cp->line.height());
1156 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1157 show_verbose_canvas_cursor ();
1159 if (is_drawable()) {
1160 track_canvas.get_window()->set_cursor (*fader_cursor);
1165 case GainAutomationControlPointItem:
1166 case PanAutomationControlPointItem:
1167 case RedirectAutomationControlPointItem:
1168 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1169 cp->set_visible (true);
1173 at_y = cp->get_y ();
1174 cp->item->i2w (at_x, at_y);
1178 fraction = 1.0 - (cp->get_y() / cp->line.height());
1180 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1181 show_verbose_canvas_cursor ();
1183 if (is_drawable()) {
1184 track_canvas.get_window()->set_cursor (*fader_cursor);
1189 if (mouse_mode == MouseGain) {
1190 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1192 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1193 if (is_drawable()) {
1194 track_canvas.get_window()->set_cursor (*fader_cursor);
1199 case GainAutomationLineItem:
1200 case RedirectAutomationLineItem:
1201 case PanAutomationLineItem:
1203 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1205 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1207 if (is_drawable()) {
1208 track_canvas.get_window()->set_cursor (*fader_cursor);
1212 case RegionViewNameHighlight:
1213 if (is_drawable() && mouse_mode == MouseObject) {
1214 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1218 case StartSelectionTrimItem:
1219 case EndSelectionTrimItem:
1220 /* <CMT Additions> */
1221 case ImageFrameHandleStartItem:
1222 case ImageFrameHandleEndItem:
1223 case MarkerViewHandleStartItem:
1224 case MarkerViewHandleEndItem:
1225 /* </CMT Additions> */
1227 if (is_drawable()) {
1228 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1232 case EditCursorItem:
1233 case PlayheadCursorItem:
1234 if (is_drawable()) {
1235 track_canvas.get_window()->set_cursor (*grabber_cursor);
1239 case RegionViewName:
1241 /* when the name is not an active item, the entire name highlight is for trimming */
1243 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1244 if (mouse_mode == MouseObject && is_drawable()) {
1245 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1251 case AutomationTrackItem:
1252 if (is_drawable()) {
1253 Gdk::Cursor *cursor;
1254 switch (mouse_mode) {
1256 cursor = selector_cursor;
1259 cursor = zoom_cursor;
1262 cursor = cross_hair_cursor;
1266 track_canvas.get_window()->set_cursor (*cursor);
1268 AutomationTimeAxisView* atv;
1269 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1270 clear_entered_track = false;
1271 set_entered_track (atv);
1277 case RangeMarkerBarItem:
1278 case TransportMarkerBarItem:
1281 if (is_drawable()) {
1282 time_canvas.get_window()->set_cursor (*timebar_cursor);
1287 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1290 marker->set_color_rgba (color_map[cEnteredMarker]);
1292 case MeterMarkerItem:
1293 case TempoMarkerItem:
1294 if (is_drawable()) {
1295 time_canvas.get_window()->set_cursor (*timebar_cursor);
1298 case FadeInHandleItem:
1299 case FadeOutHandleItem:
1300 if (mouse_mode == MouseObject) {
1301 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1303 rect->property_fill_color_rgba() = 0;
1304 rect->property_outline_pixels() = 1;
1313 /* second pass to handle entered track status in a comprehensible way.
1316 switch (item_type) {
1318 case GainAutomationLineItem:
1319 case RedirectAutomationLineItem:
1320 case PanAutomationLineItem:
1321 case GainControlPointItem:
1322 case GainAutomationControlPointItem:
1323 case PanAutomationControlPointItem:
1324 case RedirectAutomationControlPointItem:
1325 /* these do not affect the current entered track state */
1326 clear_entered_track = false;
1329 case AutomationTrackItem:
1330 /* handled above already */
1334 set_entered_track (0);
1342 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1351 switch (item_type) {
1352 case GainControlPointItem:
1353 case GainAutomationControlPointItem:
1354 case PanAutomationControlPointItem:
1355 case RedirectAutomationControlPointItem:
1356 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1357 if (cp->line.npoints() > 1) {
1358 if (!cp->selected) {
1359 cp->set_visible (false);
1363 if (is_drawable()) {
1364 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1367 hide_verbose_canvas_cursor ();
1370 case RegionViewNameHighlight:
1371 case StartSelectionTrimItem:
1372 case EndSelectionTrimItem:
1373 case EditCursorItem:
1374 case PlayheadCursorItem:
1375 /* <CMT Additions> */
1376 case ImageFrameHandleStartItem:
1377 case ImageFrameHandleEndItem:
1378 case MarkerViewHandleStartItem:
1379 case MarkerViewHandleEndItem:
1380 /* </CMT Additions> */
1381 if (is_drawable()) {
1382 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1387 case GainAutomationLineItem:
1388 case RedirectAutomationLineItem:
1389 case PanAutomationLineItem:
1390 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1392 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1394 line->property_fill_color_rgba() = al->get_line_color();
1396 if (is_drawable()) {
1397 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1401 case RegionViewName:
1402 /* see enter_handler() for notes */
1403 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1404 if (is_drawable() && mouse_mode == MouseObject) {
1405 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1410 case RangeMarkerBarItem:
1411 case TransportMarkerBarItem:
1415 if (is_drawable()) {
1416 time_canvas.get_window()->set_cursor (*timebar_cursor);
1421 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1424 loc = find_location_from_marker (marker, is_start);
1425 if (loc) location_flags_changed (loc, this);
1427 case MeterMarkerItem:
1428 case TempoMarkerItem:
1430 if (is_drawable()) {
1431 time_canvas.get_window()->set_cursor (*timebar_cursor);
1436 case FadeInHandleItem:
1437 case FadeOutHandleItem:
1438 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1440 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1442 rect->property_fill_color_rgba() = rv->get_fill_color();
1443 rect->property_outline_pixels() = 0;
1448 case AutomationTrackItem:
1449 if (is_drawable()) {
1450 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1451 clear_entered_track = true;
1452 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1464 Editor::left_automation_track ()
1466 if (clear_entered_track) {
1467 set_entered_track (0);
1468 clear_entered_track = false;
1474 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1478 /* We call this so that MOTION_NOTIFY events continue to be
1479 delivered to the canvas. We need to do this because we set
1480 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1481 the density of the events, at the expense of a round-trip
1482 to the server. Given that this will mostly occur on cases
1483 where DISPLAY = :0.0, and given the cost of what the motion
1484 event might do, its a good tradeoff.
1487 track_canvas.get_pointer (x, y);
1489 if (current_stepping_trackview) {
1490 /* don't keep the persistent stepped trackview if the mouse moves */
1491 current_stepping_trackview = 0;
1492 step_timeout.disconnect ();
1495 if (session && session->actively_recording()) {
1496 /* Sorry. no dragging stuff around while we record */
1500 drag_info.item_type = item_type;
1501 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1502 &drag_info.current_pointer_y);
1504 if (!from_autoscroll && drag_info.item) {
1505 /* item != 0 is the best test i can think of for dragging.
1507 if (!drag_info.move_threshold_passed) {
1509 drag_info.move_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1511 // and change the initial grab loc/frame if this drag info wants us to
1513 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1514 drag_info.grab_frame = drag_info.current_pointer_frame;
1515 drag_info.grab_x = drag_info.current_pointer_x;
1516 drag_info.grab_y = drag_info.current_pointer_y;
1517 drag_info.last_pointer_frame = drag_info.grab_frame;
1518 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1523 switch (item_type) {
1524 case PlayheadCursorItem:
1525 case EditCursorItem:
1527 case GainControlPointItem:
1528 case RedirectAutomationControlPointItem:
1529 case GainAutomationControlPointItem:
1530 case PanAutomationControlPointItem:
1531 case TempoMarkerItem:
1532 case MeterMarkerItem:
1533 case RegionViewNameHighlight:
1534 case StartSelectionTrimItem:
1535 case EndSelectionTrimItem:
1538 case RedirectAutomationLineItem:
1539 case GainAutomationLineItem:
1540 case PanAutomationLineItem:
1541 case FadeInHandleItem:
1542 case FadeOutHandleItem:
1543 /* <CMT Additions> */
1544 case ImageFrameHandleStartItem:
1545 case ImageFrameHandleEndItem:
1546 case MarkerViewHandleStartItem:
1547 case MarkerViewHandleEndItem:
1548 /* </CMT Additions> */
1549 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1550 (event->motion.state & Gdk::BUTTON2_MASK))) {
1551 if (!from_autoscroll) {
1552 maybe_autoscroll (event);
1554 (this->*(drag_info.motion_callback)) (item, event);
1563 switch (mouse_mode) {
1568 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1569 (event->motion.state & GDK_BUTTON2_MASK))) {
1570 if (!from_autoscroll) {
1571 maybe_autoscroll (event);
1573 (this->*(drag_info.motion_callback)) (item, event);
1584 track_canvas_motion (event);
1585 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1593 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1595 if (drag_info.item == 0) {
1596 fatal << _("programming error: start_grab called without drag item") << endmsg;
1602 cursor = grabber_cursor;
1605 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1607 if (event->button.button == 2) {
1608 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1609 drag_info.y_constrained = true;
1610 drag_info.x_constrained = false;
1612 drag_info.y_constrained = false;
1613 drag_info.x_constrained = true;
1616 drag_info.x_constrained = false;
1617 drag_info.y_constrained = false;
1620 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1621 drag_info.last_pointer_frame = drag_info.grab_frame;
1622 drag_info.current_pointer_frame = drag_info.grab_frame;
1623 drag_info.current_pointer_x = drag_info.grab_x;
1624 drag_info.current_pointer_y = drag_info.grab_y;
1625 drag_info.cumulative_x_drag = 0;
1626 drag_info.cumulative_y_drag = 0;
1627 drag_info.first_move = true;
1628 drag_info.move_threshold_passed = false;
1629 drag_info.want_move_threshold = false;
1630 drag_info.pointer_frame_offset = 0;
1631 drag_info.brushing = false;
1632 drag_info.copied_location = 0;
1634 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1636 event->button.time);
1638 if (session && session->transport_rolling()) {
1639 drag_info.was_rolling = true;
1641 drag_info.was_rolling = false;
1644 switch (snap_type) {
1645 case SnapToRegionStart:
1646 case SnapToRegionEnd:
1647 case SnapToRegionSync:
1648 case SnapToRegionBoundary:
1649 build_region_boundary_cache ();
1657 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1659 drag_info.item->ungrab (0);
1660 drag_info.item = new_item;
1663 cursor = grabber_cursor;
1666 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1670 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1672 bool did_drag = false;
1674 stop_canvas_autoscroll ();
1676 if (drag_info.item == 0) {
1680 drag_info.item->ungrab (event->button.time);
1682 if (drag_info.finished_callback) {
1683 (this->*(drag_info.finished_callback)) (item, event);
1686 did_drag = !drag_info.first_move;
1688 hide_verbose_canvas_cursor();
1691 drag_info.copy = false;
1692 drag_info.motion_callback = 0;
1693 drag_info.finished_callback = 0;
1694 drag_info.last_trackview = 0;
1695 drag_info.last_frame_position = 0;
1696 drag_info.grab_frame = 0;
1697 drag_info.last_pointer_frame = 0;
1698 drag_info.current_pointer_frame = 0;
1699 drag_info.brushing = false;
1701 if (drag_info.copied_location) {
1702 delete drag_info.copied_location;
1703 drag_info.copied_location = 0;
1710 Editor::set_edit_cursor (GdkEvent* event)
1712 nframes_t pointer_frame = event_frame (event);
1714 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1715 if (snap_type != SnapToEditCursor) {
1716 snap_to (pointer_frame);
1720 edit_cursor->set_position (pointer_frame);
1721 edit_cursor_clock.set (pointer_frame);
1725 Editor::set_playhead_cursor (GdkEvent* event)
1727 nframes_t pointer_frame = event_frame (event);
1729 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1730 snap_to (pointer_frame);
1734 session->request_locate (pointer_frame, session->transport_rolling());
1739 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1741 drag_info.item = item;
1742 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1743 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1747 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1748 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1752 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1754 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1758 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1760 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1762 nframes_t fade_length;
1764 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1765 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1771 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1775 if (pos < (arv->region()->position() + 64)) {
1776 fade_length = 64; // this should be a minimum defined somewhere
1777 } else if (pos > arv->region()->last_frame()) {
1778 fade_length = arv->region()->length();
1780 fade_length = pos - arv->region()->position();
1783 arv->reset_fade_in_shape_width (fade_length);
1785 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1787 drag_info.first_move = false;
1791 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1793 if (drag_info.first_move) return;
1795 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1797 nframes_t fade_length;
1799 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1800 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1806 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1810 if (pos < (arv->region()->position() + 64)) {
1811 fade_length = 64; // this should be a minimum defined somewhere
1813 else if (pos > arv->region()->last_frame()) {
1814 fade_length = arv->region()->length();
1817 fade_length = pos - arv->region()->position();
1820 begin_reversible_command (_("change fade in length"));
1821 AutomationList& alist = arv->audio_region()->fade_in();
1822 XMLNode &before = alist.get_state();
1824 arv->audio_region()->set_fade_in_length (fade_length);
1826 XMLNode &after = alist.get_state();
1827 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1828 commit_reversible_command ();
1829 fade_in_drag_motion_callback (item, event);
1833 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1835 drag_info.item = item;
1836 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1837 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1841 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1842 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1846 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1848 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1852 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1854 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1856 nframes_t fade_length;
1858 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1859 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1865 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1869 if (pos > (arv->region()->last_frame() - 64)) {
1870 fade_length = 64; // this should really be a minimum fade defined somewhere
1872 else if (pos < arv->region()->position()) {
1873 fade_length = arv->region()->length();
1876 fade_length = arv->region()->last_frame() - pos;
1879 arv->reset_fade_out_shape_width (fade_length);
1881 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1883 drag_info.first_move = false;
1887 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1889 if (drag_info.first_move) return;
1891 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1893 nframes_t fade_length;
1895 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1896 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1902 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1906 if (pos > (arv->region()->last_frame() - 64)) {
1907 fade_length = 64; // this should really be a minimum fade defined somewhere
1909 else if (pos < arv->region()->position()) {
1910 fade_length = arv->region()->length();
1913 fade_length = arv->region()->last_frame() - pos;
1916 begin_reversible_command (_("change fade out length"));
1917 AutomationList& alist = arv->audio_region()->fade_out();
1918 XMLNode &before = alist.get_state();
1920 arv->audio_region()->set_fade_out_length (fade_length);
1922 XMLNode &after = alist.get_state();
1923 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1924 commit_reversible_command ();
1926 fade_out_drag_motion_callback (item, event);
1930 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1932 drag_info.item = item;
1933 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1934 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1938 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1939 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1943 Cursor* cursor = (Cursor *) drag_info.data;
1945 if (session && cursor == playhead_cursor) {
1946 if (drag_info.was_rolling) {
1947 session->request_stop ();
1951 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1953 show_verbose_time_cursor (cursor->current_frame, 10);
1957 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1959 Cursor* cursor = (Cursor *) drag_info.data;
1960 nframes_t adjusted_frame;
1962 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1963 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1969 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1970 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1971 snap_to (adjusted_frame);
1975 if (adjusted_frame == drag_info.last_pointer_frame) return;
1977 cursor->set_position (adjusted_frame);
1979 if (cursor == edit_cursor) {
1980 edit_cursor_clock.set (cursor->current_frame);
1983 show_verbose_time_cursor (cursor->current_frame, 10);
1985 drag_info.last_pointer_frame = adjusted_frame;
1986 drag_info.first_move = false;
1990 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1992 if (drag_info.first_move) return;
1994 cursor_drag_motion_callback (item, event);
1996 if (item == &playhead_cursor->canvas_item) {
1998 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2000 } else if (item == &edit_cursor->canvas_item) {
2001 edit_cursor->set_position (edit_cursor->current_frame);
2002 edit_cursor_clock.set (edit_cursor->current_frame);
2007 Editor::update_marker_drag_item (Location *location)
2009 double x1 = frame_to_pixel (location->start());
2010 double x2 = frame_to_pixel (location->end());
2012 if (location->is_mark()) {
2013 marker_drag_line_points.front().set_x(x1);
2014 marker_drag_line_points.back().set_x(x1);
2015 marker_drag_line->property_points() = marker_drag_line_points;
2018 range_marker_drag_rect->property_x1() = x1;
2019 range_marker_drag_rect->property_x2() = x2;
2024 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2028 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2029 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2035 Location *location = find_location_from_marker (marker, is_start);
2037 drag_info.item = item;
2038 drag_info.data = marker;
2039 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2040 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2044 drag_info.copied_location = new Location (*location);
2045 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2047 update_marker_drag_item (location);
2049 if (location->is_mark()) {
2050 marker_drag_line->show();
2051 marker_drag_line->raise_to_top();
2054 range_marker_drag_rect->show();
2055 range_marker_drag_rect->raise_to_top();
2058 if (is_start) show_verbose_time_cursor (location->start(), 10);
2059 else show_verbose_time_cursor (location->end(), 10);
2063 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2066 Marker* marker = (Marker *) drag_info.data;
2067 Location *real_location;
2068 Location *copy_location;
2070 bool move_both = false;
2074 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2075 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2081 nframes_t next = newframe;
2083 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2084 snap_to (newframe, 0, true);
2087 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2091 /* call this to find out if its the start or end */
2093 real_location = find_location_from_marker (marker, is_start);
2095 /* use the copy that we're "dragging" around */
2097 copy_location = drag_info.copied_location;
2099 f_delta = copy_location->end() - copy_location->start();
2101 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2105 if (copy_location->is_mark()) {
2108 copy_location->set_start (newframe);
2112 if (is_start) { // start-of-range marker
2115 copy_location->set_start (newframe);
2116 copy_location->set_end (newframe + f_delta);
2117 } else if (newframe < copy_location->end()) {
2118 copy_location->set_start (newframe);
2120 snap_to (next, 1, true);
2121 copy_location->set_end (next);
2122 copy_location->set_start (newframe);
2125 } else { // end marker
2128 copy_location->set_end (newframe);
2129 copy_location->set_start (newframe - f_delta);
2130 } else if (newframe > copy_location->start()) {
2131 copy_location->set_end (newframe);
2133 } else if (newframe > 0) {
2134 snap_to (next, -1, true);
2135 copy_location->set_start (next);
2136 copy_location->set_end (newframe);
2141 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2142 drag_info.first_move = false;
2144 update_marker_drag_item (copy_location);
2146 LocationMarkers* lm = find_location_markers (real_location);
2147 lm->set_position (copy_location->start(), copy_location->end());
2149 show_verbose_time_cursor (newframe, 10);
2153 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2155 if (drag_info.first_move) {
2156 marker_drag_motion_callback (item, event);
2160 Marker* marker = (Marker *) drag_info.data;
2164 begin_reversible_command ( _("move marker") );
2165 XMLNode &before = session->locations()->get_state();
2167 Location * location = find_location_from_marker (marker, is_start);
2170 if (location->is_mark()) {
2171 location->set_start (drag_info.copied_location->start());
2173 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2177 XMLNode &after = session->locations()->get_state();
2178 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2179 commit_reversible_command ();
2181 marker_drag_line->hide();
2182 range_marker_drag_rect->hide();
2186 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2189 MeterMarker* meter_marker;
2191 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2192 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2196 meter_marker = dynamic_cast<MeterMarker*> (marker);
2198 MetricSection& section (meter_marker->meter());
2200 if (!section.movable()) {
2204 drag_info.item = item;
2205 drag_info.data = marker;
2206 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2207 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2211 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2213 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2217 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2220 MeterMarker* meter_marker;
2222 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2223 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2227 meter_marker = dynamic_cast<MeterMarker*> (marker);
2229 // create a dummy marker for visual representation of moving the copy.
2230 // The actual copying is not done before we reach the finish callback.
2232 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2233 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2234 *new MeterSection(meter_marker->meter()));
2236 drag_info.item = &new_marker->the_item();
2237 drag_info.copy = true;
2238 drag_info.data = new_marker;
2239 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2240 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2244 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2246 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2250 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2252 MeterMarker* marker = (MeterMarker *) drag_info.data;
2253 nframes_t adjusted_frame;
2255 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2256 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2262 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2263 snap_to (adjusted_frame);
2266 if (adjusted_frame == drag_info.last_pointer_frame) return;
2268 marker->set_position (adjusted_frame);
2271 drag_info.last_pointer_frame = adjusted_frame;
2272 drag_info.first_move = false;
2274 show_verbose_time_cursor (adjusted_frame, 10);
2278 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2280 if (drag_info.first_move) return;
2282 meter_marker_drag_motion_callback (drag_info.item, event);
2284 MeterMarker* marker = (MeterMarker *) drag_info.data;
2287 TempoMap& map (session->tempo_map());
2288 map.bbt_time (drag_info.last_pointer_frame, when);
2290 if (drag_info.copy == true) {
2291 begin_reversible_command (_("copy meter mark"));
2292 XMLNode &before = map.get_state();
2293 map.add_meter (marker->meter(), when);
2294 XMLNode &after = map.get_state();
2295 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2296 commit_reversible_command ();
2298 // delete the dummy marker we used for visual representation of copying.
2299 // a new visual marker will show up automatically.
2302 begin_reversible_command (_("move meter mark"));
2303 XMLNode &before = map.get_state();
2304 map.move_meter (marker->meter(), when);
2305 XMLNode &after = map.get_state();
2306 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2307 commit_reversible_command ();
2312 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2315 TempoMarker* tempo_marker;
2317 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2318 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2322 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2323 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2327 MetricSection& section (tempo_marker->tempo());
2329 if (!section.movable()) {
2333 drag_info.item = item;
2334 drag_info.data = marker;
2335 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2336 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2340 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2341 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2345 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2348 TempoMarker* tempo_marker;
2350 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2351 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2355 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2356 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2360 // create a dummy marker for visual representation of moving the copy.
2361 // The actual copying is not done before we reach the finish callback.
2363 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2364 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2365 *new TempoSection(tempo_marker->tempo()));
2367 drag_info.item = &new_marker->the_item();
2368 drag_info.copy = true;
2369 drag_info.data = new_marker;
2370 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2371 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2375 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2377 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2381 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2383 TempoMarker* marker = (TempoMarker *) drag_info.data;
2384 nframes_t adjusted_frame;
2386 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2387 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2393 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2394 snap_to (adjusted_frame);
2397 if (adjusted_frame == drag_info.last_pointer_frame) return;
2399 /* OK, we've moved far enough to make it worth actually move the thing. */
2401 marker->set_position (adjusted_frame);
2403 show_verbose_time_cursor (adjusted_frame, 10);
2405 drag_info.last_pointer_frame = adjusted_frame;
2406 drag_info.first_move = false;
2410 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2412 if (drag_info.first_move) return;
2414 tempo_marker_drag_motion_callback (drag_info.item, event);
2416 TempoMarker* marker = (TempoMarker *) drag_info.data;
2419 TempoMap& map (session->tempo_map());
2420 map.bbt_time (drag_info.last_pointer_frame, when);
2422 if (drag_info.copy == true) {
2423 begin_reversible_command (_("copy tempo mark"));
2424 XMLNode &before = map.get_state();
2425 map.add_tempo (marker->tempo(), when);
2426 XMLNode &after = map.get_state();
2427 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2428 commit_reversible_command ();
2430 // delete the dummy marker we used for visual representation of copying.
2431 // a new visual marker will show up automatically.
2434 begin_reversible_command (_("move tempo mark"));
2435 XMLNode &before = map.get_state();
2436 map.move_tempo (marker->tempo(), when);
2437 XMLNode &after = map.get_state();
2438 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2439 commit_reversible_command ();
2444 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2446 ControlPoint* control_point;
2448 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2449 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2453 // We shouldn't remove the first or last gain point
2454 if (control_point->line.is_last_point(*control_point) ||
2455 control_point->line.is_first_point(*control_point)) {
2459 control_point->line.remove_point (*control_point);
2463 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2465 ControlPoint* control_point;
2467 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2468 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2472 control_point->line.remove_point (*control_point);
2476 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2478 ControlPoint* control_point;
2480 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2481 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2485 drag_info.item = item;
2486 drag_info.data = control_point;
2487 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2488 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2490 start_grab (event, fader_cursor);
2492 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2494 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2495 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2496 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2498 show_verbose_canvas_cursor ();
2502 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2504 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2506 double cx = drag_info.current_pointer_x;
2507 double cy = drag_info.current_pointer_y;
2509 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2510 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2512 if (drag_info.x_constrained) {
2513 cx = drag_info.grab_x;
2515 if (drag_info.y_constrained) {
2516 cy = drag_info.grab_y;
2519 cp->line.parent_group().w2i (cx, cy);
2523 cy = min ((double) cp->line.height(), cy);
2525 //translate cx to frames
2526 nframes_t cx_frames = unit_to_frame (cx);
2528 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2529 snap_to (cx_frames);
2532 float fraction = 1.0 - (cy / cp->line.height());
2536 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2542 cp->line.point_drag (*cp, cx_frames , fraction, push);
2544 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2546 drag_info.first_move = false;
2550 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2552 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2554 if (drag_info.first_move) {
2558 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2559 reset_point_selection ();
2563 control_point_drag_motion_callback (item, event);
2565 cp->line.end_drag (cp);
2569 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2571 switch (mouse_mode) {
2573 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2574 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2582 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2586 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2587 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2591 start_line_grab (al, event);
2595 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2599 nframes_t frame_within_region;
2601 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2605 cx = event->button.x;
2606 cy = event->button.y;
2607 line->parent_group().w2i (cx, cy);
2608 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2610 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2611 current_line_drag_info.after)) {
2612 /* no adjacent points */
2616 drag_info.item = &line->grab_item();
2617 drag_info.data = line;
2618 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2619 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2621 start_grab (event, fader_cursor);
2623 double fraction = 1.0 - (cy / line->height());
2625 line->start_drag (0, drag_info.grab_frame, fraction);
2627 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2628 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2629 show_verbose_canvas_cursor ();
2633 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2635 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2636 double cx = drag_info.current_pointer_x;
2637 double cy = drag_info.current_pointer_y;
2639 line->parent_group().w2i (cx, cy);
2642 fraction = 1.0 - (cy / line->height());
2646 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2652 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2654 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2658 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2660 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2661 line_drag_motion_callback (item, event);
2666 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2668 if (selection->regions.empty() || clicked_regionview == 0) {
2672 drag_info.copy = false;
2673 drag_info.item = item;
2674 drag_info.data = clicked_regionview;
2675 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2676 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2681 TimeAxisView* tvp = clicked_trackview;
2682 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2684 if (tv && tv->is_audio_track()) {
2685 speed = tv->get_diskstream()->speed();
2688 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2689 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2690 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2691 // we want a move threshold
2692 drag_info.want_move_threshold = true;
2694 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2696 begin_reversible_command (_("move region(s)"));
2700 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2702 if (selection->regions.empty() || clicked_regionview == 0) {
2706 drag_info.copy = true;
2707 drag_info.item = item;
2708 drag_info.data = clicked_regionview;
2712 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2713 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2716 if (atv && atv->is_audio_track()) {
2717 speed = atv->get_diskstream()->speed();
2720 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2721 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2722 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2723 // we want a move threshold
2724 drag_info.want_move_threshold = true;
2725 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2726 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2727 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2731 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2733 if (selection->regions.empty() || clicked_regionview == 0) {
2737 drag_info.copy = false;
2738 drag_info.item = item;
2739 drag_info.data = clicked_regionview;
2740 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2741 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2746 TimeAxisView* tvp = clicked_trackview;
2747 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2749 if (tv && tv->is_audio_track()) {
2750 speed = tv->get_diskstream()->speed();
2753 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2754 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2755 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2756 // we want a move threshold
2757 drag_info.want_move_threshold = true;
2758 drag_info.brushing = true;
2760 begin_reversible_command (_("Drag region brush"));
2764 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2768 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2769 nframes_t pending_region_position = 0;
2770 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2771 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2772 bool clamp_y_axis = false;
2773 vector<int32_t> height_list(512) ;
2774 vector<int32_t>::iterator j;
2776 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2778 drag_info.want_move_threshold = false; // don't copy again
2780 /* this is committed in the grab finished callback. */
2782 begin_reversible_command (_("Drag region copy"));
2784 /* duplicate the region(s) */
2786 vector<RegionView*> new_regionviews;
2788 set<Playlist*> affected_playlists;
2789 pair<set<Playlist*>::iterator,bool> insert_result;
2791 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2796 Playlist* to_playlist = rv->region()->playlist();
2797 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2799 insert_result = affected_playlists.insert (to_playlist);
2800 if (insert_result.second) {
2801 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
2804 latest_regionview = 0;
2806 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2808 /* create a new region with the same name. */
2810 // FIXME: ew. need a (virtual) Region::duplicate() or something?
2812 boost::shared_ptr<Region> newregion;
2813 boost::shared_ptr<Region> ar;
2815 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
2816 newregion = RegionFactory::create (ar);
2818 assert(newregion != 0);
2820 /* if the original region was locked, we don't care */
2822 newregion->set_locked (false);
2824 to_playlist->add_region (newregion, (nframes_t) (rv->region()->position() * atv->get_diskstream()->speed()));
2828 if (latest_regionview) {
2829 new_regionviews.push_back (latest_regionview);
2833 if (new_regionviews.empty()) {
2837 /* reset selection to new regionviews */
2839 selection->set (new_regionviews);
2841 /* reset drag_info data to reflect the fact that we are dragging the copies */
2843 drag_info.data = new_regionviews.front();
2844 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2847 /* Which trackview is this ? */
2849 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2850 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2852 /* The region motion is only processed if the pointer is over
2856 if (!tv || !tv->is_audio_track()) {
2857 /* To make sure we hide the verbose canvas cursor when the mouse is
2858 not held over and audiotrack.
2860 hide_verbose_canvas_cursor ();
2864 original_pointer_order = drag_info.last_trackview->order;
2866 /************************************************************
2868 ************************************************************/
2870 if (drag_info.brushing) {
2871 clamp_y_axis = true;
2876 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2878 int32_t children = 0, numtracks = 0;
2879 // XXX hard coding track limit, oh my, so very very bad
2880 bitset <1024> tracks (0x00);
2881 /* get a bitmask representing the visible tracks */
2883 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2884 TimeAxisView *tracklist_timeview;
2885 tracklist_timeview = (*i);
2886 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2887 list<TimeAxisView*> children_list;
2889 /* zeroes are audio tracks. ones are other types. */
2891 if (!atv2->hidden()) {
2893 if (visible_y_high < atv2->order) {
2894 visible_y_high = atv2->order;
2896 if (visible_y_low > atv2->order) {
2897 visible_y_low = atv2->order;
2900 if (!atv2->is_audio_track()) {
2901 tracks = tracks |= (0x01 << atv2->order);
2904 height_list[atv2->order] = (*i)->height;
2906 if ((children_list = atv2->get_child_list()).size() > 0) {
2907 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2908 tracks = tracks |= (0x01 << (atv2->order + children));
2909 height_list[atv2->order + children] = (*j)->height;
2917 /* find the actual span according to the canvas */
2919 canvas_pointer_y_span = pointer_y_span;
2920 if (drag_info.last_trackview->order >= tv->order) {
2922 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2923 if (height_list[y] == 0 ) {
2924 canvas_pointer_y_span--;
2929 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2930 if ( height_list[y] == 0 ) {
2931 canvas_pointer_y_span++;
2936 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2937 RegionView* rv2 = (*i);
2938 double ix1, ix2, iy1, iy2;
2941 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2942 rv2->get_canvas_group()->i2w (ix1, iy1);
2943 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2944 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2946 if (atv2->order != original_pointer_order) {
2947 /* this isn't the pointer track */
2949 if (canvas_pointer_y_span > 0) {
2951 /* moving up the canvas */
2952 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2954 int32_t visible_tracks = 0;
2955 while (visible_tracks < canvas_pointer_y_span ) {
2958 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2959 /* we're passing through a hidden track */
2964 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2965 clamp_y_axis = true;
2969 clamp_y_axis = true;
2972 } else if (canvas_pointer_y_span < 0) {
2974 /*moving down the canvas*/
2976 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2979 int32_t visible_tracks = 0;
2981 while (visible_tracks > canvas_pointer_y_span ) {
2984 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2988 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2989 clamp_y_axis = true;
2994 clamp_y_axis = true;
3000 /* this is the pointer's track */
3001 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3002 clamp_y_axis = true;
3003 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3004 clamp_y_axis = true;
3012 } else if (drag_info.last_trackview == tv) {
3013 clamp_y_axis = true;
3017 if (!clamp_y_axis) {
3018 drag_info.last_trackview = tv;
3021 /************************************************************
3023 ************************************************************/
3025 /* compute the amount of pointer motion in frames, and where
3026 the region would be if we moved it by that much.
3029 if (drag_info.move_threshold_passed) {
3031 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3033 nframes_t sync_frame;
3034 nframes_t sync_offset;
3037 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3039 sync_offset = rv->region()->sync_offset (sync_dir);
3040 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3042 /* we snap if the snap modifier is not enabled.
3045 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3046 snap_to (sync_frame);
3049 if (sync_frame - sync_offset <= sync_frame) {
3050 pending_region_position = sync_frame - (sync_dir*sync_offset);
3052 pending_region_position = 0;
3056 pending_region_position = 0;
3059 if (pending_region_position > max_frames - rv->region()->length()) {
3060 pending_region_position = drag_info.last_frame_position;
3063 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3065 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3067 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3068 to make it appear at the new location.
3071 if (pending_region_position > drag_info.last_frame_position) {
3072 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3074 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3077 drag_info.last_frame_position = pending_region_position;
3084 /* threshold not passed */
3089 /*************************************************************
3091 ************************************************************/
3093 if (x_delta == 0 && (pointer_y_span == 0)) {
3094 /* haven't reached next snap point, and we're not switching
3095 trackviews. nothing to do.
3101 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3103 RegionView* rv2 = (*i);
3105 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3107 double ix1, ix2, iy1, iy2;
3108 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3109 rv2->get_canvas_group()->i2w (ix1, iy1);
3118 /*************************************************************
3120 ************************************************************/
3122 pair<set<Playlist*>::iterator,bool> insert_result;
3123 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3125 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3127 RegionView* rv = (*i);
3128 double ix1, ix2, iy1, iy2;
3129 int32_t temp_pointer_y_span = pointer_y_span;
3131 /* get item BBox, which will be relative to parent. so we have
3132 to query on a child, then convert to world coordinates using
3136 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3137 rv->get_canvas_group()->i2w (ix1, iy1);
3138 rv->region()->set_opaque(false);
3139 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3140 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3141 AudioTimeAxisView* temp_atv;
3143 if ((pointer_y_span != 0) && !clamp_y_axis) {
3146 for (j = height_list.begin(); j!= height_list.end(); j++) {
3147 if (x == canvas_atv->order) {
3148 /* we found the track the region is on */
3149 if (x != original_pointer_order) {
3150 /*this isn't from the same track we're dragging from */
3151 temp_pointer_y_span = canvas_pointer_y_span;
3153 while (temp_pointer_y_span > 0) {
3154 /* we're moving up canvas-wise,
3155 so we need to find the next track height
3157 if (j != height_list.begin()) {
3160 if (x != original_pointer_order) {
3161 /* we're not from the dragged track, so ignore hidden tracks. */
3163 temp_pointer_y_span++;
3167 temp_pointer_y_span--;
3169 while (temp_pointer_y_span < 0) {
3171 if (x != original_pointer_order) {
3173 temp_pointer_y_span--;
3177 if (j != height_list.end()) {
3180 temp_pointer_y_span++;
3182 /* find out where we'll be when we move and set height accordingly */
3184 tvp2 = trackview_by_y_position (iy1 + y_delta);
3185 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3186 rv->set_height (temp_atv->height);
3188 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3189 personally, i think this can confuse things, but never mind.
3192 //const GdkColor& col (temp_atv->view->get_region_color());
3193 //rv->set_color (const_cast<GdkColor&>(col));
3200 /* prevent the regionview from being moved to before
3201 the zero position on the canvas.
3206 if (-x_delta > ix1) {
3209 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3210 x_delta = max_frames - rv->region()->last_frame();
3213 if (drag_info.first_move) {
3215 /* hide any dependent views */
3217 // rv->get_time_axis_view().hide_dependent_views (*rv);
3219 /* this is subtle. raising the regionview itself won't help,
3220 because raise_to_top() just puts the item on the top of
3221 its parent's stack. so, we need to put the trackview canvas_display group
3222 on the top, since its parent is the whole canvas.
3225 rv->get_canvas_group()->raise_to_top();
3226 rv->get_time_axis_view().canvas_display->raise_to_top();
3227 cursor_group->raise_to_top();
3229 /* freeze the playlists from notifying till
3233 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3234 if (atv && atv->is_audio_track()) {
3235 AudioPlaylist* pl = dynamic_cast<AudioPlaylist*>(atv->get_diskstream()->playlist());
3237 /* only freeze and capture state once */
3239 insert_result = motion_frozen_playlists.insert (pl);
3240 if (insert_result.second) {
3242 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
3248 if (drag_info.brushing) {
3249 mouse_brush_insert_region (rv, pending_region_position);
3251 rv->move (x_delta, y_delta);
3255 if (drag_info.first_move) {
3256 cursor_group->raise_to_top();
3259 drag_info.first_move = false;
3261 if (x_delta != 0 && !drag_info.brushing) {
3262 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3268 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3271 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3272 pair<set<Playlist*>::iterator,bool> insert_result;
3273 bool nocommit = true;
3275 RouteTimeAxisView* atv;
3276 bool regionview_y_movement;
3277 bool regionview_x_movement;
3279 /* first_move is set to false if the regionview has been moved in the
3283 if (drag_info.first_move) {
3290 /* The regionview has been moved at some stage during the grab so we need
3291 to account for any mouse movement between this event and the last one.
3294 region_drag_motion_callback (item, event);
3296 if (drag_info.brushing) {
3297 /* all changes were made during motion event handlers */
3301 /* adjust for track speed */
3304 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3305 if (atv && atv->get_diskstream()) {
3306 speed = atv->get_diskstream()->speed();
3309 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3310 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3312 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3313 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3315 if (regionview_y_movement) {
3317 /* motion between tracks */
3319 list<RegionView*> new_selection;
3321 /* moved to a different audio track. */
3323 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3325 RegionView* rv2 = (*i);
3327 /* the region that used to be in the old playlist is not
3328 moved to the new one - we make a copy of it. as a result,
3329 any existing editor for the region should no longer be
3333 if (!drag_info.copy) {
3334 rv2->hide_region_editor();
3336 new_selection.push_back (rv2);
3340 /* first, freeze the target tracks */
3342 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3344 Playlist* from_playlist;
3345 Playlist* to_playlist;
3347 double ix1, ix2, iy1, iy2;
3349 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3350 (*i)->get_canvas_group()->i2w (ix1, iy1);
3351 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3352 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3354 (*i)->region()->set_opaque (true);
3356 from_playlist = (*i)->region()->playlist();
3357 to_playlist = atv2->playlist();
3359 /* the from_playlist was frozen in the "first_move" case
3360 of the motion handler. the insert can fail,
3361 but that doesn't matter. it just means
3362 we already have the playlist in the list.
3365 motion_frozen_playlists.insert (from_playlist);
3367 /* only freeze the to_playlist once */
3369 insert_result = motion_frozen_playlists.insert(to_playlist);
3370 if (insert_result.second) {
3371 to_playlist->freeze();
3372 session->add_command(new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3377 /* now do it again with the actual operations */
3379 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3381 Playlist* from_playlist;
3382 Playlist* to_playlist;
3384 double ix1, ix2, iy1, iy2;
3386 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3387 (*i)->get_canvas_group()->i2w (ix1, iy1);
3388 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3389 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3391 from_playlist = (*i)->region()->playlist();
3392 to_playlist = atv2->playlist();
3394 latest_regionview = 0;
3396 where = (nframes_t) (unit_to_frame (ix1) * speed);
3397 boost::shared_ptr<Region> new_region (RegionFactory::create ((*i)->region()));
3399 from_playlist->remove_region (((*i)->region()));
3401 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3402 to_playlist->add_region (new_region, where);
3405 if (latest_regionview) {
3406 selection->add (latest_regionview);
3412 /* motion within a single track */
3414 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3418 if (rv->region()->locked()) {
3422 if (regionview_x_movement) {
3423 double ownspeed = 1.0;
3424 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3426 if (atv && atv->get_diskstream()) {
3427 ownspeed = atv->get_diskstream()->speed();
3430 /* base the new region position on the current position of the regionview.*/
3432 double ix1, ix2, iy1, iy2;
3434 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3435 rv->get_canvas_group()->i2w (ix1, iy1);
3436 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3440 where = rv->region()->position();
3443 rv->get_time_axis_view().reveal_dependent_views (*rv);
3445 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3447 rv->region()->set_position (where, (void *) this);
3448 rv->region()->set_opaque (true);
3453 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3455 session->add_command (new MementoCommand<Playlist>(*(*p), 0, & (*p)->get_state()));
3458 motion_frozen_playlists.clear ();
3461 commit_reversible_command ();
3466 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3468 /* Either add to or set the set the region selection, unless
3469 this is an alignment click (control used)
3472 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3473 TimeAxisView* tv = &rv.get_time_axis_view();
3474 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3476 if (atv && atv->is_audio_track()) {
3477 speed = atv->get_diskstream()->speed();
3480 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3482 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3484 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3486 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3490 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3496 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3507 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3508 case AudioClock::BBT:
3509 session->bbt_time (frame, bbt);
3510 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3513 case AudioClock::SMPTE:
3514 session->smpte_time (frame, smpte);
3515 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3518 case AudioClock::MinSec:
3519 /* XXX fix this to compute min/sec properly */
3520 session->smpte_time (frame, smpte);
3521 secs = smpte.seconds + ((float) smpte.frames / Config->get_smpte_frames_per_second());
3522 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3526 snprintf (buf, sizeof(buf), "%u", frame);
3530 if (xpos >= 0 && ypos >=0) {
3531 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3534 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3536 show_verbose_canvas_cursor ();
3540 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3547 Meter meter_at_start(session->tempo_map().meter_at(start));
3553 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3554 case AudioClock::BBT:
3555 session->bbt_time (start, sbbt);
3556 session->bbt_time (end, ebbt);
3559 /* XXX this computation won't work well if the
3560 user makes a selection that spans any meter changes.
3563 ebbt.bars -= sbbt.bars;
3564 if (ebbt.beats >= sbbt.beats) {
3565 ebbt.beats -= sbbt.beats;
3568 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3570 if (ebbt.ticks >= sbbt.ticks) {
3571 ebbt.ticks -= sbbt.ticks;
3574 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3577 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3580 case AudioClock::SMPTE:
3581 session->smpte_duration (end - start, smpte);
3582 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3585 case AudioClock::MinSec:
3586 /* XXX fix this to compute min/sec properly */
3587 session->smpte_duration (end - start, smpte);
3588 secs = smpte.seconds + ((float) smpte.frames / Config->get_smpte_frames_per_second());
3589 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3593 snprintf (buf, sizeof(buf), "%u", end - start);
3597 if (xpos >= 0 && ypos >=0) {
3598 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3601 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3603 show_verbose_canvas_cursor ();
3607 Editor::collect_new_region_view (RegionView* rv)
3609 latest_regionview = rv;
3613 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3615 if (clicked_regionview == 0) {
3619 /* lets try to create new Region for the selection */
3621 vector<boost::shared_ptr<AudioRegion> > new_regions;
3622 create_region_from_selection (new_regions);
3624 if (new_regions.empty()) {
3628 /* XXX fix me one day to use all new regions */
3630 boost::shared_ptr<Region> region (new_regions.front());
3632 /* add it to the current stream/playlist.
3634 tricky: the streamview for the track will add a new regionview. we will
3635 catch the signal it sends when it creates the regionview to
3636 set the regionview we want to then drag.
3639 latest_regionview = 0;
3640 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3642 /* A selection grab currently creates two undo/redo operations, one for
3643 creating the new region and another for moving it.
3646 begin_reversible_command (_("selection grab"));
3648 Playlist* playlist = clicked_trackview->playlist();
3650 XMLNode *before = &(playlist->get_state());
3651 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3652 XMLNode *after = &(playlist->get_state());
3653 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3655 commit_reversible_command ();
3659 if (latest_regionview == 0) {
3660 /* something went wrong */
3664 /* we need to deselect all other regionviews, and select this one
3665 i'm ignoring undo stuff, because the region creation will take care of it */
3666 selection->set (latest_regionview);
3668 drag_info.item = latest_regionview->get_canvas_group();
3669 drag_info.data = latest_regionview;
3670 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3671 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3675 drag_info.last_trackview = clicked_trackview;
3676 drag_info.last_frame_position = latest_regionview->region()->position();
3677 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3679 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3683 Editor::cancel_selection ()
3685 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3686 (*i)->hide_selection ();
3688 begin_reversible_command (_("cancel selection"));
3689 selection->clear ();
3690 clicked_selection = 0;
3691 commit_reversible_command ();
3695 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3697 nframes_t start = 0;
3704 drag_info.item = item;
3705 drag_info.motion_callback = &Editor::drag_selection;
3706 drag_info.finished_callback = &Editor::end_selection_op;
3711 case CreateSelection:
3712 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3713 drag_info.copy = true;
3715 drag_info.copy = false;
3717 start_grab (event, selector_cursor);
3720 case SelectionStartTrim:
3721 if (clicked_trackview) {
3722 clicked_trackview->order_selection_trims (item, true);
3724 start_grab (event, trimmer_cursor);
3725 start = selection->time[clicked_selection].start;
3726 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3729 case SelectionEndTrim:
3730 if (clicked_trackview) {
3731 clicked_trackview->order_selection_trims (item, false);
3733 start_grab (event, trimmer_cursor);
3734 end = selection->time[clicked_selection].end;
3735 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3739 start = selection->time[clicked_selection].start;
3741 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3745 if (selection_op == SelectionMove) {
3746 show_verbose_time_cursor(start, 10);
3748 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3753 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3755 nframes_t start = 0;
3758 nframes_t pending_position;
3760 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3761 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3764 pending_position = 0;
3767 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3768 snap_to (pending_position);
3771 /* only alter selection if the current frame is
3772 different from the last frame position (adjusted)
3775 if (pending_position == drag_info.last_pointer_frame) return;
3777 switch (selection_op) {
3778 case CreateSelection:
3780 if (drag_info.first_move) {
3781 snap_to (drag_info.grab_frame);
3784 if (pending_position < drag_info.grab_frame) {
3785 start = pending_position;
3786 end = drag_info.grab_frame;
3788 end = pending_position;
3789 start = drag_info.grab_frame;
3792 /* first drag: Either add to the selection
3793 or create a new selection->
3796 if (drag_info.first_move) {
3798 begin_reversible_command (_("range selection"));
3800 if (drag_info.copy) {
3801 /* adding to the selection */
3802 clicked_selection = selection->add (start, end);
3803 drag_info.copy = false;
3805 /* new selection-> */
3806 clicked_selection = selection->set (clicked_trackview, start, end);
3811 case SelectionStartTrim:
3813 if (drag_info.first_move) {
3814 begin_reversible_command (_("trim selection start"));
3817 start = selection->time[clicked_selection].start;
3818 end = selection->time[clicked_selection].end;
3820 if (pending_position > end) {
3823 start = pending_position;
3827 case SelectionEndTrim:
3829 if (drag_info.first_move) {
3830 begin_reversible_command (_("trim selection end"));
3833 start = selection->time[clicked_selection].start;
3834 end = selection->time[clicked_selection].end;
3836 if (pending_position < start) {
3839 end = pending_position;
3846 if (drag_info.first_move) {
3847 begin_reversible_command (_("move selection"));
3850 start = selection->time[clicked_selection].start;
3851 end = selection->time[clicked_selection].end;
3853 length = end - start;
3855 start = pending_position;
3858 end = start + length;
3863 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3864 start_canvas_autoscroll (1);
3868 selection->replace (clicked_selection, start, end);
3871 drag_info.last_pointer_frame = pending_position;
3872 drag_info.first_move = false;
3874 if (selection_op == SelectionMove) {
3875 show_verbose_time_cursor(start, 10);
3877 show_verbose_time_cursor(pending_position, 10);
3882 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3884 if (!drag_info.first_move) {
3885 drag_selection (item, event);
3886 /* XXX this is not object-oriented programming at all. ick */
3887 if (selection->time.consolidate()) {
3888 selection->TimeChanged ();
3890 commit_reversible_command ();
3892 /* just a click, no pointer movement.*/
3894 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3896 selection->clear_time();
3901 /* XXX what happens if its a music selection? */
3902 session->set_audio_range (selection->time);
3903 stop_canvas_autoscroll ();
3907 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3910 TimeAxisView* tvp = clicked_trackview;
3911 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3913 if (tv && tv->is_audio_track()) {
3914 speed = tv->get_diskstream()->speed();
3917 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3918 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3919 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3921 motion_frozen_playlists.clear();
3923 //drag_info.item = clicked_regionview->get_name_highlight();
3924 drag_info.item = item;
3925 drag_info.motion_callback = &Editor::trim_motion_callback;
3926 drag_info.finished_callback = &Editor::trim_finished_callback;
3928 start_grab (event, trimmer_cursor);
3930 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3931 trim_op = ContentsTrim;
3933 /* These will get overridden for a point trim.*/
3934 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3935 /* closer to start */
3936 trim_op = StartTrim;
3937 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3945 show_verbose_time_cursor(region_start, 10);
3948 show_verbose_time_cursor(region_end, 10);
3951 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3957 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3959 RegionView* rv = clicked_regionview;
3960 nframes_t frame_delta = 0;
3961 bool left_direction;
3962 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3964 /* snap modifier works differently here..
3965 its' current state has to be passed to the
3966 various trim functions in order to work properly
3970 TimeAxisView* tvp = clicked_trackview;
3971 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3972 pair<set<Playlist*>::iterator,bool> insert_result;
3974 if (tv && tv->is_audio_track()) {
3975 speed = tv->get_diskstream()->speed();
3978 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3979 left_direction = true;
3981 left_direction = false;
3985 snap_to (drag_info.current_pointer_frame);
3988 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3992 if (drag_info.first_move) {
3998 trim_type = "Region start trim";
4001 trim_type = "Region end trim";
4004 trim_type = "Region content trim";
4008 begin_reversible_command (trim_type);
4010 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4011 (*i)->region()->set_opaque(false);
4012 (*i)->region()->freeze ();
4014 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4016 arv->temporarily_hide_envelope ();
4018 Playlist * pl = (*i)->region()->playlist();
4019 insert_result = motion_frozen_playlists.insert (pl);
4020 if (insert_result.second) {
4021 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4026 if (left_direction) {
4027 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4029 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4034 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4037 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4038 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4044 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4047 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4048 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4055 bool swap_direction = false;
4057 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4058 swap_direction = true;
4061 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4062 i != selection->regions.by_layer().end(); ++i)
4064 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4072 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4075 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4078 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4082 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4083 drag_info.first_move = false;
4087 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4089 boost::shared_ptr<Region> region (rv.region());
4091 if (region->locked()) {
4095 nframes_t new_bound;
4098 TimeAxisView* tvp = clicked_trackview;
4099 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4101 if (tv && tv->is_audio_track()) {
4102 speed = tv->get_diskstream()->speed();
4105 if (left_direction) {
4106 if (swap_direction) {
4107 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4109 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4112 if (swap_direction) {
4113 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4115 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4120 snap_to (new_bound);
4122 region->trim_start ((nframes_t) (new_bound * speed), this);
4123 rv.region_changed (StartChanged);
4127 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4129 boost::shared_ptr<Region> region (rv.region());
4131 if (region->locked()) {
4135 nframes_t new_bound;
4138 TimeAxisView* tvp = clicked_trackview;
4139 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4141 if (tv && tv->is_audio_track()) {
4142 speed = tv->get_diskstream()->speed();
4145 if (left_direction) {
4146 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4148 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4152 snap_to (new_bound, (left_direction ? 0 : 1));
4155 region->trim_front ((nframes_t) (new_bound * speed), this);
4157 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4161 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4163 boost::shared_ptr<Region> region (rv.region());
4165 if (region->locked()) {
4169 nframes_t new_bound;
4172 TimeAxisView* tvp = clicked_trackview;
4173 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4175 if (tv && tv->is_audio_track()) {
4176 speed = tv->get_diskstream()->speed();
4179 if (left_direction) {
4180 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4182 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4186 snap_to (new_bound);
4188 region->trim_end ((nframes_t) (new_bound * speed), this);
4189 rv.region_changed (LengthChanged);
4193 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4195 if (!drag_info.first_move) {
4196 trim_motion_callback (item, event);
4198 if (!clicked_regionview->get_selected()) {
4199 thaw_region_after_trim (*clicked_regionview);
4202 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4203 i != selection->regions.by_layer().end(); ++i)
4205 thaw_region_after_trim (**i);
4206 (*i)->region()->set_opaque(true);
4210 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4212 session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
4215 motion_frozen_playlists.clear ();
4217 commit_reversible_command();
4219 /* no mouse movement */
4225 Editor::point_trim (GdkEvent* event)
4227 RegionView* rv = clicked_regionview;
4228 nframes_t new_bound = drag_info.current_pointer_frame;
4230 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4231 snap_to (new_bound);
4234 /* Choose action dependant on which button was pressed */
4235 switch (event->button.button) {
4237 trim_op = StartTrim;
4238 begin_reversible_command (_("Start point trim"));
4240 if (rv->get_selected()) {
4242 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4243 i != selection->regions.by_layer().end(); ++i)
4245 if (!(*i)->region()->locked()) {
4246 Playlist *pl = (*i)->region()->playlist();
4247 XMLNode &before = pl->get_state();
4248 (*i)->region()->trim_front (new_bound, this);
4249 XMLNode &after = pl->get_state();
4250 session->add_command(new MementoCommand<Playlist>(*pl, &before, &after));
4256 if (!rv->region()->locked()) {
4257 Playlist *pl = rv->region()->playlist();
4258 XMLNode &before = pl->get_state();
4259 rv->region()->trim_front (new_bound, this);
4260 XMLNode &after = pl->get_state();
4261 session->add_command(new MementoCommand<Playlist>(*pl, &before, &after));
4265 commit_reversible_command();
4270 begin_reversible_command (_("End point trim"));
4272 if (rv->get_selected()) {
4274 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4276 if (!(*i)->region()->locked()) {
4277 Playlist *pl = (*i)->region()->playlist();
4278 XMLNode &before = pl->get_state();
4279 (*i)->region()->trim_end (new_bound, this);
4280 XMLNode &after = pl->get_state();
4281 session->add_command(new MementoCommand<Playlist>(*pl, &before, &after));
4287 if (!rv->region()->locked()) {
4288 Playlist *pl = rv->region()->playlist();
4289 XMLNode &before = pl->get_state();
4290 rv->region()->trim_end (new_bound, this);
4291 XMLNode &after = pl->get_state();
4292 session->add_command (new MementoCommand<Playlist>(*pl, &before, &after));
4296 commit_reversible_command();
4305 Editor::thaw_region_after_trim (RegionView& rv)
4307 boost::shared_ptr<Region> region (rv.region());
4309 if (region->locked()) {
4313 region->thaw (_("trimmed region"));
4314 XMLNode &after = region->playlist()->get_state();
4315 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4317 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4319 arv->unhide_envelope ();
4323 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4328 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4329 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4333 Location* location = find_location_from_marker (marker, is_start);
4334 location->set_hidden (true, this);
4339 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4345 drag_info.item = item;
4346 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4347 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4349 range_marker_op = op;
4351 if (!temp_location) {
4352 temp_location = new Location;
4356 case CreateRangeMarker:
4357 case CreateTransportMarker:
4359 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4360 drag_info.copy = true;
4362 drag_info.copy = false;
4364 start_grab (event, selector_cursor);
4368 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4373 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4375 nframes_t start = 0;
4377 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4379 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4380 snap_to (drag_info.current_pointer_frame);
4383 /* only alter selection if the current frame is
4384 different from the last frame position.
4387 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4389 switch (range_marker_op) {
4390 case CreateRangeMarker:
4391 case CreateTransportMarker:
4392 if (drag_info.first_move) {
4393 snap_to (drag_info.grab_frame);
4396 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4397 start = drag_info.current_pointer_frame;
4398 end = drag_info.grab_frame;
4400 end = drag_info.current_pointer_frame;
4401 start = drag_info.grab_frame;
4404 /* first drag: Either add to the selection
4405 or create a new selection.
4408 if (drag_info.first_move) {
4410 temp_location->set (start, end);
4414 update_marker_drag_item (temp_location);
4415 range_marker_drag_rect->show();
4416 range_marker_drag_rect->raise_to_top();
4422 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4423 start_canvas_autoscroll (1);
4427 temp_location->set (start, end);
4429 double x1 = frame_to_pixel (start);
4430 double x2 = frame_to_pixel (end);
4431 crect->property_x1() = x1;
4432 crect->property_x2() = x2;
4434 update_marker_drag_item (temp_location);
4437 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4438 drag_info.first_move = false;
4440 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4445 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4447 Location * newloc = 0;
4449 if (!drag_info.first_move) {
4450 drag_range_markerbar_op (item, event);
4452 switch (range_marker_op) {
4453 case CreateRangeMarker:
4455 begin_reversible_command (_("new range marker"));
4456 XMLNode &before = session->locations()->get_state();
4457 newloc = new Location(temp_location->start(), temp_location->end(), "unnamed", Location::IsRangeMarker);
4458 session->locations()->add (newloc, true);
4459 XMLNode &after = session->locations()->get_state();
4460 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4461 commit_reversible_command ();
4463 range_bar_drag_rect->hide();
4464 range_marker_drag_rect->hide();
4468 case CreateTransportMarker:
4469 // popup menu to pick loop or punch
4470 new_transport_marker_context_menu (&event->button, item);
4475 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4477 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4482 start = session->locations()->first_mark_before (drag_info.grab_frame);
4483 end = session->locations()->first_mark_after (drag_info.grab_frame);
4485 if (end == max_frames) {
4486 end = session->current_end_frame ();
4490 start = session->current_start_frame ();
4493 switch (mouse_mode) {
4495 /* find the two markers on either side and then make the selection from it */
4496 cerr << "select between " << start << " .. " << end << endl;
4497 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4501 /* find the two markers on either side of the click and make the range out of it */
4502 selection->set (0, start, end);
4511 stop_canvas_autoscroll ();
4517 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4519 drag_info.item = item;
4520 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4521 drag_info.finished_callback = &Editor::end_mouse_zoom;
4523 start_grab (event, zoom_cursor);
4525 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4529 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4534 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4535 snap_to (drag_info.current_pointer_frame);
4537 if (drag_info.first_move) {
4538 snap_to (drag_info.grab_frame);
4542 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4544 /* base start and end on initial click position */
4545 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4546 start = drag_info.current_pointer_frame;
4547 end = drag_info.grab_frame;
4549 end = drag_info.current_pointer_frame;
4550 start = drag_info.grab_frame;
4555 if (drag_info.first_move) {
4557 zoom_rect->raise_to_top();
4560 reposition_zoom_rect(start, end);
4562 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4563 drag_info.first_move = false;
4565 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4570 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4572 if (!drag_info.first_move) {
4573 drag_mouse_zoom (item, event);
4575 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4576 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4578 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4581 temporal_zoom_to_frame (false, drag_info.grab_frame);
4583 temporal_zoom_step (false);
4584 center_screen (drag_info.grab_frame);
4592 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4594 double x1 = frame_to_pixel (start);
4595 double x2 = frame_to_pixel (end);
4596 double y2 = canvas_height - 2;
4598 zoom_rect->property_x1() = x1;
4599 zoom_rect->property_y1() = 1.0;
4600 zoom_rect->property_x2() = x2;
4601 zoom_rect->property_y2() = y2;
4605 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4607 drag_info.item = item;
4608 drag_info.motion_callback = &Editor::drag_rubberband_select;
4609 drag_info.finished_callback = &Editor::end_rubberband_select;
4611 start_grab (event, cross_hair_cursor);
4613 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4617 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4624 /* use a bigger drag threshold than the default */
4626 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4630 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4631 // snap_to (drag_info.current_pointer_frame);
4633 // if (drag_info.first_move) {
4634 // snap_to (drag_info.grab_frame);
4639 /* base start and end on initial click position */
4640 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4641 start = drag_info.current_pointer_frame;
4642 end = drag_info.grab_frame;
4644 end = drag_info.current_pointer_frame;
4645 start = drag_info.grab_frame;
4648 if (drag_info.current_pointer_y < drag_info.grab_y) {
4649 y1 = drag_info.current_pointer_y;
4650 y2 = drag_info.grab_y;
4653 y2 = drag_info.current_pointer_y;
4654 y1 = drag_info.grab_y;
4658 if (start != end || y1 != y2) {
4660 double x1 = frame_to_pixel (start);
4661 double x2 = frame_to_pixel (end);
4663 rubberband_rect->property_x1() = x1;
4664 rubberband_rect->property_y1() = y1;
4665 rubberband_rect->property_x2() = x2;
4666 rubberband_rect->property_y2() = y2;
4668 rubberband_rect->show();
4669 rubberband_rect->raise_to_top();
4671 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4672 drag_info.first_move = false;
4674 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4679 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4681 if (!drag_info.first_move) {
4683 drag_rubberband_select (item, event);
4686 if (drag_info.current_pointer_y < drag_info.grab_y) {
4687 y1 = drag_info.current_pointer_y;
4688 y2 = drag_info.grab_y;
4691 y2 = drag_info.current_pointer_y;
4692 y1 = drag_info.grab_y;
4696 Selection::Operation op = Keyboard::selection_type (event->button.state);
4699 begin_reversible_command (_("select regions"));
4701 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4702 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4704 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4708 commit_reversible_command ();
4712 selection->clear_regions();
4713 selection->clear_points ();
4714 selection->clear_lines ();
4717 rubberband_rect->hide();
4722 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4724 using namespace Gtkmm2ext;
4726 ArdourPrompter prompter (false);
4728 prompter.set_prompt (_("Name for region:"));
4729 prompter.set_initial_text (clicked_regionview->region()->name());
4730 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4731 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4732 prompter.show_all ();
4733 switch (prompter.run ()) {
4734 case Gtk::RESPONSE_ACCEPT:
4736 prompter.get_result(str);
4738 clicked_regionview->region()->set_name (str);
4746 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4748 drag_info.item = item;
4749 drag_info.motion_callback = &Editor::time_fx_motion;
4750 drag_info.finished_callback = &Editor::end_time_fx;
4754 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4758 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4760 RegionView* rv = clicked_regionview;
4762 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4763 snap_to (drag_info.current_pointer_frame);
4766 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4770 if (drag_info.current_pointer_frame > rv->region()->position()) {
4771 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4774 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4775 drag_info.first_move = false;
4777 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4781 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4783 clicked_regionview->get_time_axis_view().hide_timestretch ();
4785 if (drag_info.first_move) {
4789 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4790 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4792 begin_reversible_command (_("timestretch"));
4794 if (run_timestretch (selection->regions, percentage) == 0) {
4795 session->commit_reversible_command ();
4800 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4802 /* no brushing without a useful snap setting */
4805 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4808 switch (snap_mode) {
4810 return; /* can't work because it allows region to be placed anywhere */
4815 switch (snap_type) {
4818 case SnapToEditCursor:
4825 /* don't brush a copy over the original */
4827 if (pos == rv->region()->position()) {
4831 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4833 if (atv == 0 || !atv->is_audio_track()) {
4837 Playlist* playlist = atv->playlist();
4838 double speed = atv->get_diskstream()->speed();
4840 XMLNode &before = playlist->get_state();
4841 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4842 XMLNode &after = playlist->get_state();
4843 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
4845 // playlist is frozen, so we have to update manually
4847 playlist->Modified(); /* EMIT SIGNAL */
4851 Editor::track_height_step_timeout ()
4854 struct timeval delta;
4856 gettimeofday (&now, 0);
4857 timersub (&now, &last_track_height_step_timestamp, &delta);
4859 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4860 current_stepping_trackview = 0;