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 "pbd/enumwriter.h"
30 #include <gtkmm2ext/utils.h>
31 #include <gtkmm2ext/tearoff.h>
32 #include "pbd/memento_command.h"
33 #include "pbd/basename.h"
35 #include "ardour_ui.h"
38 #include "time_axis_view.h"
39 #include "audio_time_axis.h"
40 #include "audio_region_view.h"
41 #include "midi_region_view.h"
43 #include "streamview.h"
44 #include "region_gain_line.h"
45 #include "automation_time_axis.h"
46 #include "control_point.h"
49 #include "selection.h"
52 #include "rgb_macros.h"
53 #include "control_point_dialog.h"
54 #include "editor_drag.h"
56 #include "ardour/types.h"
57 #include "ardour/profile.h"
58 #include "ardour/route.h"
59 #include "ardour/audio_track.h"
60 #include "ardour/audio_diskstream.h"
61 #include "ardour/midi_diskstream.h"
62 #include "ardour/playlist.h"
63 #include "ardour/audioplaylist.h"
64 #include "ardour/audioregion.h"
65 #include "ardour/midi_region.h"
66 #include "ardour/dB.h"
67 #include "ardour/utils.h"
68 #include "ardour/region_factory.h"
69 #include "ardour/source_factory.h"
76 using namespace ARDOUR;
80 using namespace Editing;
83 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
87 Gdk::ModifierType mask;
88 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
89 Glib::RefPtr<const Gdk::Window> pointer_window;
95 pointer_window = canvas_window->get_pointer (x, y, mask);
97 if (pointer_window == track_canvas->get_bin_window()) {
100 in_track_canvas = true;
103 in_track_canvas = false;
108 event.type = GDK_BUTTON_RELEASE;
112 where = event_frame (&event, 0, 0);
117 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
131 switch (event->type) {
132 case GDK_BUTTON_RELEASE:
133 case GDK_BUTTON_PRESS:
134 case GDK_2BUTTON_PRESS:
135 case GDK_3BUTTON_PRESS:
137 *pcx = event->button.x;
138 *pcy = event->button.y;
139 _trackview_group->w2i(*pcx, *pcy);
141 case GDK_MOTION_NOTIFY:
143 *pcx = event->motion.x;
144 *pcy = event->motion.y;
145 _trackview_group->w2i(*pcx, *pcy);
147 case GDK_ENTER_NOTIFY:
148 case GDK_LEAVE_NOTIFY:
149 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
152 case GDK_KEY_RELEASE:
153 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
156 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
160 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
161 position is negative (as can be the case with motion events in particular),
162 the frame location is always positive.
165 return pixel_to_frame (*pcx);
169 Editor::which_grabber_cursor ()
171 Gdk::Cursor* c = grabber_cursor;
173 if (_internal_editing) {
174 switch (mouse_mode) {
176 c = midi_pencil_cursor;
184 c = midi_resize_cursor;
193 switch (_edit_point) {
195 c = grabber_edit_point_cursor;
206 Editor::set_canvas_cursor ()
208 if (_internal_editing) {
210 switch (mouse_mode) {
212 current_canvas_cursor = midi_pencil_cursor;
216 current_canvas_cursor = which_grabber_cursor();
220 current_canvas_cursor = midi_resize_cursor;
229 switch (mouse_mode) {
231 current_canvas_cursor = selector_cursor;
235 current_canvas_cursor = which_grabber_cursor();
239 current_canvas_cursor = cross_hair_cursor;
243 current_canvas_cursor = zoom_cursor;
247 current_canvas_cursor = time_fx_cursor; // just use playhead
251 current_canvas_cursor = speaker_cursor;
257 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
262 Editor::set_mouse_mode (MouseMode m, bool force)
268 if (!force && m == mouse_mode) {
272 Glib::RefPtr<Action> act;
276 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
280 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
284 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
288 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
292 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
296 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
302 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
305 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
306 tact->set_active (false);
307 tact->set_active (true);
311 Editor::mouse_mode_toggled (MouseMode m)
317 if (mouse_mode != MouseRange) {
319 /* in all modes except range, hide the range selection,
320 show the object (region) selection.
323 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
324 (*i)->set_should_show_selection (true);
326 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
327 (*i)->hide_selection ();
333 in range mode,show the range selection.
336 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
337 if ((*i)->get_selected()) {
338 (*i)->show_selection (selection->time);
343 set_canvas_cursor ();
347 Editor::step_mouse_mode (bool next)
349 switch (current_mouse_mode()) {
352 if (Profile->get_sae()) {
353 set_mouse_mode (MouseZoom);
355 set_mouse_mode (MouseRange);
358 set_mouse_mode (MouseTimeFX);
363 if (next) set_mouse_mode (MouseZoom);
364 else set_mouse_mode (MouseObject);
369 if (Profile->get_sae()) {
370 set_mouse_mode (MouseTimeFX);
372 set_mouse_mode (MouseGain);
375 if (Profile->get_sae()) {
376 set_mouse_mode (MouseObject);
378 set_mouse_mode (MouseRange);
384 if (next) set_mouse_mode (MouseTimeFX);
385 else set_mouse_mode (MouseZoom);
390 set_mouse_mode (MouseAudition);
392 if (Profile->get_sae()) {
393 set_mouse_mode (MouseZoom);
395 set_mouse_mode (MouseGain);
401 if (next) set_mouse_mode (MouseObject);
402 else set_mouse_mode (MouseTimeFX);
408 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
410 /* in object/audition/timefx/gain-automation mode,
411 any button press sets the selection if the object
412 can be selected. this is a bit of hack, because
413 we want to avoid this if the mouse operation is a
416 note: not dbl-click or triple-click
419 if (((mouse_mode != MouseObject) &&
420 (mouse_mode != MouseAudition || item_type != RegionItem) &&
421 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
422 (mouse_mode != MouseGain) &&
423 (mouse_mode != MouseRange)) ||
425 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
430 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
432 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
434 /* almost no selection action on modified button-2 or button-3 events */
436 if (item_type != RegionItem && event->button.button != 2) {
442 Selection::Operation op = Keyboard::selection_type (event->button.state);
443 bool press = (event->type == GDK_BUTTON_PRESS);
445 // begin_reversible_command (_("select on click"));
449 if (mouse_mode != MouseRange) {
450 set_selected_regionview_from_click (press, op, true);
451 } else if (event->type == GDK_BUTTON_PRESS) {
452 set_selected_track_as_side_effect ();
456 case RegionViewNameHighlight:
458 if (mouse_mode != MouseRange) {
459 set_selected_regionview_from_click (press, op, true);
460 } else if (event->type == GDK_BUTTON_PRESS) {
461 set_selected_track_as_side_effect ();
466 case FadeInHandleItem:
468 case FadeOutHandleItem:
470 if (mouse_mode != MouseRange) {
471 set_selected_regionview_from_click (press, op, true);
472 } else if (event->type == GDK_BUTTON_PRESS) {
473 set_selected_track_as_side_effect ();
477 case ControlPointItem:
478 set_selected_track_as_side_effect ();
479 if (mouse_mode != MouseRange) {
480 set_selected_control_point_from_click (op, false);
485 /* for context click or range selection, select track */
486 if (event->button.button == 3) {
487 set_selected_track_as_side_effect ();
488 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
489 set_selected_track_as_side_effect ();
493 case AutomationTrackItem:
494 set_selected_track_as_side_effect (true);
503 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
506 _drag->item()->ungrab (event->button.time);
511 /* single mouse clicks on any of these item types operate
512 independent of mouse mode, mostly because they are
513 not on the main track canvas or because we want
518 case PlayheadCursorItem:
520 _drag = new CursorDrag (this, item, true);
521 _drag->start_grab (event);
525 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
526 hide_marker (item, event);
529 _drag = new MarkerDrag (this, item);
530 _drag->start_grab (event);
534 case TempoMarkerItem:
536 _drag = new TempoMarkerDrag (
539 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
541 _drag->start_grab (event);
544 case MeterMarkerItem:
546 _drag = new MeterMarkerDrag (
549 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
551 _drag->start_grab (event);
557 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
559 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
560 _drag->start_grab (event);
566 case RangeMarkerBarItem:
568 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
569 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
571 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker);
573 _drag->start_grab (event);
577 case CdMarkerBarItem:
579 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
580 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
582 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker);
584 _drag->start_grab (event);
588 case TransportMarkerBarItem:
590 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
591 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
593 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker);
595 _drag->start_grab (event);
603 if (internal_editing()) {
607 _drag = new RegionCreateDrag (this, item, clicked_axisview);
608 _drag->start_grab (event);
611 /* Note: we don't get here if not in internal_editing() mode */
612 if (mouse_mode == MouseTimeFX) {
614 _drag = new NoteResizeDrag (this, item);
615 _drag->start_grab (event);
617 } else if (mouse_mode == MouseObject) {
619 _drag = new NoteDrag (this, item);
620 _drag->start_grab (event);
630 switch (mouse_mode) {
633 case StartSelectionTrimItem:
635 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim);
636 _drag->start_grab (event);
639 case EndSelectionTrimItem:
641 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim);
642 _drag->start_grab (event);
646 if (Keyboard::modifier_state_contains
647 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
648 // contains and not equals because I can't use alt as a modifier alone.
649 start_selection_grab (item, event);
650 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
651 /* grab selection for moving */
653 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionMove);
654 _drag->start_grab (event);
656 /* this was debated, but decided the more common action was to
657 make a new selection */
659 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
660 _drag->start_grab (event);
666 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
667 _drag->start_grab (event);
673 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
674 event->type == GDK_BUTTON_PRESS) {
677 _drag = new RubberbandSelectDrag (this, item);
678 _drag->start_grab (event);
680 } else if (event->type == GDK_BUTTON_PRESS) {
683 case FadeInHandleItem:
686 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
687 _drag = new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
688 _drag->start_grab (event);
692 case FadeOutHandleItem:
695 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
696 _drag = new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
697 _drag->start_grab (event);
702 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
703 start_region_copy_grab (item, event, clicked_regionview);
704 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
705 start_region_brush_grab (item, event, clicked_regionview);
707 start_region_grab (item, event, clicked_regionview);
711 case RegionViewNameHighlight:
714 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
715 _drag = new TrimDrag (this, item, clicked_regionview, s.by_layer());
716 _drag->start_grab (event);
723 /* rename happens on edit clicks */
725 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
726 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer());
727 _drag->start_grab (event);
732 case ControlPointItem:
734 _drag = new ControlPointDrag (this, item);
735 _drag->start_grab (event);
739 case AutomationLineItem:
741 _drag = new LineDrag (this, item);
742 _drag->start_grab (event);
747 case AutomationTrackItem:
749 _drag = new RubberbandSelectDrag (this, item);
750 _drag->start_grab (event);
754 case ImageFrameHandleStartItem:
755 imageframe_start_handle_op(item, event) ;
758 case ImageFrameHandleEndItem:
759 imageframe_end_handle_op(item, event) ;
762 case MarkerViewHandleStartItem:
763 markerview_item_start_handle_op(item, event) ;
766 case MarkerViewHandleEndItem:
767 markerview_item_end_handle_op(item, event) ;
771 start_markerview_grab(item, event) ;
774 start_imageframe_grab(item, event) ;
792 /* start a grab so that if we finish after moving
793 we can tell what happened.
796 _drag = new RegionGainDrag (this, item);
797 _drag->start_grab (event, current_canvas_cursor);
802 _drag = new LineDrag (this, item);
803 _drag->start_grab (event);
806 case ControlPointItem:
808 _drag = new ControlPointDrag (this, item);
809 _drag->start_grab (event);
820 case ControlPointItem:
822 _drag = new ControlPointDrag (this, item);
823 _drag->start_grab (event);
826 case AutomationLineItem:
828 _drag = new LineDrag (this, item);
829 _drag->start_grab (event);
833 // XXX need automation mode to identify which
835 // start_line_grab_from_regionview (item, event);
845 if (event->type == GDK_BUTTON_PRESS) {
847 _drag = new MouseZoomDrag (this, item);
848 _drag->start_grab (event);
855 if (item_type == RegionItem) {
857 _drag = new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer());
858 _drag->start_grab (event);
863 _drag = new ScrubDrag (this, item);
864 _drag->start_grab (event);
866 scrub_reverse_distance = 0;
867 last_scrub_x = event->button.x;
868 scrubbing_direction = 0;
869 track_canvas->get_window()->set_cursor (*transparent_cursor);
881 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
883 switch (mouse_mode) {
887 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
888 start_region_copy_grab (item, event, clicked_regionview);
890 start_region_grab (item, event, clicked_regionview);
894 case ControlPointItem:
896 _drag = new ControlPointDrag (this, item);
897 _drag->start_grab (event);
906 case RegionViewNameHighlight:
908 _drag = new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer());
909 _drag->start_grab (event);
915 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer());
916 _drag->start_grab (event);
927 /* relax till release */
933 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
934 temporal_zoom_session();
936 temporal_zoom_to_frame (true, event_frame(event));
949 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
951 if (event->type != GDK_BUTTON_PRESS) {
955 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
958 Glib::RefPtr<const Gdk::Window> pointer_window;
961 Gdk::ModifierType mask;
963 pointer_window = canvas_window->get_pointer (x, y, mask);
965 if (pointer_window == track_canvas->get_bin_window()) {
966 track_canvas->window_to_world (x, y, wx, wy);
967 allow_vertical_scroll = true;
969 allow_vertical_scroll = false;
973 track_canvas->grab_focus();
975 if (session && session->actively_recording()) {
979 button_selection (item, event, item_type);
982 (Keyboard::is_delete_event (&event->button) ||
983 Keyboard::is_context_menu_event (&event->button) ||
984 Keyboard::is_edit_event (&event->button))) {
986 /* handled by button release */
990 switch (event->button.button) {
992 return button_press_handler_1 (item, event, item_type);
996 return button_press_handler_2 (item, event, item_type);
1011 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1013 nframes64_t where = event_frame (event, 0, 0);
1014 AutomationTimeAxisView* atv = 0;
1016 /* no action if we're recording */
1018 if (session && session->actively_recording()) {
1022 /* first, see if we're finishing a drag ... */
1024 bool were_dragging = false;
1026 bool const r = _drag->end_grab (event);
1030 /* grab dragged, so do nothing else */
1034 were_dragging = true;
1037 button_selection (item, event, item_type);
1039 /* edit events get handled here */
1041 if (_drag == 0 && Keyboard::is_edit_event (&event->button)) {
1042 switch (item_type) {
1047 case TempoMarkerItem:
1048 edit_tempo_marker (item);
1051 case MeterMarkerItem:
1052 edit_meter_marker (item);
1055 case RegionViewName:
1056 if (clicked_regionview->name_active()) {
1057 return mouse_rename_region (item, event);
1061 case ControlPointItem:
1062 edit_control_point (item);
1071 /* context menu events get handled here */
1073 if (Keyboard::is_context_menu_event (&event->button)) {
1077 /* no matter which button pops up the context menu, tell the menu
1078 widget to use button 1 to drive menu selection.
1081 switch (item_type) {
1083 case FadeInHandleItem:
1085 case FadeOutHandleItem:
1086 popup_fade_context_menu (1, event->button.time, item, item_type);
1090 popup_track_context_menu (1, event->button.time, item_type, false, where);
1094 case RegionViewNameHighlight:
1095 case RegionViewName:
1096 popup_track_context_menu (1, event->button.time, item_type, false, where);
1100 popup_track_context_menu (1, event->button.time, item_type, true, where);
1103 case AutomationTrackItem:
1104 popup_track_context_menu (1, event->button.time, item_type, false, where);
1108 case RangeMarkerBarItem:
1109 case TransportMarkerBarItem:
1110 case CdMarkerBarItem:
1113 popup_ruler_menu (where, item_type);
1117 marker_context_menu (&event->button, item);
1120 case TempoMarkerItem:
1121 tm_marker_context_menu (&event->button, item);
1124 case MeterMarkerItem:
1125 tm_marker_context_menu (&event->button, item);
1128 case CrossfadeViewItem:
1129 popup_track_context_menu (1, event->button.time, item_type, false, where);
1133 case ImageFrameItem:
1134 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1136 case ImageFrameTimeAxisItem:
1137 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1139 case MarkerViewItem:
1140 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1142 case MarkerTimeAxisItem:
1143 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1155 /* delete events get handled here */
1157 if (_drag == 0 && Keyboard::is_delete_event (&event->button)) {
1159 switch (item_type) {
1160 case TempoMarkerItem:
1161 remove_tempo_marker (item);
1164 case MeterMarkerItem:
1165 remove_meter_marker (item);
1169 remove_marker (*item, event);
1173 if (mouse_mode == MouseObject) {
1174 remove_clicked_region ();
1178 case ControlPointItem:
1179 if (mouse_mode == MouseGain) {
1180 remove_gain_control_point (item, event);
1182 remove_control_point (item, event);
1192 switch (event->button.button) {
1195 switch (item_type) {
1196 /* see comments in button_press_handler */
1197 case PlayheadCursorItem:
1200 case AutomationLineItem:
1201 case StartSelectionTrimItem:
1202 case EndSelectionTrimItem:
1206 if (!_dragging_playhead) {
1207 snap_to_with_modifier (where, event, 0, true);
1208 mouse_add_new_marker (where);
1212 case CdMarkerBarItem:
1213 if (!_dragging_playhead) {
1214 // if we get here then a dragged range wasn't done
1215 snap_to_with_modifier (where, event, 0, true);
1216 mouse_add_new_marker (where, true);
1221 if (!_dragging_playhead) {
1222 snap_to_with_modifier (where, event);
1223 mouse_add_new_tempo_event (where);
1228 if (!_dragging_playhead) {
1229 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1238 switch (mouse_mode) {
1240 switch (item_type) {
1241 case AutomationTrackItem:
1242 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1244 atv->add_automation_event (item, event, where, event->button.y);
1255 // Gain only makes sense for audio regions
1257 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1261 switch (item_type) {
1263 /* check that we didn't drag before releasing, since
1264 its really annoying to create new control
1265 points when doing this.
1267 if (were_dragging) {
1268 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1273 case AutomationTrackItem:
1274 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1275 add_automation_event (item, event, where, event->button.y);
1284 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1285 if (scrubbing_direction == 0) {
1286 /* no drag, just a click */
1287 switch (item_type) {
1289 play_selected_region ();
1295 /* make sure we stop */
1296 session->request_transport_speed (0.0);
1310 switch (mouse_mode) {
1313 switch (item_type) {
1315 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1317 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1320 // Button2 click is unused
1333 // x_style_paste (where, 1.0);
1353 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1359 if (last_item_entered != item) {
1360 last_item_entered = item;
1361 last_item_entered_n = 0;
1364 switch (item_type) {
1365 case ControlPointItem:
1366 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1367 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1368 cp->set_visible (true);
1372 at_y = cp->get_y ();
1373 cp->item()->i2w (at_x, at_y);
1377 fraction = 1.0 - (cp->get_y() / cp->line().height());
1379 if (is_drawable() && dynamic_cast<ScrubDrag*> (_drag) == 0) {
1380 track_canvas->get_window()->set_cursor (*fader_cursor);
1383 last_item_entered_n++;
1384 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1385 if (last_item_entered_n < 10) {
1386 show_verbose_canvas_cursor ();
1392 if (mouse_mode == MouseGain) {
1393 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1395 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1396 if (is_drawable()) {
1397 track_canvas->get_window()->set_cursor (*fader_cursor);
1402 case AutomationLineItem:
1403 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1405 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1407 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1409 if (is_drawable()) {
1410 track_canvas->get_window()->set_cursor (*fader_cursor);
1415 case RegionViewNameHighlight:
1416 if (is_drawable() && mouse_mode == MouseObject) {
1417 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1421 case StartSelectionTrimItem:
1422 case EndSelectionTrimItem:
1425 case ImageFrameHandleStartItem:
1426 case ImageFrameHandleEndItem:
1427 case MarkerViewHandleStartItem:
1428 case MarkerViewHandleEndItem:
1431 if (is_drawable()) {
1432 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1436 case PlayheadCursorItem:
1437 if (is_drawable()) {
1438 switch (_edit_point) {
1440 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1443 track_canvas->get_window()->set_cursor (*grabber_cursor);
1449 case RegionViewName:
1451 /* when the name is not an active item, the entire name highlight is for trimming */
1453 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1454 if (mouse_mode == MouseObject && is_drawable()) {
1455 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1461 case AutomationTrackItem:
1462 if (is_drawable()) {
1463 Gdk::Cursor *cursor;
1464 switch (mouse_mode) {
1466 cursor = selector_cursor;
1469 cursor = zoom_cursor;
1472 cursor = cross_hair_cursor;
1476 track_canvas->get_window()->set_cursor (*cursor);
1478 AutomationTimeAxisView* atv;
1479 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1480 clear_entered_track = false;
1481 set_entered_track (atv);
1487 case RangeMarkerBarItem:
1488 case TransportMarkerBarItem:
1489 case CdMarkerBarItem:
1492 if (is_drawable()) {
1493 track_canvas->get_window()->set_cursor (*timebar_cursor);
1498 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1501 entered_marker = marker;
1502 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1504 case MeterMarkerItem:
1505 case TempoMarkerItem:
1506 if (is_drawable()) {
1507 track_canvas->get_window()->set_cursor (*timebar_cursor);
1510 case FadeInHandleItem:
1511 case FadeOutHandleItem:
1512 if (mouse_mode == MouseObject) {
1513 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1515 rect->property_fill_color_rgba() = 0;
1516 rect->property_outline_pixels() = 1;
1525 /* second pass to handle entered track status in a comprehensible way.
1528 switch (item_type) {
1530 case AutomationLineItem:
1531 case ControlPointItem:
1532 /* these do not affect the current entered track state */
1533 clear_entered_track = false;
1536 case AutomationTrackItem:
1537 /* handled above already */
1541 set_entered_track (0);
1549 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1558 switch (item_type) {
1559 case ControlPointItem:
1560 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1561 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1562 if (cp->line().npoints() > 1 && !cp->selected()) {
1563 cp->set_visible (false);
1567 if (is_drawable()) {
1568 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1571 hide_verbose_canvas_cursor ();
1574 case RegionViewNameHighlight:
1575 case StartSelectionTrimItem:
1576 case EndSelectionTrimItem:
1577 case PlayheadCursorItem:
1580 case ImageFrameHandleStartItem:
1581 case ImageFrameHandleEndItem:
1582 case MarkerViewHandleStartItem:
1583 case MarkerViewHandleEndItem:
1586 if (is_drawable()) {
1587 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1592 case AutomationLineItem:
1593 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1595 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1597 line->property_fill_color_rgba() = al->get_line_color();
1599 if (is_drawable()) {
1600 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1604 case RegionViewName:
1605 /* see enter_handler() for notes */
1606 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1607 if (is_drawable() && mouse_mode == MouseObject) {
1608 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1613 case RangeMarkerBarItem:
1614 case TransportMarkerBarItem:
1615 case CdMarkerBarItem:
1619 if (is_drawable()) {
1620 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1625 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1629 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1630 location_flags_changed (loc, this);
1633 case MeterMarkerItem:
1634 case TempoMarkerItem:
1636 if (is_drawable()) {
1637 track_canvas->get_window()->set_cursor (*timebar_cursor);
1642 case FadeInHandleItem:
1643 case FadeOutHandleItem:
1644 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1646 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1648 rect->property_fill_color_rgba() = rv->get_fill_color();
1649 rect->property_outline_pixels() = 0;
1654 case AutomationTrackItem:
1655 if (is_drawable()) {
1656 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1657 clear_entered_track = true;
1658 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1670 Editor::left_automation_track ()
1672 if (clear_entered_track) {
1673 set_entered_track (0);
1674 clear_entered_track = false;
1684 if (scrubbing_direction == 0) {
1686 session->request_locate (_drag->current_pointer_frame(), false);
1687 session->request_transport_speed (0.1);
1688 scrubbing_direction = 1;
1692 if (last_scrub_x > _drag->current_pointer_x()) {
1694 /* pointer moved to the left */
1696 if (scrubbing_direction > 0) {
1698 /* we reversed direction to go backwards */
1701 scrub_reverse_distance += (int) (last_scrub_x - _drag->current_pointer_x());
1705 /* still moving to the left (backwards) */
1707 scrub_reversals = 0;
1708 scrub_reverse_distance = 0;
1710 delta = 0.01 * (last_scrub_x - _drag->current_pointer_x());
1711 session->request_transport_speed (session->transport_speed() - delta);
1715 /* pointer moved to the right */
1717 if (scrubbing_direction < 0) {
1718 /* we reversed direction to go forward */
1721 scrub_reverse_distance += (int) (_drag->current_pointer_x() - last_scrub_x);
1724 /* still moving to the right */
1726 scrub_reversals = 0;
1727 scrub_reverse_distance = 0;
1729 delta = 0.01 * (_drag->current_pointer_x() - last_scrub_x);
1730 session->request_transport_speed (session->transport_speed() + delta);
1734 /* if there have been more than 2 opposite motion moves detected, or one that moves
1735 back more than 10 pixels, reverse direction
1738 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1740 if (scrubbing_direction > 0) {
1741 /* was forwards, go backwards */
1742 session->request_transport_speed (-0.1);
1743 scrubbing_direction = -1;
1745 /* was backwards, go forwards */
1746 session->request_transport_speed (0.1);
1747 scrubbing_direction = 1;
1750 scrub_reverse_distance = 0;
1751 scrub_reversals = 0;
1755 last_scrub_x = _drag->current_pointer_x();
1759 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1761 if (event->motion.is_hint) {
1764 /* We call this so that MOTION_NOTIFY events continue to be
1765 delivered to the canvas. We need to do this because we set
1766 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1767 the density of the events, at the expense of a round-trip
1768 to the server. Given that this will mostly occur on cases
1769 where DISPLAY = :0.0, and given the cost of what the motion
1770 event might do, its a good tradeoff.
1773 track_canvas->get_pointer (x, y);
1776 if (current_stepping_trackview) {
1777 /* don't keep the persistent stepped trackview if the mouse moves */
1778 current_stepping_trackview = 0;
1779 step_timeout.disconnect ();
1782 if (session && session->actively_recording()) {
1783 /* Sorry. no dragging stuff around while we record */
1787 bool handled = false;
1789 handled = _drag->motion_handler (event, from_autoscroll);
1796 track_canvas_motion (event);
1801 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1803 ControlPoint* control_point;
1805 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1806 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1810 // We shouldn't remove the first or last gain point
1811 if (control_point->line().is_last_point(*control_point) ||
1812 control_point->line().is_first_point(*control_point)) {
1816 control_point->line().remove_point (*control_point);
1820 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1822 ControlPoint* control_point;
1824 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1825 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1829 control_point->line().remove_point (*control_point);
1833 Editor::edit_control_point (ArdourCanvas::Item* item)
1835 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1838 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1842 ControlPointDialog d (p);
1843 d.set_position (Gtk::WIN_POS_MOUSE);
1846 if (d.run () != RESPONSE_ACCEPT) {
1850 p->line().modify_point_y (*p, d.get_y_fraction ());
1855 Editor::visible_order_range (int* low, int* high) const
1857 *low = TimeAxisView::max_order ();
1860 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1862 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1864 if (!rtv->hidden()) {
1866 if (*high < rtv->order()) {
1867 *high = rtv->order ();
1870 if (*low > rtv->order()) {
1871 *low = rtv->order ();
1878 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1880 /* Either add to or set the set the region selection, unless
1881 this is an alignment click (control used)
1884 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1885 TimeAxisView* tv = &rv.get_time_axis_view();
1886 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1888 if (rtv && rtv->is_track()) {
1889 speed = rtv->get_diskstream()->speed();
1892 nframes64_t where = get_preferred_edit_position();
1896 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1898 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
1900 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1902 align_region (rv.region(), End, (nframes64_t) (where * speed));
1906 align_region (rv.region(), Start, (nframes64_t) (where * speed));
1913 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
1916 Timecode::Time timecode;
1919 nframes64_t frame_rate;
1928 if (Profile->get_sae() || Profile->get_small_screen()) {
1929 m = ARDOUR_UI::instance()->primary_clock.mode();
1931 m = ARDOUR_UI::instance()->secondary_clock.mode();
1935 case AudioClock::BBT:
1936 session->bbt_time (frame, bbt);
1937 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
1940 case AudioClock::Timecode:
1941 session->timecode_time (frame, timecode);
1942 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1945 case AudioClock::MinSec:
1946 /* XXX this is copied from show_verbose_duration_cursor() */
1947 frame_rate = session->frame_rate();
1948 hours = frame / (frame_rate * 3600);
1949 frame = frame % (frame_rate * 3600);
1950 mins = frame / (frame_rate * 60);
1951 frame = frame % (frame_rate * 60);
1952 secs = (float) frame / (float) frame_rate;
1953 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
1957 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
1961 if (xpos >= 0 && ypos >=0) {
1962 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
1965 set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset - horizontal_adjustment.get_value(), _drag->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
1967 show_verbose_canvas_cursor ();
1971 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
1974 Timecode::Time timecode;
1978 nframes64_t distance, frame_rate;
1980 Meter meter_at_start(session->tempo_map().meter_at(start));
1988 if (Profile->get_sae() || Profile->get_small_screen()) {
1989 m = ARDOUR_UI::instance()->primary_clock.mode ();
1991 m = ARDOUR_UI::instance()->secondary_clock.mode ();
1995 case AudioClock::BBT:
1996 session->bbt_time (start, sbbt);
1997 session->bbt_time (end, ebbt);
2000 /* XXX this computation won't work well if the
2001 user makes a selection that spans any meter changes.
2004 ebbt.bars -= sbbt.bars;
2005 if (ebbt.beats >= sbbt.beats) {
2006 ebbt.beats -= sbbt.beats;
2009 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2011 if (ebbt.ticks >= sbbt.ticks) {
2012 ebbt.ticks -= sbbt.ticks;
2015 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2018 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2021 case AudioClock::Timecode:
2022 session->timecode_duration (end - start, timecode);
2023 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2026 case AudioClock::MinSec:
2027 /* XXX this stuff should be elsewhere.. */
2028 distance = end - start;
2029 frame_rate = session->frame_rate();
2030 hours = distance / (frame_rate * 3600);
2031 distance = distance % (frame_rate * 3600);
2032 mins = distance / (frame_rate * 60);
2033 distance = distance % (frame_rate * 60);
2034 secs = (float) distance / (float) frame_rate;
2035 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2039 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2043 if (xpos >= 0 && ypos >=0) {
2044 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2047 set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset, _drag->current_pointer_y() + offset);
2050 show_verbose_canvas_cursor ();
2054 Editor::collect_new_region_view (RegionView* rv)
2056 latest_regionviews.push_back (rv);
2060 Editor::collect_and_select_new_region_view (RegionView* rv)
2063 latest_regionviews.push_back (rv);
2067 Editor::cancel_selection ()
2069 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2070 (*i)->hide_selection ();
2073 selection->clear ();
2074 clicked_selection = 0;
2079 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
2081 boost::shared_ptr<Region> region (rv.region());
2083 if (region->locked()) {
2087 nframes64_t new_bound;
2090 TimeAxisView* tvp = clicked_axisview;
2091 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2093 if (tv && tv->is_track()) {
2094 speed = tv->get_diskstream()->speed();
2097 if (left_direction) {
2098 if (swap_direction) {
2099 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2101 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2104 if (swap_direction) {
2105 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2107 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2112 snap_to (new_bound);
2114 region->trim_start ((nframes64_t) (new_bound * speed), this);
2115 rv.region_changed (StartChanged);
2119 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2121 boost::shared_ptr<Region> region (rv.region());
2123 if (region->locked()) {
2127 nframes64_t new_bound;
2130 TimeAxisView* tvp = clicked_axisview;
2131 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2133 if (tv && tv->is_track()) {
2134 speed = tv->get_diskstream()->speed();
2137 if (left_direction) {
2138 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2140 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2144 snap_to (new_bound, (left_direction ? 0 : 1));
2147 nframes64_t pre_trim_first_frame = region->first_frame();
2149 region->trim_front ((nframes64_t) (new_bound * speed), this);
2152 //Get the next region on the left of this region and shrink/expand it.
2153 boost::shared_ptr<Playlist> playlist (region->playlist());
2154 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2156 bool regions_touching = false;
2158 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2159 regions_touching = true;
2162 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2163 if (region_left != 0 &&
2164 (region_left->last_frame() > region->first_frame() || regions_touching))
2166 region_left->trim_end(region->first_frame(), this);
2172 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2176 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2178 boost::shared_ptr<Region> region (rv.region());
2180 if (region->locked()) {
2184 nframes64_t new_bound;
2187 TimeAxisView* tvp = clicked_axisview;
2188 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2190 if (tv && tv->is_track()) {
2191 speed = tv->get_diskstream()->speed();
2194 if (left_direction) {
2195 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
2197 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
2201 snap_to (new_bound);
2204 nframes64_t pre_trim_last_frame = region->last_frame();
2206 region->trim_end ((nframes64_t) (new_bound * speed), this);
2209 //Get the next region on the right of this region and shrink/expand it.
2210 boost::shared_ptr<Playlist> playlist (region->playlist());
2211 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2213 bool regions_touching = false;
2215 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)){
2216 regions_touching = true;
2219 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2220 if (region_right != 0 &&
2221 (region_right->first_frame() < region->last_frame() || regions_touching))
2223 region_right->trim_front(region->last_frame() + 1, this);
2226 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2229 rv.region_changed (LengthChanged);
2235 Editor::point_trim (GdkEvent* event)
2237 RegionView* rv = clicked_regionview;
2239 nframes64_t new_bound = _drag->current_pointer_frame();
2241 snap_to_with_modifier (new_bound, event);
2243 /* Choose action dependant on which button was pressed */
2244 switch (event->button.button) {
2246 begin_reversible_command (_("Start point trim"));
2248 if (selection->selected (rv)) {
2249 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2250 i != selection->regions.by_layer().end(); ++i)
2253 cerr << "region view contains null region" << endl;
2256 if (!(*i)->region()->locked()) {
2257 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2258 XMLNode &before = pl->get_state();
2260 (*i)->region()->trim_front (new_bound, this);
2262 XMLNode &after = pl->get_state();
2263 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2268 if (!rv->region()->locked()) {
2269 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2270 XMLNode &before = pl->get_state();
2271 rv->region()->trim_front (new_bound, this);
2272 XMLNode &after = pl->get_state();
2273 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2277 commit_reversible_command();
2281 begin_reversible_command (_("End point trim"));
2283 if (selection->selected (rv)) {
2285 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2287 if (!(*i)->region()->locked()) {
2288 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2289 XMLNode &before = pl->get_state();
2290 (*i)->region()->trim_end (new_bound, this);
2291 XMLNode &after = pl->get_state();
2292 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2298 if (!rv->region()->locked()) {
2299 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2300 XMLNode &before = pl->get_state();
2301 rv->region()->trim_end (new_bound, this);
2302 XMLNode &after = pl->get_state();
2303 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
2307 commit_reversible_command();
2316 Editor::thaw_region_after_trim (RegionView& rv)
2318 boost::shared_ptr<Region> region (rv.region());
2320 if (region->locked()) {
2324 region->thaw (_("trimmed region"));
2326 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2329 arv->unhide_envelope ();
2334 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2339 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2340 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2344 Location* location = find_location_from_marker (marker, is_start);
2345 location->set_hidden (true, this);
2350 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2352 double x1 = frame_to_pixel (start);
2353 double x2 = frame_to_pixel (end);
2354 double y2 = full_canvas_height - 1.0;
2356 zoom_rect->property_x1() = x1;
2357 zoom_rect->property_y1() = 1.0;
2358 zoom_rect->property_x2() = x2;
2359 zoom_rect->property_y2() = y2;
2364 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2366 using namespace Gtkmm2ext;
2368 ArdourPrompter prompter (false);
2370 prompter.set_prompt (_("Name for region:"));
2371 prompter.set_initial_text (clicked_regionview->region()->name());
2372 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2373 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2374 prompter.show_all ();
2375 switch (prompter.run ()) {
2376 case Gtk::RESPONSE_ACCEPT:
2378 prompter.get_result(str);
2380 clicked_regionview->region()->set_name (str);
2389 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2391 /* no brushing without a useful snap setting */
2393 switch (snap_mode) {
2395 return; /* can't work because it allows region to be placed anywhere */
2400 switch (snap_type) {
2408 /* don't brush a copy over the original */
2410 if (pos == rv->region()->position()) {
2414 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2416 if (rtv == 0 || !rtv->is_track()) {
2420 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2421 double speed = rtv->get_diskstream()->speed();
2423 XMLNode &before = playlist->get_state();
2424 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
2425 XMLNode &after = playlist->get_state();
2426 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
2428 // playlist is frozen, so we have to update manually
2430 playlist->Modified(); /* EMIT SIGNAL */
2434 Editor::track_height_step_timeout ()
2436 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2437 current_stepping_trackview = 0;
2444 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2446 assert (region_view);
2448 _region_motion_group->raise_to_top ();
2450 assert (_drag == 0);
2452 if (Config->get_edit_mode() == Splice) {
2453 _drag = new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer());
2455 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2456 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false);
2459 _drag->start_grab (event);
2461 begin_reversible_command (_("move region(s)"));
2463 /* sync the canvas to what we think is its current state */
2464 update_canvas_now();
2468 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2470 assert (region_view);
2471 assert (_drag == 0);
2473 _region_motion_group->raise_to_top ();
2475 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2476 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true);
2477 _drag->start_grab(event);
2481 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2483 assert (region_view);
2484 assert (_drag == 0);
2486 if (Config->get_edit_mode() == Splice) {
2490 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2491 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false);
2492 _drag->start_grab (event);
2494 begin_reversible_command (_("Drag region brush"));
2497 /** Start a grab where a time range is selected, track(s) are selected, and the
2498 * user clicks and drags a region with a modifier in order to create a new region containing
2499 * the section of the clicked region that lies within the time range.
2502 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2504 if (clicked_regionview == 0) {
2508 /* lets try to create new Region for the selection */
2510 vector<boost::shared_ptr<Region> > new_regions;
2511 create_region_from_selection (new_regions);
2513 if (new_regions.empty()) {
2517 /* XXX fix me one day to use all new regions */
2519 boost::shared_ptr<Region> region (new_regions.front());
2521 /* add it to the current stream/playlist.
2523 tricky: the streamview for the track will add a new regionview. we will
2524 catch the signal it sends when it creates the regionview to
2525 set the regionview we want to then drag.
2528 latest_regionviews.clear();
2529 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2531 /* A selection grab currently creates two undo/redo operations, one for
2532 creating the new region and another for moving it.
2535 begin_reversible_command (_("selection grab"));
2537 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2539 XMLNode *before = &(playlist->get_state());
2540 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2541 XMLNode *after = &(playlist->get_state());
2542 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
2544 commit_reversible_command ();
2548 if (latest_regionviews.empty()) {
2549 /* something went wrong */
2553 /* we need to deselect all other regionviews, and select this one
2554 i'm ignoring undo stuff, because the region creation will take care of it
2556 selection->set (latest_regionviews);
2558 assert (_drag == 0);
2559 _drag = new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false);
2560 _drag->start_grab (event);
2564 Editor::break_drag ()
2567 _drag->break_drag ();
2572 Editor::set_internal_edit (bool yn)
2574 _internal_editing = yn;
2577 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2578 mouse_select_button.get_image ()->show ();
2580 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2581 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2583 mtv->start_step_editing ();
2586 start_step_editing ();
2590 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2591 mouse_select_button.get_image ()->show ();
2592 stop_step_editing ();
2594 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2595 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2597 mtv->stop_step_editing ();
2602 set_canvas_cursor ();