2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include <pbd/error.h>
29 #include <gtkmm2ext/utils.h>
30 #include <pbd/memento_command.h>
31 #include <pbd/basename.h>
33 #include "ardour_ui.h"
35 #include "time_axis_view.h"
36 #include "audio_time_axis.h"
37 #include "audio_region_view.h"
38 #include "midi_region_view.h"
40 #include "streamview.h"
41 #include "region_gain_line.h"
42 #include "automation_time_axis.h"
43 #include "control_point.h"
46 #include "selection.h"
49 #include "rgb_macros.h"
51 #include <ardour/types.h>
52 #include <ardour/profile.h>
53 #include <ardour/route.h>
54 #include <ardour/audio_track.h>
55 #include <ardour/audio_diskstream.h>
56 #include <ardour/midi_diskstream.h>
57 #include <ardour/playlist.h>
58 #include <ardour/audioplaylist.h>
59 #include <ardour/audioregion.h>
60 #include <ardour/midi_region.h>
61 #include <ardour/dB.h>
62 #include <ardour/utils.h>
63 #include <ardour/region_factory.h>
64 #include <ardour/source_factory.h>
71 using namespace ARDOUR;
75 using namespace Editing;
77 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
80 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
84 Gdk::ModifierType mask;
85 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas.get_window();
86 Glib::RefPtr<const Gdk::Window> pointer_window;
88 pointer_window = canvas_window->get_pointer (x, y, mask);
90 if (pointer_window == track_canvas.get_bin_window()) {
92 track_canvas.window_to_world (x, y, wx, wy);
93 in_track_canvas = true;
96 in_track_canvas = false;
98 if (pointer_window == time_canvas.get_bin_window()) {
99 time_canvas.window_to_world (x, y, wx, wy);
105 wx += horizontal_adjustment.get_value();
106 wy += vertical_adjustment.get_value();
109 event.type = GDK_BUTTON_RELEASE;
113 where = event_frame (&event, 0, 0);
118 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const
132 switch (event->type) {
133 case GDK_BUTTON_RELEASE:
134 case GDK_BUTTON_PRESS:
135 case GDK_2BUTTON_PRESS:
136 case GDK_3BUTTON_PRESS:
137 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
139 case GDK_MOTION_NOTIFY:
140 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
142 case GDK_ENTER_NOTIFY:
143 case GDK_LEAVE_NOTIFY:
144 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
147 case GDK_KEY_RELEASE:
148 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
151 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
155 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
156 position is negative (as can be the case with motion events in particular),
157 the frame location is always positive.
160 return pixel_to_frame (*pcx);
164 Editor::mouse_mode_toggled (MouseMode m)
166 if (ignore_mouse_mode_toggle) {
172 if (mouse_select_button.get_active()) {
178 if (mouse_move_button.get_active()) {
184 if (mouse_gain_button.get_active()) {
190 if (mouse_zoom_button.get_active()) {
196 if (mouse_timefx_button.get_active()) {
202 if (mouse_audition_button.get_active()) {
208 if (mouse_note_button.get_active()) {
219 Editor::set_mouse_mode (MouseMode m, bool force)
221 if (drag_info.item) {
225 if (!force && m == mouse_mode) {
233 if (mouse_mode != MouseRange) {
235 /* in all modes except range, hide the range selection,
236 show the object (region) selection.
239 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
240 (*i)->set_should_show_selection (true);
242 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
243 (*i)->hide_selection ();
249 in range mode,show the range selection.
252 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
253 if ((*i)->get_selected()) {
254 (*i)->show_selection (selection->time);
259 /* XXX the hack of unsetting all other buttons should go
260 away once GTK2 allows us to use regular radio buttons drawn like
261 normal buttons, rather than my silly GroupedButton hack.
264 ignore_mouse_mode_toggle = true;
266 switch (mouse_mode) {
268 mouse_select_button.set_active (true);
269 current_canvas_cursor = selector_cursor;
273 mouse_move_button.set_active (true);
274 current_canvas_cursor = grabber_cursor;
278 mouse_gain_button.set_active (true);
279 current_canvas_cursor = cross_hair_cursor;
283 mouse_zoom_button.set_active (true);
284 current_canvas_cursor = zoom_cursor;
288 mouse_timefx_button.set_active (true);
289 current_canvas_cursor = time_fx_cursor; // just use playhead
293 mouse_audition_button.set_active (true);
294 current_canvas_cursor = speaker_cursor;
298 mouse_note_button.set_active (true);
299 set_midi_edit_cursor (current_midi_edit_mode());
303 if (mouse_mode == MouseNote)
304 midi_toolbar_frame.show();
306 midi_toolbar_frame.hide();
308 ignore_mouse_mode_toggle = false;
311 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
316 Editor::step_mouse_mode (bool next)
318 switch (current_mouse_mode()) {
320 if (next) set_mouse_mode (MouseRange);
321 else set_mouse_mode (MouseTimeFX);
325 if (next) set_mouse_mode (MouseZoom);
326 else set_mouse_mode (MouseObject);
330 if (next) set_mouse_mode (MouseGain);
331 else set_mouse_mode (MouseRange);
335 if (next) set_mouse_mode (MouseTimeFX);
336 else set_mouse_mode (MouseZoom);
340 if (next) set_mouse_mode (MouseAudition);
341 else set_mouse_mode (MouseGain);
345 if (next) set_mouse_mode (MouseObject);
346 else set_mouse_mode (MouseTimeFX);
350 if (next) set_mouse_mode (MouseObject);
351 else set_mouse_mode (MouseAudition);
357 Editor::midi_edit_mode_toggled (MidiEditMode m)
359 if (ignore_midi_edit_mode_toggle) {
365 if (midi_tool_pencil_button.get_active())
366 set_midi_edit_mode (m);
370 if (midi_tool_select_button.get_active())
371 set_midi_edit_mode (m);
375 if (midi_tool_erase_button.get_active())
376 set_midi_edit_mode (m);
383 set_midi_edit_cursor(m);
388 Editor::set_midi_edit_mode (MidiEditMode m, bool force)
390 if (drag_info.item) {
394 if (!force && m == midi_edit_mode) {
402 ignore_midi_edit_mode_toggle = true;
404 switch (midi_edit_mode) {
406 midi_tool_pencil_button.set_active (true);
410 midi_tool_select_button.set_active (true);
414 midi_tool_erase_button.set_active (true);
418 ignore_midi_edit_mode_toggle = false;
420 set_midi_edit_cursor (current_midi_edit_mode());
423 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
428 Editor::set_midi_edit_cursor (MidiEditMode m)
430 switch (midi_edit_mode) {
432 current_canvas_cursor = midi_pencil_cursor;
436 current_canvas_cursor = midi_select_cursor;
440 current_canvas_cursor = midi_erase_cursor;
446 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
450 /* in object/audition/timefx mode, any button press sets
451 the selection if the object can be selected. this is a
452 bit of hack, because we want to avoid this if the
453 mouse operation is a region alignment.
455 note: not dbl-click or triple-click
458 if (((mouse_mode != MouseObject) &&
459 (mouse_mode != MouseAudition || item_type != RegionItem) &&
460 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
461 (mouse_mode != MouseRange)) ||
463 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
468 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
470 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
472 /* almost no selection action on modified button-2 or button-3 events */
474 if (item_type != RegionItem && event->button.button != 2) {
480 Selection::Operation op = Keyboard::selection_type (event->button.state);
481 bool press = (event->type == GDK_BUTTON_PRESS);
483 // begin_reversible_command (_("select on click"));
487 if (mouse_mode != MouseRange) {
488 commit = set_selected_regionview_from_click (press, op, true);
489 } else if (event->type == GDK_BUTTON_PRESS) {
490 commit = set_selected_track_from_click (press, op, false);
494 case RegionViewNameHighlight:
496 if (mouse_mode != MouseRange) {
497 commit = set_selected_regionview_from_click (press, op, true);
498 } else if (event->type == GDK_BUTTON_PRESS) {
499 commit = set_selected_track_from_click (press, op, false);
504 case FadeInHandleItem:
506 case FadeOutHandleItem:
508 if (mouse_mode != MouseRange) {
509 commit = set_selected_regionview_from_click (press, op, true);
510 } else if (event->type == GDK_BUTTON_PRESS) {
511 commit = set_selected_track_from_click (press, op, false);
515 case CrossfadeViewItem:
516 commit = set_selected_track_from_click (press, op, false);
519 case ControlPointItem:
520 commit = set_selected_track_from_click (press, op, true);
521 if (mouse_mode != MouseRange) {
522 commit |= set_selected_control_point_from_click (op, false);
527 /* for context click or range selection, select track */
528 if (event->button.button == 3) {
529 commit = set_selected_track_from_click (press, op, true);
530 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
531 commit = set_selected_track_from_click (press, op, false);
535 case AutomationTrackItem:
536 commit = set_selected_track_from_click (press, op, true);
544 // commit_reversible_command ();
549 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
551 track_canvas.grab_focus();
553 if (session && session->actively_recording()) {
557 button_selection (item, event, item_type);
559 if (drag_info.item == 0 &&
560 (Keyboard::is_delete_event (&event->button) ||
561 Keyboard::is_context_menu_event (&event->button) ||
562 Keyboard::is_edit_event (&event->button))) {
564 /* handled by button release */
568 switch (event->button.button) {
571 if (event->type == GDK_BUTTON_PRESS) {
573 if (drag_info.item) {
574 drag_info.item->ungrab (event->button.time);
577 /* single mouse clicks on any of these item types operate
578 independent of mouse mode, mostly because they are
579 not on the main track canvas or because we want
584 case PlayheadCursorItem:
585 start_cursor_grab (item, event);
589 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
590 hide_marker (item, event);
592 start_marker_grab (item, event);
596 case TempoMarkerItem:
597 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
598 start_tempo_marker_copy_grab (item, event);
600 start_tempo_marker_grab (item, event);
604 case MeterMarkerItem:
605 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
606 start_meter_marker_copy_grab (item, event);
608 start_meter_marker_grab (item, event);
618 case RangeMarkerBarItem:
619 start_range_markerbar_op (item, event, CreateRangeMarker);
623 case TransportMarkerBarItem:
624 start_range_markerbar_op (item, event, CreateTransportMarker);
633 switch (mouse_mode) {
636 case StartSelectionTrimItem:
637 start_selection_op (item, event, SelectionStartTrim);
640 case EndSelectionTrimItem:
641 start_selection_op (item, event, SelectionEndTrim);
645 if (Keyboard::modifier_state_contains
646 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
647 // contains and not equals because I can't use alt as a modifier alone.
648 start_selection_grab (item, event);
649 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
650 /* grab selection for moving */
651 start_selection_op (item, event, SelectionMove);
654 /* this was debated, but decided the more common action was to
655 make a new selection */
656 start_selection_op (item, event, CreateSelection);
661 start_selection_op (item, event, CreateSelection);
667 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
668 event->type == GDK_BUTTON_PRESS) {
670 start_rubberband_select (item, event);
672 } else if (event->type == GDK_BUTTON_PRESS) {
675 case FadeInHandleItem:
676 start_fade_in_grab (item, event);
679 case FadeOutHandleItem:
680 start_fade_out_grab (item, event);
684 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
685 start_region_copy_grab (item, event);
686 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
687 start_region_brush_grab (item, event);
689 start_region_grab (item, event);
693 case RegionViewNameHighlight:
694 start_trim (item, event);
699 /* rename happens on edit clicks */
700 start_trim (clicked_regionview->get_name_highlight(), event);
704 case ControlPointItem:
705 start_control_point_grab (item, event);
709 case AutomationLineItem:
710 start_line_grab_from_line (item, event);
715 case AutomationTrackItem:
716 start_rubberband_select (item, event);
720 case ImageFrameHandleStartItem:
721 imageframe_start_handle_op(item, event) ;
724 case ImageFrameHandleEndItem:
725 imageframe_end_handle_op(item, event) ;
728 case MarkerViewHandleStartItem:
729 markerview_item_start_handle_op(item, event) ;
732 case MarkerViewHandleEndItem:
733 markerview_item_end_handle_op(item, event) ;
737 start_markerview_grab(item, event) ;
740 start_imageframe_grab(item, event) ;
758 // start_line_grab_from_regionview (item, event);
762 start_line_grab_from_line (item, event);
765 case ControlPointItem:
766 start_control_point_grab (item, event);
777 case ControlPointItem:
778 start_control_point_grab (item, event);
781 case AutomationLineItem:
782 start_line_grab_from_line (item, event);
786 // XXX need automation mode to identify which
788 // start_line_grab_from_regionview (item, event);
798 if (event->type == GDK_BUTTON_PRESS) {
799 start_mouse_zoom (item, event);
806 if (item_type == RegionItem) {
807 start_time_fx (item, event);
813 last_scrub_x = event->button.x;
814 scrubbing_direction = 0;
815 /* rest handled in motion & release */
819 start_create_region_grab (item, event);
828 switch (mouse_mode) {
830 if (event->type == GDK_BUTTON_PRESS) {
833 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
834 start_region_copy_grab (item, event);
836 start_region_grab (item, event);
840 case ControlPointItem:
841 start_control_point_grab (item, event);
852 case RegionViewNameHighlight:
853 start_trim (item, event);
858 start_trim (clicked_regionview->get_name_highlight(), event);
869 if (event->type == GDK_BUTTON_PRESS) {
870 /* relax till release */
877 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
878 temporal_zoom_session();
880 temporal_zoom_to_frame (true, event_frame(event));
903 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
905 nframes_t where = event_frame (event, 0, 0);
907 /* no action if we're recording */
909 if (session && session->actively_recording()) {
913 /* first, see if we're finishing a drag ... */
915 if (drag_info.item) {
916 if (end_grab (item, event)) {
917 /* grab dragged, so do nothing else */
922 button_selection (item, event, item_type);
924 /* edit events get handled here */
926 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
932 case TempoMarkerItem:
933 edit_tempo_marker (item);
936 case MeterMarkerItem:
937 edit_meter_marker (item);
941 if (clicked_regionview->name_active()) {
942 return mouse_rename_region (item, event);
952 /* context menu events get handled here */
954 if (Keyboard::is_context_menu_event (&event->button)) {
956 if (drag_info.item == 0) {
958 /* no matter which button pops up the context menu, tell the menu
959 widget to use button 1 to drive menu selection.
964 case FadeInHandleItem:
966 case FadeOutHandleItem:
967 popup_fade_context_menu (1, event->button.time, item, item_type);
971 popup_track_context_menu (1, event->button.time, where);
975 case RegionViewNameHighlight:
977 popup_track_context_menu (1, event->button.time, where);
981 popup_track_context_menu (1, event->button.time, where);
984 case AutomationTrackItem:
985 case CrossfadeViewItem:
986 popup_track_context_menu (1, event->button.time, where);
990 case RangeMarkerBarItem:
991 case TransportMarkerBarItem:
994 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
998 marker_context_menu (&event->button, item);
1001 case TempoMarkerItem:
1002 tm_marker_context_menu (&event->button, item);
1005 case MeterMarkerItem:
1006 tm_marker_context_menu (&event->button, item);
1010 case ImageFrameItem:
1011 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1013 case ImageFrameTimeAxisItem:
1014 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1016 case MarkerViewItem:
1017 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1019 case MarkerTimeAxisItem:
1020 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1032 /* delete events get handled here */
1034 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
1036 switch (item_type) {
1037 case TempoMarkerItem:
1038 remove_tempo_marker (item);
1041 case MeterMarkerItem:
1042 remove_meter_marker (item);
1046 remove_marker (*item, event);
1050 if (mouse_mode == MouseObject) {
1051 remove_clicked_region ();
1055 case ControlPointItem:
1056 if (mouse_mode == MouseGain) {
1057 remove_gain_control_point (item, event);
1059 remove_control_point (item, event);
1069 switch (event->button.button) {
1072 switch (item_type) {
1073 /* see comments in button_press_handler */
1074 case PlayheadCursorItem:
1077 case AutomationLineItem:
1078 case StartSelectionTrimItem:
1079 case EndSelectionTrimItem:
1083 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1084 snap_to (where, 0, true);
1086 mouse_add_new_marker (where);
1090 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1093 mouse_add_new_tempo_event (where);
1097 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1105 switch (mouse_mode) {
1107 switch (item_type) {
1108 case AutomationTrackItem:
1109 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->add_automation_event
1123 // Gain only makes sense for audio regions
1125 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1129 switch (item_type) {
1131 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1135 case AutomationTrackItem:
1136 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1137 add_automation_event (item, event, where, event->button.y);
1147 if (scrubbing_direction == 0) {
1148 /* no drag, just a click */
1149 switch (item_type) {
1151 audition_selected_region ();
1157 /* make sure we stop */
1158 session->request_transport_speed (0.0);
1172 switch (mouse_mode) {
1176 // x_style_paste (where, 1.0);
1196 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1202 switch (item_type) {
1203 case ControlPointItem:
1204 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1205 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1206 cp->set_visible (true);
1210 at_y = cp->get_y ();
1211 cp->item()->i2w (at_x, at_y);
1215 fraction = 1.0 - (cp->get_y() / cp->line().height());
1217 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1218 show_verbose_canvas_cursor ();
1220 if (is_drawable()) {
1221 track_canvas.get_window()->set_cursor (*fader_cursor);
1227 if (mouse_mode == MouseGain) {
1228 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1230 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1231 if (is_drawable()) {
1232 track_canvas.get_window()->set_cursor (*fader_cursor);
1237 case AutomationLineItem:
1238 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1240 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1242 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1244 if (is_drawable()) {
1245 track_canvas.get_window()->set_cursor (*fader_cursor);
1250 case RegionViewNameHighlight:
1251 if (is_drawable() && mouse_mode == MouseObject) {
1252 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1256 case StartSelectionTrimItem:
1257 case EndSelectionTrimItem:
1260 case ImageFrameHandleStartItem:
1261 case ImageFrameHandleEndItem:
1262 case MarkerViewHandleStartItem:
1263 case MarkerViewHandleEndItem:
1266 if (is_drawable()) {
1267 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1271 case PlayheadCursorItem:
1272 if (is_drawable()) {
1273 track_canvas.get_window()->set_cursor (*grabber_cursor);
1277 case RegionViewName:
1279 /* when the name is not an active item, the entire name highlight is for trimming */
1281 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1282 if (mouse_mode == MouseObject && is_drawable()) {
1283 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1289 case AutomationTrackItem:
1290 if (is_drawable()) {
1291 Gdk::Cursor *cursor;
1292 switch (mouse_mode) {
1294 cursor = selector_cursor;
1297 cursor = zoom_cursor;
1300 cursor = cross_hair_cursor;
1304 track_canvas.get_window()->set_cursor (*cursor);
1306 AutomationTimeAxisView* atv;
1307 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1308 clear_entered_track = false;
1309 set_entered_track (atv);
1315 case RangeMarkerBarItem:
1316 case TransportMarkerBarItem:
1319 if (is_drawable()) {
1320 time_canvas.get_window()->set_cursor (*timebar_cursor);
1325 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1328 entered_marker = marker;
1329 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1331 case MeterMarkerItem:
1332 case TempoMarkerItem:
1333 if (is_drawable()) {
1334 time_canvas.get_window()->set_cursor (*timebar_cursor);
1337 case FadeInHandleItem:
1338 case FadeOutHandleItem:
1339 if (mouse_mode == MouseObject) {
1340 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1342 rect->property_fill_color_rgba() = 0;
1343 rect->property_outline_pixels() = 1;
1352 /* second pass to handle entered track status in a comprehensible way.
1355 switch (item_type) {
1357 case AutomationLineItem:
1358 case ControlPointItem:
1359 /* these do not affect the current entered track state */
1360 clear_entered_track = false;
1363 case AutomationTrackItem:
1364 /* handled above already */
1368 set_entered_track (0);
1376 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1385 switch (item_type) {
1386 case ControlPointItem:
1387 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1388 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1389 if (cp->line().npoints() > 1 && !cp->selected()) {
1390 cp->set_visible (false);
1394 if (is_drawable()) {
1395 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1398 hide_verbose_canvas_cursor ();
1401 case RegionViewNameHighlight:
1402 case StartSelectionTrimItem:
1403 case EndSelectionTrimItem:
1404 case PlayheadCursorItem:
1407 case ImageFrameHandleStartItem:
1408 case ImageFrameHandleEndItem:
1409 case MarkerViewHandleStartItem:
1410 case MarkerViewHandleEndItem:
1413 if (is_drawable()) {
1414 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1419 case AutomationLineItem:
1420 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1422 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1424 line->property_fill_color_rgba() = al->get_line_color();
1426 if (is_drawable()) {
1427 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1431 case RegionViewName:
1432 /* see enter_handler() for notes */
1433 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1434 if (is_drawable() && mouse_mode == MouseObject) {
1435 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1440 case RangeMarkerBarItem:
1441 case TransportMarkerBarItem:
1445 if (is_drawable()) {
1446 time_canvas.get_window()->set_cursor (*timebar_cursor);
1451 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1455 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1456 location_flags_changed (loc, this);
1459 case MeterMarkerItem:
1460 case TempoMarkerItem:
1462 if (is_drawable()) {
1463 time_canvas.get_window()->set_cursor (*timebar_cursor);
1468 case FadeInHandleItem:
1469 case FadeOutHandleItem:
1470 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1472 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1474 rect->property_fill_color_rgba() = rv->get_fill_color();
1475 rect->property_outline_pixels() = 0;
1480 case AutomationTrackItem:
1481 if (is_drawable()) {
1482 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1483 clear_entered_track = true;
1484 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1496 Editor::left_automation_track ()
1498 if (clear_entered_track) {
1499 set_entered_track (0);
1500 clear_entered_track = false;
1506 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1510 /* We call this so that MOTION_NOTIFY events continue to be
1511 delivered to the canvas. We need to do this because we set
1512 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1513 the density of the events, at the expense of a round-trip
1514 to the server. Given that this will mostly occur on cases
1515 where DISPLAY = :0.0, and given the cost of what the motion
1516 event might do, its a good tradeoff.
1519 track_canvas.get_pointer (x, y);
1521 if (current_stepping_trackview) {
1522 /* don't keep the persistent stepped trackview if the mouse moves */
1523 current_stepping_trackview = 0;
1524 step_timeout.disconnect ();
1527 if (session && session->actively_recording()) {
1528 /* Sorry. no dragging stuff around while we record */
1532 drag_info.item_type = item_type;
1533 drag_info.last_pointer_x = drag_info.current_pointer_x;
1534 drag_info.last_pointer_y = drag_info.current_pointer_y;
1535 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1536 &drag_info.current_pointer_y);
1538 switch (mouse_mode) {
1544 if (scrubbing_direction == 0) {
1546 session->request_locate (drag_info.current_pointer_frame, false);
1547 session->request_transport_speed (0.1);
1548 scrubbing_direction = 1;
1553 if (last_scrub_x > drag_info.current_pointer_x) {
1554 /* move to the left */
1556 if (scrubbing_direction > 0) {
1557 /* we reversed direction to go backwards */
1559 session->request_transport_speed (-0.1);
1562 /* still moving to the left (backwards) */
1564 delta = 0.005 * (last_scrub_x - drag_info.current_pointer_x);
1565 session->request_transport_speed (session->transport_speed() - delta);
1568 scrubbing_direction = -1;
1571 /* move to the right */
1572 if (scrubbing_direction < 0) {
1573 /* we reversed direction to go forward */
1575 session->request_transport_speed (0.1);
1577 /* still moving to the right */
1579 delta = 0.005 * (drag_info.current_pointer_x - last_scrub_x);
1580 session->request_transport_speed (session->transport_speed() + delta);
1583 scrubbing_direction = 1;
1587 last_scrub_x = drag_info.current_pointer_x;
1595 if (!from_autoscroll && drag_info.item) {
1596 /* item != 0 is the best test i can think of for dragging.
1598 if (!drag_info.move_threshold_passed) {
1600 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1601 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1603 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1605 // and change the initial grab loc/frame if this drag info wants us to
1607 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1608 drag_info.grab_frame = drag_info.current_pointer_frame;
1609 drag_info.grab_x = drag_info.current_pointer_x;
1610 drag_info.grab_y = drag_info.current_pointer_y;
1611 drag_info.last_pointer_frame = drag_info.grab_frame;
1612 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1617 switch (item_type) {
1618 case PlayheadCursorItem:
1620 case ControlPointItem:
1621 case TempoMarkerItem:
1622 case MeterMarkerItem:
1623 case RegionViewNameHighlight:
1624 case StartSelectionTrimItem:
1625 case EndSelectionTrimItem:
1628 case AutomationLineItem:
1629 case FadeInHandleItem:
1630 case FadeOutHandleItem:
1633 case ImageFrameHandleStartItem:
1634 case ImageFrameHandleEndItem:
1635 case MarkerViewHandleStartItem:
1636 case MarkerViewHandleEndItem:
1639 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1640 (event->motion.state & Gdk::BUTTON2_MASK))) {
1641 if (!from_autoscroll) {
1642 maybe_autoscroll (event);
1644 (this->*(drag_info.motion_callback)) (item, event);
1653 switch (mouse_mode) {
1659 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1660 (event->motion.state & GDK_BUTTON2_MASK))) {
1661 if (!from_autoscroll) {
1662 maybe_autoscroll (event);
1664 (this->*(drag_info.motion_callback)) (item, event);
1675 track_canvas_motion (event);
1676 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1684 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1686 if (drag_info.item == 0) {
1687 fatal << _("programming error: start_grab called without drag item") << endmsg;
1693 cursor = grabber_cursor;
1696 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1698 if (event->button.button == 2) {
1699 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1700 drag_info.y_constrained = true;
1701 drag_info.x_constrained = false;
1703 drag_info.y_constrained = false;
1704 drag_info.x_constrained = true;
1707 drag_info.x_constrained = false;
1708 drag_info.y_constrained = false;
1711 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1712 drag_info.last_pointer_frame = drag_info.grab_frame;
1713 drag_info.current_pointer_frame = drag_info.grab_frame;
1714 drag_info.current_pointer_x = drag_info.grab_x;
1715 drag_info.current_pointer_y = drag_info.grab_y;
1716 drag_info.last_pointer_x = drag_info.current_pointer_x;
1717 drag_info.last_pointer_y = drag_info.current_pointer_y;
1718 drag_info.cumulative_x_drag = 0;
1719 drag_info.cumulative_y_drag = 0;
1720 drag_info.first_move = true;
1721 drag_info.move_threshold_passed = false;
1722 drag_info.want_move_threshold = false;
1723 drag_info.pointer_frame_offset = 0;
1724 drag_info.brushing = false;
1725 drag_info.copied_location = 0;
1727 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1729 event->button.time);
1731 if (session && session->transport_rolling()) {
1732 drag_info.was_rolling = true;
1734 drag_info.was_rolling = false;
1737 switch (snap_type) {
1738 case SnapToRegionStart:
1739 case SnapToRegionEnd:
1740 case SnapToRegionSync:
1741 case SnapToRegionBoundary:
1742 build_region_boundary_cache ();
1750 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1752 drag_info.item->ungrab (0);
1753 drag_info.item = new_item;
1756 cursor = grabber_cursor;
1759 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1763 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1765 bool did_drag = false;
1767 stop_canvas_autoscroll ();
1769 if (drag_info.item == 0) {
1773 drag_info.item->ungrab (event->button.time);
1775 if (drag_info.finished_callback) {
1776 drag_info.last_pointer_x = drag_info.current_pointer_x;
1777 drag_info.last_pointer_y = drag_info.current_pointer_y;
1778 (this->*(drag_info.finished_callback)) (item, event);
1781 did_drag = !drag_info.first_move;
1783 hide_verbose_canvas_cursor();
1786 drag_info.copy = false;
1787 drag_info.motion_callback = 0;
1788 drag_info.finished_callback = 0;
1789 drag_info.last_trackview = 0;
1790 drag_info.last_frame_position = 0;
1791 drag_info.grab_frame = 0;
1792 drag_info.last_pointer_frame = 0;
1793 drag_info.current_pointer_frame = 0;
1794 drag_info.brushing = false;
1796 if (drag_info.copied_location) {
1797 delete drag_info.copied_location;
1798 drag_info.copied_location = 0;
1805 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1807 drag_info.item = item;
1808 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1809 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1813 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1814 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1818 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1820 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
1824 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1826 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1828 nframes_t fade_length;
1830 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1831 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1837 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1841 if (pos < (arv->region()->position() + 64)) {
1842 fade_length = 64; // this should be a minimum defined somewhere
1843 } else if (pos > arv->region()->last_frame()) {
1844 fade_length = arv->region()->length();
1846 fade_length = pos - arv->region()->position();
1848 /* mapover the region selection */
1850 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1852 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1858 tmp->reset_fade_in_shape_width (fade_length);
1861 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1863 drag_info.first_move = false;
1867 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1869 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1871 nframes_t fade_length;
1873 if (drag_info.first_move) return;
1875 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1876 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1881 if (pos < (arv->region()->position() + 64)) {
1882 fade_length = 64; // this should be a minimum defined somewhere
1883 } else if (pos > arv->region()->last_frame()) {
1884 fade_length = arv->region()->length();
1886 fade_length = pos - arv->region()->position();
1889 begin_reversible_command (_("change fade in length"));
1891 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1893 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1899 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
1900 XMLNode &before = alist->get_state();
1902 tmp->audio_region()->set_fade_in_length (fade_length);
1904 XMLNode &after = alist->get_state();
1905 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1908 commit_reversible_command ();
1912 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1914 drag_info.item = item;
1915 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1916 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1920 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1921 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1925 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1927 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
1931 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1933 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1935 nframes_t fade_length;
1937 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1938 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1943 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1947 if (pos > (arv->region()->last_frame() - 64)) {
1948 fade_length = 64; // this should really be a minimum fade defined somewhere
1950 else if (pos < arv->region()->position()) {
1951 fade_length = arv->region()->length();
1954 fade_length = arv->region()->last_frame() - pos;
1957 /* mapover the region selection */
1959 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1961 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1967 tmp->reset_fade_out_shape_width (fade_length);
1970 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1972 drag_info.first_move = false;
1976 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1978 if (drag_info.first_move) return;
1980 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1982 nframes_t fade_length;
1984 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1985 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1991 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1995 if (pos > (arv->region()->last_frame() - 64)) {
1996 fade_length = 64; // this should really be a minimum fade defined somewhere
1998 else if (pos < arv->region()->position()) {
1999 fade_length = arv->region()->length();
2002 fade_length = arv->region()->last_frame() - pos;
2005 begin_reversible_command (_("change fade out length"));
2007 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2009 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2015 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2016 XMLNode &before = alist->get_state();
2018 tmp->audio_region()->set_fade_out_length (fade_length);
2020 XMLNode &after = alist->get_state();
2021 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2024 commit_reversible_command ();
2028 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2030 drag_info.item = item;
2031 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2032 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2036 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2037 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2041 Cursor* cursor = (Cursor *) drag_info.data;
2043 if (cursor == playhead_cursor) {
2044 _dragging_playhead = true;
2046 if (session && drag_info.was_rolling) {
2047 session->request_stop ();
2050 if (session && session->is_auditioning()) {
2051 session->cancel_audition ();
2055 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2057 show_verbose_time_cursor (cursor->current_frame, 10);
2061 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2063 Cursor* cursor = (Cursor *) drag_info.data;
2064 nframes_t adjusted_frame;
2066 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2067 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2073 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2074 if (cursor == playhead_cursor && snap_type != SnapToEditPoint) {
2075 snap_to (adjusted_frame);
2079 if (adjusted_frame == drag_info.last_pointer_frame) return;
2081 cursor->set_position (adjusted_frame);
2083 UpdateAllTransportClocks (cursor->current_frame);
2085 show_verbose_time_cursor (cursor->current_frame, 10);
2087 drag_info.last_pointer_frame = adjusted_frame;
2088 drag_info.first_move = false;
2092 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2094 if (drag_info.first_move) return;
2096 cursor_drag_motion_callback (item, event);
2098 _dragging_playhead = false;
2100 if (item == &playhead_cursor->canvas_item) {
2102 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2108 Editor::update_marker_drag_item (Location *location)
2110 double x1 = frame_to_pixel (location->start());
2111 double x2 = frame_to_pixel (location->end());
2113 if (location->is_mark()) {
2114 marker_drag_line_points.front().set_x(x1);
2115 marker_drag_line_points.back().set_x(x1);
2116 marker_drag_line->property_points() = marker_drag_line_points;
2119 range_marker_drag_rect->property_x1() = x1;
2120 range_marker_drag_rect->property_x2() = x2;
2125 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2129 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2130 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2136 Location *location = find_location_from_marker (marker, is_start);
2138 drag_info.item = item;
2139 drag_info.data = marker;
2140 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2141 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2145 _dragging_edit_point = true;
2147 drag_info.copied_location = new Location (*location);
2148 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2150 update_marker_drag_item (location);
2152 if (location->is_mark()) {
2153 // marker_drag_line->show();
2154 // marker_drag_line->raise_to_top();
2156 range_marker_drag_rect->show();
2157 range_marker_drag_rect->raise_to_top();
2161 show_verbose_time_cursor (location->start(), 10);
2163 show_verbose_time_cursor (location->end(), 10);
2166 Selection::Operation op = Keyboard::selection_type (event->button.state);
2169 case Selection::Toggle:
2170 selection->toggle (marker);
2172 case Selection::Set:
2173 selection->set (marker);
2175 case Selection::Extend:
2176 selection->add (marker);
2178 case Selection::Add:
2179 selection->add (marker);
2185 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2188 Marker* marker = (Marker *) drag_info.data;
2189 Location *real_location;
2190 Location *copy_location;
2192 bool move_both = false;
2195 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2196 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2201 nframes_t next = newframe;
2203 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2204 snap_to (newframe, 0, true);
2207 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2211 /* call this to find out if its the start or end */
2213 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2217 if (real_location->locked()) {
2221 /* use the copy that we're "dragging" around */
2223 copy_location = drag_info.copied_location;
2225 f_delta = copy_location->end() - copy_location->start();
2227 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2231 if (copy_location->is_mark()) {
2234 copy_location->set_start (newframe);
2238 if (is_start) { // start-of-range marker
2241 copy_location->set_start (newframe);
2242 copy_location->set_end (newframe + f_delta);
2243 } else if (newframe < copy_location->end()) {
2244 copy_location->set_start (newframe);
2246 snap_to (next, 1, true);
2247 copy_location->set_end (next);
2248 copy_location->set_start (newframe);
2251 } else { // end marker
2254 copy_location->set_end (newframe);
2255 copy_location->set_start (newframe - f_delta);
2256 } else if (newframe > copy_location->start()) {
2257 copy_location->set_end (newframe);
2259 } else if (newframe > 0) {
2260 snap_to (next, -1, true);
2261 copy_location->set_start (next);
2262 copy_location->set_end (newframe);
2267 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2268 drag_info.first_move = false;
2270 update_marker_drag_item (copy_location);
2272 LocationMarkers* lm = find_location_markers (real_location);
2273 lm->set_position (copy_location->start(), copy_location->end());
2274 edit_point_clock.set (copy_location->start());
2276 show_verbose_time_cursor (newframe, 10);
2280 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2282 if (drag_info.first_move) {
2283 marker_drag_motion_callback (item, event);
2287 _dragging_edit_point = false;
2289 Marker* marker = (Marker *) drag_info.data;
2292 begin_reversible_command ( _("move marker") );
2293 XMLNode &before = session->locations()->get_state();
2295 Location * location = find_location_from_marker (marker, is_start);
2299 if (location->locked()) {
2303 if (location->is_mark()) {
2304 location->set_start (drag_info.copied_location->start());
2306 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2310 XMLNode &after = session->locations()->get_state();
2311 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2312 commit_reversible_command ();
2314 marker_drag_line->hide();
2315 range_marker_drag_rect->hide();
2319 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2322 MeterMarker* meter_marker;
2324 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2325 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2329 meter_marker = dynamic_cast<MeterMarker*> (marker);
2331 MetricSection& section (meter_marker->meter());
2333 if (!section.movable()) {
2337 drag_info.item = item;
2338 drag_info.copy = false;
2339 drag_info.data = marker;
2340 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2341 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2345 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2347 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2351 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2354 MeterMarker* meter_marker;
2356 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2357 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2361 meter_marker = dynamic_cast<MeterMarker*> (marker);
2363 // create a dummy marker for visual representation of moving the copy.
2364 // The actual copying is not done before we reach the finish callback.
2366 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2367 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2368 *new MeterSection(meter_marker->meter()));
2370 drag_info.item = &new_marker->the_item();
2371 drag_info.copy = true;
2372 drag_info.data = new_marker;
2373 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2374 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2378 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2380 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2384 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2386 MeterMarker* marker = (MeterMarker *) drag_info.data;
2387 nframes_t adjusted_frame;
2389 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2390 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2396 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2397 snap_to (adjusted_frame);
2400 if (adjusted_frame == drag_info.last_pointer_frame) return;
2402 marker->set_position (adjusted_frame);
2405 drag_info.last_pointer_frame = adjusted_frame;
2406 drag_info.first_move = false;
2408 show_verbose_time_cursor (adjusted_frame, 10);
2412 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2414 if (drag_info.first_move) return;
2416 meter_marker_drag_motion_callback (drag_info.item, event);
2418 MeterMarker* marker = (MeterMarker *) drag_info.data;
2421 TempoMap& map (session->tempo_map());
2422 map.bbt_time (drag_info.last_pointer_frame, when);
2424 if (drag_info.copy == true) {
2425 begin_reversible_command (_("copy meter mark"));
2426 XMLNode &before = map.get_state();
2427 map.add_meter (marker->meter(), when);
2428 XMLNode &after = map.get_state();
2429 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2430 commit_reversible_command ();
2432 // delete the dummy marker we used for visual representation of copying.
2433 // a new visual marker will show up automatically.
2436 begin_reversible_command (_("move meter mark"));
2437 XMLNode &before = map.get_state();
2438 map.move_meter (marker->meter(), when);
2439 XMLNode &after = map.get_state();
2440 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2441 commit_reversible_command ();
2446 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2449 TempoMarker* tempo_marker;
2451 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2452 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2456 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2457 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2461 MetricSection& section (tempo_marker->tempo());
2463 if (!section.movable()) {
2467 drag_info.item = item;
2468 drag_info.copy = false;
2469 drag_info.data = marker;
2470 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2471 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2475 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2476 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2480 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2483 TempoMarker* tempo_marker;
2485 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2486 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2490 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2491 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2495 // create a dummy marker for visual representation of moving the copy.
2496 // The actual copying is not done before we reach the finish callback.
2498 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2499 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2500 *new TempoSection(tempo_marker->tempo()));
2502 drag_info.item = &new_marker->the_item();
2503 drag_info.copy = true;
2504 drag_info.data = new_marker;
2505 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2506 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2510 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2512 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2516 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2518 TempoMarker* marker = (TempoMarker *) drag_info.data;
2519 nframes_t adjusted_frame;
2521 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2522 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2528 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2529 snap_to (adjusted_frame);
2532 if (adjusted_frame == drag_info.last_pointer_frame) return;
2534 /* OK, we've moved far enough to make it worth actually move the thing. */
2536 marker->set_position (adjusted_frame);
2538 show_verbose_time_cursor (adjusted_frame, 10);
2540 drag_info.last_pointer_frame = adjusted_frame;
2541 drag_info.first_move = false;
2545 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2547 if (drag_info.first_move) return;
2549 tempo_marker_drag_motion_callback (drag_info.item, event);
2551 TempoMarker* marker = (TempoMarker *) drag_info.data;
2554 TempoMap& map (session->tempo_map());
2555 map.bbt_time (drag_info.last_pointer_frame, when);
2557 if (drag_info.copy == true) {
2558 begin_reversible_command (_("copy tempo mark"));
2559 XMLNode &before = map.get_state();
2560 map.add_tempo (marker->tempo(), when);
2561 XMLNode &after = map.get_state();
2562 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2563 commit_reversible_command ();
2565 // delete the dummy marker we used for visual representation of copying.
2566 // a new visual marker will show up automatically.
2569 begin_reversible_command (_("move tempo mark"));
2570 XMLNode &before = map.get_state();
2571 map.move_tempo (marker->tempo(), when);
2572 XMLNode &after = map.get_state();
2573 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2574 commit_reversible_command ();
2579 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2581 ControlPoint* control_point;
2583 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2584 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2588 // We shouldn't remove the first or last gain point
2589 if (control_point->line().is_last_point(*control_point) ||
2590 control_point->line().is_first_point(*control_point)) {
2594 control_point->line().remove_point (*control_point);
2598 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
2600 ControlPoint* control_point;
2602 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2603 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2607 control_point->line().remove_point (*control_point);
2611 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2613 ControlPoint* control_point;
2615 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2616 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2620 drag_info.item = item;
2621 drag_info.data = control_point;
2622 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2623 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2625 start_grab (event, fader_cursor);
2627 // start the grab at the center of the control point so
2628 // the point doesn't 'jump' to the mouse after the first drag
2629 drag_info.grab_x = control_point->get_x();
2630 drag_info.grab_y = control_point->get_y();
2631 control_point->line().parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2632 track_canvas.w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y);
2634 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2636 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
2638 double fraction = 1.0 - ((control_point->get_y() - control_point->line().y_position()) / (double)control_point->line().height());
2639 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
2640 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2642 show_verbose_canvas_cursor ();
2646 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2648 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2650 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2651 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2653 if (event->button.state & Keyboard::Alt) {
2658 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2659 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2661 // calculate zero crossing point. back off by .01 to stay on the
2662 // positive side of zero
2664 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line().height() - .01;
2665 cp->line().parent_group().i2w(_unused, zero_gain_y);
2667 // make sure we hit zero when passing through
2668 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2669 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2673 if (drag_info.x_constrained) {
2674 cx = drag_info.grab_x;
2676 if (drag_info.y_constrained) {
2677 cy = drag_info.grab_y;
2680 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2681 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2683 cp->line().parent_group().w2i (cx, cy);
2687 cy = min ((double) (cp->line().y_position() + cp->line().height()), cy);
2689 //translate cx to frames
2690 nframes_t cx_frames = unit_to_frame (cx);
2692 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2693 snap_to (cx_frames);
2696 const double fraction = 1.0 - ((cy - cp->line().y_position()) / (double)cp->line().height());
2700 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2706 cp->line().point_drag (*cp, cx_frames , fraction, push);
2708 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
2710 drag_info.first_move = false;
2714 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2716 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2718 if (drag_info.first_move) {
2722 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2723 reset_point_selection ();
2727 control_point_drag_motion_callback (item, event);
2729 cp->line().end_drag (cp);
2733 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2735 switch (mouse_mode) {
2737 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2738 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2746 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2750 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2751 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2755 start_line_grab (al, event);
2759 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2763 nframes_t frame_within_region;
2765 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2769 cx = event->button.x;
2770 cy = event->button.y;
2771 line->parent_group().w2i (cx, cy);
2772 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2774 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2775 current_line_drag_info.after)) {
2776 /* no adjacent points */
2780 drag_info.item = &line->grab_item();
2781 drag_info.data = line;
2782 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2783 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2785 start_grab (event, fader_cursor);
2787 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2789 line->start_drag (0, drag_info.grab_frame, fraction);
2791 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2792 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2793 show_verbose_canvas_cursor ();
2797 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2799 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2801 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2803 if (event->button.state & Keyboard::Alt) {
2807 double cx = drag_info.current_pointer_x;
2808 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2810 // calculate zero crossing point. back off by .01 to stay on the
2811 // positive side of zero
2813 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
2814 line->parent_group().i2w(_unused, zero_gain_y);
2816 // make sure we hit zero when passing through
2817 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2818 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2822 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2824 line->parent_group().w2i (cx, cy);
2827 cy = min ((double) line->height(), cy);
2829 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2833 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2839 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2841 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2845 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2847 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2848 line_drag_motion_callback (item, event);
2853 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2855 if (selection->regions.empty() || clicked_regionview == 0) {
2859 drag_info.copy = false;
2860 drag_info.item = item;
2861 drag_info.data = clicked_regionview;
2862 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2863 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2868 TimeAxisView* tvp = clicked_axisview;
2869 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2871 if (tv && tv->is_track()) {
2872 speed = tv->get_diskstream()->speed();
2875 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2876 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2877 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2878 // we want a move threshold
2879 drag_info.want_move_threshold = true;
2881 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2883 begin_reversible_command (_("move region(s)"));
2887 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2889 drag_info.copy = false;
2890 drag_info.item = item;
2891 drag_info.data = clicked_axisview;
2892 drag_info.last_trackview = clicked_axisview;
2893 drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
2894 drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
2900 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2902 if (selection->regions.empty() || clicked_regionview == 0) {
2906 drag_info.copy = true;
2907 drag_info.item = item;
2908 drag_info.data = clicked_regionview;
2912 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2913 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2916 if (rtv && rtv->is_track()) {
2917 speed = rtv->get_diskstream()->speed();
2920 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2921 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2922 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2923 // we want a move threshold
2924 drag_info.want_move_threshold = true;
2925 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2926 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2927 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2931 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2933 if (selection->regions.empty() || clicked_regionview == 0) {
2937 drag_info.copy = false;
2938 drag_info.item = item;
2939 drag_info.data = clicked_regionview;
2940 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2941 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2946 TimeAxisView* tvp = clicked_axisview;
2947 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2949 if (tv && tv->is_track()) {
2950 speed = tv->get_diskstream()->speed();
2953 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2954 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2955 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2956 // we want a move threshold
2957 drag_info.want_move_threshold = true;
2958 drag_info.brushing = true;
2960 begin_reversible_command (_("Drag region brush"));
2964 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2968 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2969 nframes_t pending_region_position = 0;
2970 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2971 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2972 bool clamp_y_axis = false;
2973 vector<int32_t> height_list(512) ;
2974 vector<int32_t>::iterator j;
2976 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2978 drag_info.want_move_threshold = false; // don't copy again
2980 /* duplicate the region(s) */
2982 vector<RegionView*> new_regionviews;
2984 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2991 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2992 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2995 nrv = new AudioRegionView (*arv);
2997 nrv = new MidiRegionView (*mrv);
3002 nrv->get_canvas_group()->show ();
3004 new_regionviews.push_back (nrv);
3007 if (new_regionviews.empty()) {
3011 /* reset selection to new regionviews */
3013 selection->set (new_regionviews);
3015 /* reset drag_info data to reflect the fact that we are dragging the copies */
3017 drag_info.data = new_regionviews.front();
3019 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3022 /* Which trackview is this ? */
3024 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3025 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3027 /* The region motion is only processed if the pointer is over
3031 if (!tv || !tv->is_track()) {
3032 /* To make sure we hide the verbose canvas cursor when the mouse is
3033 not held over a track.
3035 hide_verbose_canvas_cursor ();
3039 original_pointer_order = drag_info.last_trackview->order;
3041 /************************************************************
3043 ************************************************************/
3045 if (drag_info.brushing) {
3046 clamp_y_axis = true;
3051 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
3053 int32_t children = 0, numtracks = 0;
3054 // XXX hard coding track limit, oh my, so very very bad
3055 bitset <1024> tracks (0x00);
3056 /* get a bitmask representing the visible tracks */
3058 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3059 TimeAxisView *tracklist_timeview;
3060 tracklist_timeview = (*i);
3061 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
3062 TimeAxisView::Children children_list;
3064 /* zeroes are audio tracks. ones are other types. */
3066 if (!rtv2->hidden()) {
3068 if (visible_y_high < rtv2->order) {
3069 visible_y_high = rtv2->order;
3071 if (visible_y_low > rtv2->order) {
3072 visible_y_low = rtv2->order;
3075 if (!rtv2->is_track()) {
3076 tracks = tracks |= (0x01 << rtv2->order);
3079 height_list[rtv2->order] = (*i)->height;
3081 if ((children_list = rtv2->get_child_list()).size() > 0) {
3082 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3083 tracks = tracks |= (0x01 << (rtv2->order + children));
3084 height_list[rtv2->order + children] = (*j)->height;
3092 /* find the actual span according to the canvas */
3094 canvas_pointer_y_span = pointer_y_span;
3095 if (drag_info.last_trackview->order >= tv->order) {
3097 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
3098 if (height_list[y] == 0 ) {
3099 canvas_pointer_y_span--;
3104 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
3105 if ( height_list[y] == 0 ) {
3106 canvas_pointer_y_span++;
3111 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3112 RegionView* rv2 = (*i);
3113 double ix1, ix2, iy1, iy2;
3116 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3117 rv2->get_canvas_group()->i2w (ix1, iy1);
3118 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3119 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3121 if (rtv2->order != original_pointer_order) {
3122 /* this isn't the pointer track */
3124 if (canvas_pointer_y_span > 0) {
3126 /* moving up the canvas */
3127 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
3129 int32_t visible_tracks = 0;
3130 while (visible_tracks < canvas_pointer_y_span ) {
3133 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3134 /* we're passing through a hidden track */
3139 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3140 clamp_y_axis = true;
3144 clamp_y_axis = true;
3147 } else if (canvas_pointer_y_span < 0) {
3149 /*moving down the canvas*/
3151 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3154 int32_t visible_tracks = 0;
3156 while (visible_tracks > canvas_pointer_y_span ) {
3159 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3163 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3164 clamp_y_axis = true;
3169 clamp_y_axis = true;
3175 /* this is the pointer's track */
3176 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3177 clamp_y_axis = true;
3178 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3179 clamp_y_axis = true;
3187 } else if (drag_info.last_trackview == tv) {
3188 clamp_y_axis = true;
3192 if (!clamp_y_axis) {
3193 drag_info.last_trackview = tv;
3196 /************************************************************
3198 ************************************************************/
3200 /* compute the amount of pointer motion in frames, and where
3201 the region would be if we moved it by that much.
3204 if (drag_info.move_threshold_passed) {
3206 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3208 nframes_t sync_frame;
3209 nframes_t sync_offset;
3212 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3214 sync_offset = rv->region()->sync_offset (sync_dir);
3215 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3217 /* we snap if the snap modifier is not enabled.
3220 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3221 snap_to (sync_frame);
3224 if (sync_frame - sync_offset <= sync_frame) {
3225 pending_region_position = sync_frame - (sync_dir*sync_offset);
3227 pending_region_position = 0;
3231 pending_region_position = 0;
3234 if (pending_region_position > max_frames - rv->region()->length()) {
3235 pending_region_position = drag_info.last_frame_position;
3238 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3240 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3242 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3243 to make it appear at the new location.
3246 if (pending_region_position > drag_info.last_frame_position) {
3247 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3249 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3252 drag_info.last_frame_position = pending_region_position;
3259 /* threshold not passed */
3264 /*************************************************************
3266 ************************************************************/
3268 if (x_delta == 0 && (pointer_y_span == 0)) {
3269 /* haven't reached next snap point, and we're not switching
3270 trackviews. nothing to do.
3277 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3279 RegionView* rv2 = (*i);
3281 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3283 double ix1, ix2, iy1, iy2;
3284 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3285 rv2->get_canvas_group()->i2w (ix1, iy1);
3294 /*************************************************************
3296 ************************************************************/
3300 if (drag_info.first_move) {
3301 if (drag_info.move_threshold_passed) {
3312 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3313 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3315 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3317 RegionView* rv = (*i);
3318 double ix1, ix2, iy1, iy2;
3319 int32_t temp_pointer_y_span = pointer_y_span;
3321 /* get item BBox, which will be relative to parent. so we have
3322 to query on a child, then convert to world coordinates using
3326 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3327 rv->get_canvas_group()->i2w (ix1, iy1);
3328 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3329 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3330 RouteTimeAxisView* temp_rtv;
3332 if ((pointer_y_span != 0) && !clamp_y_axis) {
3335 for (j = height_list.begin(); j!= height_list.end(); j++) {
3336 if (x == canvas_rtv->order) {
3337 /* we found the track the region is on */
3338 if (x != original_pointer_order) {
3339 /*this isn't from the same track we're dragging from */
3340 temp_pointer_y_span = canvas_pointer_y_span;
3342 while (temp_pointer_y_span > 0) {
3343 /* we're moving up canvas-wise,
3344 so we need to find the next track height
3346 if (j != height_list.begin()) {
3349 if (x != original_pointer_order) {
3350 /* we're not from the dragged track, so ignore hidden tracks. */
3352 temp_pointer_y_span++;
3356 temp_pointer_y_span--;
3358 while (temp_pointer_y_span < 0) {
3360 if (x != original_pointer_order) {
3362 temp_pointer_y_span--;
3366 if (j != height_list.end()) {
3369 temp_pointer_y_span++;
3371 /* find out where we'll be when we move and set height accordingly */
3373 tvp2 = trackview_by_y_position (iy1 + y_delta);
3374 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3375 rv->set_y_position_and_height (0, temp_rtv->height);
3377 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3378 personally, i think this can confuse things, but never mind.
3381 //const GdkColor& col (temp_rtv->view->get_region_color());
3382 //rv->set_color (const_cast<GdkColor&>(col));
3389 /* prevent the regionview from being moved to before
3390 the zero position on the canvas.
3395 if (-x_delta > ix1) {
3398 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3399 x_delta = max_frames - rv->region()->last_frame();
3403 if (drag_info.first_move) {
3405 /* hide any dependent views */
3407 rv->get_time_axis_view().hide_dependent_views (*rv);
3409 /* this is subtle. raising the regionview itself won't help,
3410 because raise_to_top() just puts the item on the top of
3411 its parent's stack. so, we need to put the trackview canvas_display group
3412 on the top, since its parent is the whole canvas.
3415 rv->get_canvas_group()->raise_to_top();
3416 rv->get_time_axis_view().canvas_display->raise_to_top();
3417 cursor_group->raise_to_top();
3419 rv->fake_set_opaque (true);
3422 if (drag_info.brushing) {
3423 mouse_brush_insert_region (rv, pending_region_position);
3425 rv->move (x_delta, y_delta);
3428 } /* foreach region */
3432 if (drag_info.first_move && drag_info.move_threshold_passed) {
3433 cursor_group->raise_to_top();
3434 drag_info.first_move = false;
3437 if (x_delta != 0 && !drag_info.brushing) {
3438 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3443 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3446 RegionView* rvdi = reinterpret_cast<RegionView *> (drag_info.data);
3447 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3448 bool nocommit = true;
3450 RouteTimeAxisView* rtv;
3451 bool regionview_y_movement;
3452 bool regionview_x_movement;
3453 vector<RegionView*> copies;
3454 list <boost::shared_ptr<Playlist > > used_playlists;
3455 list <sigc::connection > used_connections;
3456 bool preserve_selection = false;
3458 /* first_move is set to false if the regionview has been moved in the
3462 if (drag_info.first_move) {
3469 /* The regionview has been moved at some stage during the grab so we need
3470 to account for any mouse movement between this event and the last one.
3473 region_drag_motion_callback (item, event);
3475 if (drag_info.brushing) {
3476 /* all changes were made during motion event handlers */
3478 if (drag_info.copy) {
3479 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3480 copies.push_back (*i);
3487 /* adjust for track speed */
3490 rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3491 if (rtv && rtv->get_diskstream()) {
3492 speed = rtv->get_diskstream()->speed();
3495 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rvdi->region()->position()/speed));
3496 regionview_y_movement = (drag_info.last_trackview != &rvdi->get_time_axis_view());
3498 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3499 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3503 if (drag_info.copy) {
3504 if (drag_info.x_constrained) {
3505 op_string = _("fixed time region copy");
3507 op_string = _("region copy");
3510 if (drag_info.x_constrained) {
3511 op_string = _("fixed time region drag");
3513 op_string = _("region drag");
3517 begin_reversible_command (op_string);
3519 if (regionview_y_movement) {
3521 /* moved to a different audio track. */
3523 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3525 RegionView* rv = (*i);
3527 double ix1, ix2, iy1, iy2;
3529 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3530 rv->get_canvas_group()->i2w (ix1, iy1);
3532 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(trackview_by_y_position (iy1));
3534 boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3536 if (! to_playlist->frozen()) {
3538 we haven't seen this playlist before.
3539 we want to freeze it because we don't want to relayer per-region.
3540 its much better to do that just once if the playlist is large.
3544 connect so the selection is changed when the new regionview finally appears (after thaw).
3545 keep track of it so we can disconnect later.
3548 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view));
3549 used_connections.push_back (c);
3552 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3554 /* remember used playlists so we can thaw them later */
3555 used_playlists.push_back(to_playlist);
3556 to_playlist->freeze();
3559 where = (nframes_t) (unit_to_frame (ix1) * speed);
3560 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3562 if (!drag_info.copy) {
3565 /* the region that used to be in the old playlist is not
3566 moved to the new one - we make a copy of it. as a result,
3567 any existing editor for the region should no longer be
3571 RouteTimeAxisView* from_playlist_rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_trackview());
3572 boost::shared_ptr<Playlist> from_playlist = from_playlist_rtv->playlist();
3574 if (! from_playlist->frozen()) {
3575 from_playlist->freeze();
3576 used_playlists.push_back(from_playlist);
3578 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view));
3579 used_connections.push_back (c);
3581 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3584 rv->hide_region_editor();
3585 rv->fake_set_opaque (false);
3587 from_playlist->remove_region ((rv->region()));
3591 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3593 copies.push_back (rv);
3596 latest_regionview = 0;
3598 to_playlist->add_region (new_region, where);
3600 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3601 was selected in all of them, then removing it from the playlist will have removed all
3602 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3603 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3604 corresponding regionview, and the selection is now empty).
3606 this could have invalidated any and all iterators into the region selection.
3608 the heuristic we use here is: if the region selection is empty, break out of the loop
3609 here. if the region selection is not empty, then restart the loop because we know that
3610 we must have removed at least the region(view) we've just been working on as well as any
3611 that we processed on previous iterations.
3613 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3614 we can just iterate.
3618 if (drag_info.copy) {
3621 if (selection->regions.empty()) {
3625 XXX see above .. but we just froze the playlists.. we have to keep iterating, right?
3628 //i = selection->regions.by_layer().begin();
3636 /* motion within a single track */
3638 list<RegionView*> regions = selection->regions.by_layer();
3640 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3642 RegionView* rv = (*i);
3643 boost::shared_ptr<Playlist> to_playlist = (*i)->region()->playlist();
3644 RouteTimeAxisView* from_rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3646 if (!rv->region()->can_move()) {
3650 if (regionview_x_movement) {
3651 double ownspeed = 1.0;
3653 if (from_rtv && from_rtv->get_diskstream()) {
3654 ownspeed = from_rtv->get_diskstream()->speed();
3657 /* base the new region position on the current position of the regionview.*/
3659 double ix1, ix2, iy1, iy2;
3661 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3662 rv->get_canvas_group()->i2w (ix1, iy1);
3663 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3667 where = rv->region()->position();
3670 if (! to_playlist->frozen()) {
3671 sigc::connection c = from_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view));
3672 used_connections.push_back (c);
3675 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3677 used_playlists.push_back(to_playlist);
3678 to_playlist->freeze();
3681 if (drag_info.copy) {
3683 boost::shared_ptr<Region> newregion;
3684 boost::shared_ptr<Region> ar;
3685 boost::shared_ptr<Region> mr;
3687 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3688 newregion = RegionFactory::create (ar);
3689 } else if ((mr = boost::dynamic_pointer_cast<MidiRegion>(rv->region())) != 0) {
3690 newregion = RegionFactory::create (mr);
3695 to_playlist->add_region (newregion, (nframes_t) (where * from_rtv->get_diskstream()->speed()));
3697 /* if the original region was locked, we don't care for the new one */
3699 newregion->set_locked (false);
3700 copies.push_back (rv);
3704 /* just change the model */
3706 rv->region()->set_position (where, (void*) this);
3707 preserve_selection = true;
3714 if (! preserve_selection) {
3715 //selection->clear_regions();
3717 while (used_playlists.size() > 0) {
3719 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
3722 if (used_connections.size()) {
3723 sigc::connection c = used_connections.front();
3725 used_connections.pop_front();
3729 session->add_command (new MementoCommand<Playlist>(*(*i), 0, &(*i)->get_state()));
3730 used_playlists.pop_front();
3736 commit_reversible_command ();
3739 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3746 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3748 if (drag_info.move_threshold_passed) {
3749 if (drag_info.first_move) {
3750 // TODO: create region-create-drag region view here
3751 drag_info.first_move = false;
3754 // TODO: resize region-create-drag region view here
3759 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3761 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.last_trackview);
3765 const boost::shared_ptr<MidiDiskstream> diskstream =
3766 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
3769 warning << "Cannot create non-MIDI region" << endl;
3773 if (drag_info.first_move) {
3774 begin_reversible_command (_("create region"));
3775 XMLNode &before = mtv->playlist()->get_state();
3777 nframes_t start = drag_info.grab_frame;
3778 snap_to (start, -1);
3779 const Meter& m = session->tempo_map().meter_at(start);
3780 const Tempo& t = session->tempo_map().tempo_at(start);
3781 double length = floor (m.frames_per_bar(t, session->frame_rate()));
3783 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
3785 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
3786 (RegionFactory::create(src, 0, (nframes_t) length,
3787 PBD::basename_nosuffix(src->name()))), start);
3788 XMLNode &after = mtv->playlist()->get_state();
3789 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
3790 commit_reversible_command();
3793 create_region_drag_motion_callback (item, event);
3794 // TODO: create region-create-drag region here
3799 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3801 /* Either add to or set the set the region selection, unless
3802 this is an alignment click (control used)
3805 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3806 TimeAxisView* tv = &rv.get_time_axis_view();
3807 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3809 if (rtv && rtv->is_track()) {
3810 speed = rtv->get_diskstream()->speed();
3813 nframes64_t where = get_preferred_edit_position();
3817 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3819 align_region (rv.region(), SyncPoint, (nframes_t) (where * speed));
3821 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3823 align_region (rv.region(), End, (nframes_t) (where * speed));
3827 align_region (rv.region(), Start, (nframes_t) (where * speed));
3834 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3840 nframes_t frame_rate;
3847 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3848 case AudioClock::BBT:
3849 session->bbt_time (frame, bbt);
3850 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3853 case AudioClock::SMPTE:
3854 session->smpte_time (frame, smpte);
3855 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3858 case AudioClock::MinSec:
3859 /* XXX this is copied from show_verbose_duration_cursor() */
3860 frame_rate = session->frame_rate();
3861 hours = frame / (frame_rate * 3600);
3862 frame = frame % (frame_rate * 3600);
3863 mins = frame / (frame_rate * 60);
3864 frame = frame % (frame_rate * 60);
3865 secs = (float) frame / (float) frame_rate;
3866 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3870 snprintf (buf, sizeof(buf), "%u", frame);
3874 if (xpos >= 0 && ypos >=0) {
3875 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3878 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3880 show_verbose_canvas_cursor ();
3884 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3891 nframes_t distance, frame_rate;
3893 Meter meter_at_start(session->tempo_map().meter_at(start));
3899 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3900 case AudioClock::BBT:
3901 session->bbt_time (start, sbbt);
3902 session->bbt_time (end, ebbt);
3905 /* XXX this computation won't work well if the
3906 user makes a selection that spans any meter changes.
3909 ebbt.bars -= sbbt.bars;
3910 if (ebbt.beats >= sbbt.beats) {
3911 ebbt.beats -= sbbt.beats;
3914 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3916 if (ebbt.ticks >= sbbt.ticks) {
3917 ebbt.ticks -= sbbt.ticks;
3920 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3923 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3926 case AudioClock::SMPTE:
3927 session->smpte_duration (end - start, smpte);
3928 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3931 case AudioClock::MinSec:
3932 /* XXX this stuff should be elsewhere.. */
3933 distance = end - start;
3934 frame_rate = session->frame_rate();
3935 hours = distance / (frame_rate * 3600);
3936 distance = distance % (frame_rate * 3600);
3937 mins = distance / (frame_rate * 60);
3938 distance = distance % (frame_rate * 60);
3939 secs = (float) distance / (float) frame_rate;
3940 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3944 snprintf (buf, sizeof(buf), "%u", end - start);
3948 if (xpos >= 0 && ypos >=0) {
3949 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3952 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3954 show_verbose_canvas_cursor ();
3958 Editor::collect_new_region_view (RegionView* rv)
3960 latest_regionview = rv;
3964 Editor::collect_and_select_new_region_view (RegionView* rv)
3967 latest_regionview = rv;
3971 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3973 if (clicked_regionview == 0) {
3977 /* lets try to create new Region for the selection */
3979 vector<boost::shared_ptr<AudioRegion> > new_regions;
3980 create_region_from_selection (new_regions);
3982 if (new_regions.empty()) {
3986 /* XXX fix me one day to use all new regions */
3988 boost::shared_ptr<Region> region (new_regions.front());
3990 /* add it to the current stream/playlist.
3992 tricky: the streamview for the track will add a new regionview. we will
3993 catch the signal it sends when it creates the regionview to
3994 set the regionview we want to then drag.
3997 latest_regionview = 0;
3998 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4000 /* A selection grab currently creates two undo/redo operations, one for
4001 creating the new region and another for moving it.
4004 begin_reversible_command (_("selection grab"));
4006 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
4008 XMLNode *before = &(playlist->get_state());
4009 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
4010 XMLNode *after = &(playlist->get_state());
4011 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4013 commit_reversible_command ();
4017 if (latest_regionview == 0) {
4018 /* something went wrong */
4022 /* we need to deselect all other regionviews, and select this one
4023 i'm ignoring undo stuff, because the region creation will take care of it */
4024 //selection->set (latest_regionview);
4026 drag_info.item = latest_regionview->get_canvas_group();
4027 drag_info.data = latest_regionview;
4028 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4029 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4033 drag_info.last_trackview = clicked_axisview;
4034 drag_info.last_frame_position = latest_regionview->region()->position();
4035 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4037 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4041 Editor::cancel_selection ()
4043 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4044 (*i)->hide_selection ();
4046 begin_reversible_command (_("cancel selection"));
4047 selection->clear ();
4048 clicked_selection = 0;
4049 commit_reversible_command ();
4053 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4055 nframes_t start = 0;
4062 drag_info.item = item;
4063 drag_info.motion_callback = &Editor::drag_selection;
4064 drag_info.finished_callback = &Editor::end_selection_op;
4069 case CreateSelection:
4070 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4071 drag_info.copy = true;
4073 drag_info.copy = false;
4075 start_grab (event, selector_cursor);
4078 case SelectionStartTrim:
4079 if (clicked_axisview) {
4080 clicked_axisview->order_selection_trims (item, true);
4082 start_grab (event, trimmer_cursor);
4083 start = selection->time[clicked_selection].start;
4084 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4087 case SelectionEndTrim:
4088 if (clicked_axisview) {
4089 clicked_axisview->order_selection_trims (item, false);
4091 start_grab (event, trimmer_cursor);
4092 end = selection->time[clicked_selection].end;
4093 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4097 start = selection->time[clicked_selection].start;
4099 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4103 if (selection_op == SelectionMove) {
4104 show_verbose_time_cursor(start, 10);
4106 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4111 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4113 nframes_t start = 0;
4116 nframes_t pending_position;
4118 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4119 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4121 pending_position = 0;
4124 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4125 snap_to (pending_position);
4128 /* only alter selection if the current frame is
4129 different from the last frame position (adjusted)
4132 if (pending_position == drag_info.last_pointer_frame) return;
4134 switch (selection_op) {
4135 case CreateSelection:
4137 if (drag_info.first_move) {
4138 snap_to (drag_info.grab_frame);
4141 if (pending_position < drag_info.grab_frame) {
4142 start = pending_position;
4143 end = drag_info.grab_frame;
4145 end = pending_position;
4146 start = drag_info.grab_frame;
4149 /* first drag: Either add to the selection
4150 or create a new selection->
4153 if (drag_info.first_move) {
4155 begin_reversible_command (_("range selection"));
4157 if (drag_info.copy) {
4158 /* adding to the selection */
4159 clicked_selection = selection->add (start, end);
4160 drag_info.copy = false;
4162 /* new selection-> */
4163 clicked_selection = selection->set (clicked_axisview, start, end);
4168 case SelectionStartTrim:
4170 if (drag_info.first_move) {
4171 begin_reversible_command (_("trim selection start"));
4174 start = selection->time[clicked_selection].start;
4175 end = selection->time[clicked_selection].end;
4177 if (pending_position > end) {
4180 start = pending_position;
4184 case SelectionEndTrim:
4186 if (drag_info.first_move) {
4187 begin_reversible_command (_("trim selection end"));
4190 start = selection->time[clicked_selection].start;
4191 end = selection->time[clicked_selection].end;
4193 if (pending_position < start) {
4196 end = pending_position;
4203 if (drag_info.first_move) {
4204 begin_reversible_command (_("move selection"));
4207 start = selection->time[clicked_selection].start;
4208 end = selection->time[clicked_selection].end;
4210 length = end - start;
4212 start = pending_position;
4215 end = start + length;
4220 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4221 start_canvas_autoscroll (1);
4225 selection->replace (clicked_selection, start, end);
4228 drag_info.last_pointer_frame = pending_position;
4229 drag_info.first_move = false;
4231 if (selection_op == SelectionMove) {
4232 show_verbose_time_cursor(start, 10);
4234 show_verbose_time_cursor(pending_position, 10);
4239 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4241 if (!drag_info.first_move) {
4242 drag_selection (item, event);
4243 /* XXX this is not object-oriented programming at all. ick */
4244 if (selection->time.consolidate()) {
4245 selection->TimeChanged ();
4247 commit_reversible_command ();
4249 /* just a click, no pointer movement.*/
4251 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4253 selection->clear_time();
4258 /* XXX what happens if its a music selection? */
4259 session->set_audio_range (selection->time);
4260 stop_canvas_autoscroll ();
4264 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4267 TimeAxisView* tvp = clicked_axisview;
4268 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4270 if (tv && tv->is_track()) {
4271 speed = tv->get_diskstream()->speed();
4274 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4275 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4276 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4278 //drag_info.item = clicked_regionview->get_name_highlight();
4279 drag_info.item = item;
4280 drag_info.motion_callback = &Editor::trim_motion_callback;
4281 drag_info.finished_callback = &Editor::trim_finished_callback;
4283 start_grab (event, trimmer_cursor);
4285 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4286 trim_op = ContentsTrim;
4288 /* These will get overridden for a point trim.*/
4289 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4290 /* closer to start */
4291 trim_op = StartTrim;
4292 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4300 show_verbose_time_cursor(region_start, 10);
4303 show_verbose_time_cursor(region_end, 10);
4306 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4312 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4314 RegionView* rv = clicked_regionview;
4315 nframes_t frame_delta = 0;
4316 bool left_direction;
4317 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4319 /* snap modifier works differently here..
4320 its' current state has to be passed to the
4321 various trim functions in order to work properly
4325 TimeAxisView* tvp = clicked_axisview;
4326 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4327 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4329 if (tv && tv->is_track()) {
4330 speed = tv->get_diskstream()->speed();
4333 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4334 left_direction = true;
4336 left_direction = false;
4340 snap_to (drag_info.current_pointer_frame);
4343 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4347 if (drag_info.first_move) {
4353 trim_type = "Region start trim";
4356 trim_type = "Region end trim";
4359 trim_type = "Region content trim";
4363 begin_reversible_command (trim_type);
4365 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4366 (*i)->fake_set_opaque(false);
4367 (*i)->region()->freeze ();
4369 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4371 arv->temporarily_hide_envelope ();
4373 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4374 insert_result = motion_frozen_playlists.insert (pl);
4375 if (insert_result.second) {
4376 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4382 if (left_direction) {
4383 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4385 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4390 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4393 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4394 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4400 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4403 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4404 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4411 bool swap_direction = false;
4413 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4414 swap_direction = true;
4417 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4418 i != selection->regions.by_layer().end(); ++i)
4420 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4428 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4431 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4434 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4438 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4439 drag_info.first_move = false;
4443 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4445 boost::shared_ptr<Region> region (rv.region());
4447 if (region->locked()) {
4451 nframes_t new_bound;
4454 TimeAxisView* tvp = clicked_axisview;
4455 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4457 if (tv && tv->is_track()) {
4458 speed = tv->get_diskstream()->speed();
4461 if (left_direction) {
4462 if (swap_direction) {
4463 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4465 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4468 if (swap_direction) {
4469 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4471 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4476 snap_to (new_bound);
4478 region->trim_start ((nframes_t) (new_bound * speed), this);
4479 rv.region_changed (StartChanged);
4483 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4485 boost::shared_ptr<Region> region (rv.region());
4487 if (region->locked()) {
4491 nframes_t new_bound;
4494 TimeAxisView* tvp = clicked_axisview;
4495 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4497 if (tv && tv->is_track()) {
4498 speed = tv->get_diskstream()->speed();
4501 if (left_direction) {
4502 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4504 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4508 snap_to (new_bound, (left_direction ? 0 : 1));
4511 region->trim_front ((nframes_t) (new_bound * speed), this);
4513 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4517 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4519 boost::shared_ptr<Region> region (rv.region());
4521 if (region->locked()) {
4525 nframes_t new_bound;
4528 TimeAxisView* tvp = clicked_axisview;
4529 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4531 if (tv && tv->is_track()) {
4532 speed = tv->get_diskstream()->speed();
4535 if (left_direction) {
4536 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4538 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4542 snap_to (new_bound);
4544 region->trim_end ((nframes_t) (new_bound * speed), this);
4545 rv.region_changed (LengthChanged);
4549 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4551 if (!drag_info.first_move) {
4552 trim_motion_callback (item, event);
4554 if (!clicked_regionview->get_selected()) {
4555 thaw_region_after_trim (*clicked_regionview);
4558 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4559 i != selection->regions.by_layer().end(); ++i)
4561 thaw_region_after_trim (**i);
4562 (*i)->fake_set_opaque (true);
4566 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4568 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4571 motion_frozen_playlists.clear ();
4573 commit_reversible_command();
4575 /* no mouse movement */
4581 Editor::point_trim (GdkEvent* event)
4583 RegionView* rv = clicked_regionview;
4584 nframes_t new_bound = drag_info.current_pointer_frame;
4586 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4587 snap_to (new_bound);
4590 /* Choose action dependant on which button was pressed */
4591 switch (event->button.button) {
4593 trim_op = StartTrim;
4594 begin_reversible_command (_("Start point trim"));
4596 if (rv->get_selected()) {
4598 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4599 i != selection->regions.by_layer().end(); ++i)
4601 if (!(*i)->region()->locked()) {
4602 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4603 XMLNode &before = pl->get_state();
4604 (*i)->region()->trim_front (new_bound, this);
4605 XMLNode &after = pl->get_state();
4606 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4612 if (!rv->region()->locked()) {
4613 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4614 XMLNode &before = pl->get_state();
4615 rv->region()->trim_front (new_bound, this);
4616 XMLNode &after = pl->get_state();
4617 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4621 commit_reversible_command();
4626 begin_reversible_command (_("End point trim"));
4628 if (rv->get_selected()) {
4630 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4632 if (!(*i)->region()->locked()) {
4633 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4634 XMLNode &before = pl->get_state();
4635 (*i)->region()->trim_end (new_bound, this);
4636 XMLNode &after = pl->get_state();
4637 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4643 if (!rv->region()->locked()) {
4644 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4645 XMLNode &before = pl->get_state();
4646 rv->region()->trim_end (new_bound, this);
4647 XMLNode &after = pl->get_state();
4648 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4652 commit_reversible_command();
4661 Editor::thaw_region_after_trim (RegionView& rv)
4663 boost::shared_ptr<Region> region (rv.region());
4665 if (region->locked()) {
4669 region->thaw (_("trimmed region"));
4670 XMLNode &after = region->playlist()->get_state();
4671 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4673 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4675 arv->unhide_envelope ();
4679 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4684 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4685 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4689 Location* location = find_location_from_marker (marker, is_start);
4690 location->set_hidden (true, this);
4695 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4701 drag_info.item = item;
4702 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4703 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4705 range_marker_op = op;
4707 if (!temp_location) {
4708 temp_location = new Location;
4712 case CreateRangeMarker:
4713 case CreateTransportMarker:
4715 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4716 drag_info.copy = true;
4718 drag_info.copy = false;
4720 start_grab (event, selector_cursor);
4724 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4729 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4731 nframes_t start = 0;
4733 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4735 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4736 snap_to (drag_info.current_pointer_frame);
4739 /* only alter selection if the current frame is
4740 different from the last frame position.
4743 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4745 switch (range_marker_op) {
4746 case CreateRangeMarker:
4747 case CreateTransportMarker:
4748 if (drag_info.first_move) {
4749 snap_to (drag_info.grab_frame);
4752 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4753 start = drag_info.current_pointer_frame;
4754 end = drag_info.grab_frame;
4756 end = drag_info.current_pointer_frame;
4757 start = drag_info.grab_frame;
4760 /* first drag: Either add to the selection
4761 or create a new selection.
4764 if (drag_info.first_move) {
4766 temp_location->set (start, end);
4770 update_marker_drag_item (temp_location);
4771 range_marker_drag_rect->show();
4772 range_marker_drag_rect->raise_to_top();
4778 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4779 start_canvas_autoscroll (1);
4783 temp_location->set (start, end);
4785 double x1 = frame_to_pixel (start);
4786 double x2 = frame_to_pixel (end);
4787 crect->property_x1() = x1;
4788 crect->property_x2() = x2;
4790 update_marker_drag_item (temp_location);
4793 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4794 drag_info.first_move = false;
4796 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4801 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4803 Location * newloc = 0;
4806 if (!drag_info.first_move) {
4807 drag_range_markerbar_op (item, event);
4809 switch (range_marker_op) {
4810 case CreateRangeMarker:
4812 begin_reversible_command (_("new range marker"));
4813 XMLNode &before = session->locations()->get_state();
4814 session->locations()->next_available_name(rangename,"unnamed");
4815 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4816 session->locations()->add (newloc, true);
4817 XMLNode &after = session->locations()->get_state();
4818 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4819 commit_reversible_command ();
4821 range_bar_drag_rect->hide();
4822 range_marker_drag_rect->hide();
4826 case CreateTransportMarker:
4827 // popup menu to pick loop or punch
4828 new_transport_marker_context_menu (&event->button, item);
4833 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4835 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4840 start = session->locations()->first_mark_before (drag_info.grab_frame);
4841 end = session->locations()->first_mark_after (drag_info.grab_frame);
4843 if (end == max_frames) {
4844 end = session->current_end_frame ();
4848 start = session->current_start_frame ();
4851 switch (mouse_mode) {
4853 /* find the two markers on either side and then make the selection from it */
4854 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4858 /* find the two markers on either side of the click and make the range out of it */
4859 selection->set (0, start, end);
4868 stop_canvas_autoscroll ();
4874 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4876 drag_info.item = item;
4877 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4878 drag_info.finished_callback = &Editor::end_mouse_zoom;
4880 start_grab (event, zoom_cursor);
4882 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4886 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4891 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4892 snap_to (drag_info.current_pointer_frame);
4894 if (drag_info.first_move) {
4895 snap_to (drag_info.grab_frame);
4899 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4901 /* base start and end on initial click position */
4902 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4903 start = drag_info.current_pointer_frame;
4904 end = drag_info.grab_frame;
4906 end = drag_info.current_pointer_frame;
4907 start = drag_info.grab_frame;
4912 if (drag_info.first_move) {
4914 zoom_rect->raise_to_top();
4917 reposition_zoom_rect(start, end);
4919 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4920 drag_info.first_move = false;
4922 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4927 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4929 if (!drag_info.first_move) {
4930 drag_mouse_zoom (item, event);
4932 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4933 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4935 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4938 temporal_zoom_to_frame (false, drag_info.grab_frame);
4940 temporal_zoom_step (false);
4941 center_screen (drag_info.grab_frame);
4949 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4951 double x1 = frame_to_pixel (start);
4952 double x2 = frame_to_pixel (end);
4953 double y2 = full_canvas_height - 1.0;
4955 zoom_rect->property_x1() = x1;
4956 zoom_rect->property_y1() = 1.0;
4957 zoom_rect->property_x2() = x2;
4958 zoom_rect->property_y2() = y2;
4962 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4964 drag_info.item = item;
4965 drag_info.motion_callback = &Editor::drag_rubberband_select;
4966 drag_info.finished_callback = &Editor::end_rubberband_select;
4968 start_grab (event, cross_hair_cursor);
4970 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4974 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4981 /* use a bigger drag threshold than the default */
4983 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4987 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4988 if (drag_info.first_move) {
4989 snap_to (drag_info.grab_frame);
4991 snap_to (drag_info.current_pointer_frame);
4994 /* base start and end on initial click position */
4996 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4997 start = drag_info.current_pointer_frame;
4998 end = drag_info.grab_frame;
5000 end = drag_info.current_pointer_frame;
5001 start = drag_info.grab_frame;
5004 if (drag_info.current_pointer_y < drag_info.grab_y) {
5005 y1 = drag_info.current_pointer_y;
5006 y2 = drag_info.grab_y;
5008 y2 = drag_info.current_pointer_y;
5009 y1 = drag_info.grab_y;
5013 if (start != end || y1 != y2) {
5015 double x1 = frame_to_pixel (start);
5016 double x2 = frame_to_pixel (end);
5018 rubberband_rect->property_x1() = x1;
5019 rubberband_rect->property_y1() = y1;
5020 rubberband_rect->property_x2() = x2;
5021 rubberband_rect->property_y2() = y2;
5023 rubberband_rect->show();
5024 rubberband_rect->raise_to_top();
5026 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5027 drag_info.first_move = false;
5029 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5034 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5036 if (!drag_info.first_move) {
5038 drag_rubberband_select (item, event);
5041 if (drag_info.current_pointer_y < drag_info.grab_y) {
5042 y1 = drag_info.current_pointer_y;
5043 y2 = drag_info.grab_y;
5046 y2 = drag_info.current_pointer_y;
5047 y1 = drag_info.grab_y;
5051 Selection::Operation op = Keyboard::selection_type (event->button.state);
5054 begin_reversible_command (_("rubberband selection"));
5056 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5057 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5059 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5063 commit_reversible_command ();
5067 selection->clear_tracks();
5068 selection->clear_regions();
5069 selection->clear_points ();
5070 selection->clear_lines ();
5073 rubberband_rect->hide();
5078 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5080 using namespace Gtkmm2ext;
5082 ArdourPrompter prompter (false);
5084 prompter.set_prompt (_("Name for region:"));
5085 prompter.set_initial_text (clicked_regionview->region()->name());
5086 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5087 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5088 prompter.show_all ();
5089 switch (prompter.run ()) {
5090 case Gtk::RESPONSE_ACCEPT:
5092 prompter.get_result(str);
5094 clicked_regionview->region()->set_name (str);
5102 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5104 drag_info.item = item;
5105 drag_info.motion_callback = &Editor::time_fx_motion;
5106 drag_info.finished_callback = &Editor::end_time_fx;
5110 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5114 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5116 RegionView* rv = clicked_regionview;
5118 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5119 snap_to (drag_info.current_pointer_frame);
5122 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5126 if (drag_info.current_pointer_frame > rv->region()->position()) {
5127 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5130 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5131 drag_info.first_move = false;
5133 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5137 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5139 clicked_regionview->get_time_axis_view().hide_timestretch ();
5141 if (drag_info.first_move) {
5145 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5146 /* backwards drag of the left edge - not usable */
5150 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5151 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5153 begin_reversible_command (_("timestretch"));
5155 if (run_timestretch (selection->regions, percentage) == 0) {
5156 session->commit_reversible_command ();
5161 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5163 /* no brushing without a useful snap setting */
5166 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5169 switch (snap_mode) {
5171 return; /* can't work because it allows region to be placed anywhere */
5176 switch (snap_type) {
5179 case SnapToEditPoint:
5186 /* don't brush a copy over the original */
5188 if (pos == rv->region()->position()) {
5192 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5194 if (rtv == 0 || !rtv->is_track()) {
5198 boost::shared_ptr<Playlist> playlist = rtv->playlist();
5199 double speed = rtv->get_diskstream()->speed();
5201 XMLNode &before = playlist->get_state();
5202 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5203 XMLNode &after = playlist->get_state();
5204 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5206 // playlist is frozen, so we have to update manually
5208 playlist->Modified(); /* EMIT SIGNAL */
5212 Editor::track_height_step_timeout ()
5215 struct timeval delta;
5217 gettimeofday (&now, 0);
5218 timersub (&now, &last_track_height_step_timestamp, &delta);
5220 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5221 current_stepping_trackview = 0;