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"
70 #include "ardour/session.h"
77 using namespace ARDOUR;
81 using namespace Editing;
82 using Gtkmm2ext::Keyboard;
85 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
89 Gdk::ModifierType mask;
90 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
91 Glib::RefPtr<const Gdk::Window> pointer_window;
97 pointer_window = canvas_window->get_pointer (x, y, mask);
99 if (pointer_window == track_canvas->get_bin_window()) {
102 in_track_canvas = true;
105 in_track_canvas = false;
110 event.type = GDK_BUTTON_RELEASE;
114 where = event_frame (&event, 0, 0);
119 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
133 switch (event->type) {
134 case GDK_BUTTON_RELEASE:
135 case GDK_BUTTON_PRESS:
136 case GDK_2BUTTON_PRESS:
137 case GDK_3BUTTON_PRESS:
139 *pcx = event->button.x;
140 *pcy = event->button.y;
141 _trackview_group->w2i(*pcx, *pcy);
143 case GDK_MOTION_NOTIFY:
145 *pcx = event->motion.x;
146 *pcy = event->motion.y;
147 _trackview_group->w2i(*pcx, *pcy);
149 case GDK_ENTER_NOTIFY:
150 case GDK_LEAVE_NOTIFY:
151 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
154 case GDK_KEY_RELEASE:
155 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
158 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
162 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
163 position is negative (as can be the case with motion events in particular),
164 the frame location is always positive.
167 return pixel_to_frame (*pcx);
171 Editor::which_grabber_cursor ()
173 Gdk::Cursor* c = grabber_cursor;
175 if (_internal_editing) {
176 switch (mouse_mode) {
178 c = midi_pencil_cursor;
186 c = midi_resize_cursor;
195 switch (_edit_point) {
197 c = grabber_edit_point_cursor;
208 Editor::set_canvas_cursor ()
210 if (_internal_editing) {
212 switch (mouse_mode) {
214 current_canvas_cursor = midi_pencil_cursor;
218 current_canvas_cursor = which_grabber_cursor();
222 current_canvas_cursor = midi_resize_cursor;
231 switch (mouse_mode) {
233 current_canvas_cursor = selector_cursor;
237 current_canvas_cursor = which_grabber_cursor();
241 current_canvas_cursor = cross_hair_cursor;
245 current_canvas_cursor = zoom_cursor;
249 current_canvas_cursor = time_fx_cursor; // just use playhead
253 current_canvas_cursor = speaker_cursor;
259 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
264 Editor::set_mouse_mode (MouseMode m, bool force)
270 if (!force && m == mouse_mode) {
274 Glib::RefPtr<Action> act;
278 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
282 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
286 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
290 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
294 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
298 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
304 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
307 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
308 tact->set_active (false);
309 tact->set_active (true);
313 Editor::mouse_mode_toggled (MouseMode m)
319 if (mouse_mode != MouseRange) {
321 /* in all modes except range, hide the range selection,
322 show the object (region) selection.
325 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
326 (*i)->set_should_show_selection (true);
328 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
329 (*i)->hide_selection ();
335 in range mode,show the range selection.
338 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
339 if ((*i)->get_selected()) {
340 (*i)->show_selection (selection->time);
345 set_canvas_cursor ();
349 Editor::step_mouse_mode (bool next)
351 switch (current_mouse_mode()) {
354 if (Profile->get_sae()) {
355 set_mouse_mode (MouseZoom);
357 set_mouse_mode (MouseRange);
360 set_mouse_mode (MouseTimeFX);
365 if (next) set_mouse_mode (MouseZoom);
366 else set_mouse_mode (MouseObject);
371 if (Profile->get_sae()) {
372 set_mouse_mode (MouseTimeFX);
374 set_mouse_mode (MouseGain);
377 if (Profile->get_sae()) {
378 set_mouse_mode (MouseObject);
380 set_mouse_mode (MouseRange);
386 if (next) set_mouse_mode (MouseTimeFX);
387 else set_mouse_mode (MouseZoom);
392 set_mouse_mode (MouseAudition);
394 if (Profile->get_sae()) {
395 set_mouse_mode (MouseZoom);
397 set_mouse_mode (MouseGain);
403 if (next) set_mouse_mode (MouseObject);
404 else set_mouse_mode (MouseTimeFX);
410 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
412 /* in object/audition/timefx/gain-automation mode,
413 any button press sets the selection if the object
414 can be selected. this is a bit of hack, because
415 we want to avoid this if the mouse operation is a
418 note: not dbl-click or triple-click
421 if (((mouse_mode != MouseObject) &&
422 (mouse_mode != MouseAudition || item_type != RegionItem) &&
423 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
424 (mouse_mode != MouseGain) &&
425 (mouse_mode != MouseRange)) ||
427 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
432 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
434 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
436 /* almost no selection action on modified button-2 or button-3 events */
438 if (item_type != RegionItem && event->button.button != 2) {
444 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
445 bool press = (event->type == GDK_BUTTON_PRESS);
447 // begin_reversible_command (_("select on click"));
451 if (mouse_mode != MouseRange) {
452 set_selected_regionview_from_click (press, op, true);
453 } else if (event->type == GDK_BUTTON_PRESS) {
454 set_selected_track_as_side_effect ();
458 case RegionViewNameHighlight:
460 if (mouse_mode != MouseRange) {
461 set_selected_regionview_from_click (press, op, true);
462 } else if (event->type == GDK_BUTTON_PRESS) {
463 set_selected_track_as_side_effect ();
468 case FadeInHandleItem:
470 case FadeOutHandleItem:
472 if (mouse_mode != MouseRange) {
473 set_selected_regionview_from_click (press, op, true);
474 } else if (event->type == GDK_BUTTON_PRESS) {
475 set_selected_track_as_side_effect ();
479 case ControlPointItem:
480 set_selected_track_as_side_effect ();
481 if (mouse_mode != MouseRange) {
482 set_selected_control_point_from_click (op, false);
487 /* for context click or range selection, select track */
488 if (event->button.button == 3) {
489 set_selected_track_as_side_effect ();
490 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
491 set_selected_track_as_side_effect ();
495 case AutomationTrackItem:
496 set_selected_track_as_side_effect (true);
505 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
508 _drag->item()->ungrab (event->button.time);
513 /* single mouse clicks on any of these item types operate
514 independent of mouse mode, mostly because they are
515 not on the main track canvas or because we want
520 case PlayheadCursorItem:
522 _drag = new CursorDrag (this, item, true);
523 _drag->start_grab (event);
527 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
528 hide_marker (item, event);
531 _drag = new MarkerDrag (this, item);
532 _drag->start_grab (event);
536 case TempoMarkerItem:
538 _drag = new TempoMarkerDrag (
541 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
543 _drag->start_grab (event);
546 case MeterMarkerItem:
548 _drag = new MeterMarkerDrag (
551 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
553 _drag->start_grab (event);
559 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
561 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
562 _drag->start_grab (event);
568 case RangeMarkerBarItem:
570 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
571 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
573 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker);
575 _drag->start_grab (event);
579 case CdMarkerBarItem:
581 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
582 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
584 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker);
586 _drag->start_grab (event);
590 case TransportMarkerBarItem:
592 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
593 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
595 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker);
597 _drag->start_grab (event);
605 switch (mouse_mode) {
608 case StartSelectionTrimItem:
610 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim);
611 _drag->start_grab (event);
614 case EndSelectionTrimItem:
616 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim);
617 _drag->start_grab (event);
621 if (Keyboard::modifier_state_contains
622 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
623 // contains and not equals because I can't use alt as a modifier alone.
624 start_selection_grab (item, event);
625 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
626 /* grab selection for moving */
628 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionMove);
629 _drag->start_grab (event);
631 /* this was debated, but decided the more common action was to
632 make a new selection */
634 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
635 _drag->start_grab (event);
641 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
642 _drag->start_grab (event);
650 if (internal_editing()) {
651 /* Note: we don't get here if not in internal_editing() mode */
653 _drag = new NoteDrag (this, item);
654 _drag->start_grab (event);
663 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
664 event->type == GDK_BUTTON_PRESS) {
667 _drag = new RubberbandSelectDrag (this, item);
668 _drag->start_grab (event);
670 } else if (event->type == GDK_BUTTON_PRESS) {
673 case FadeInHandleItem:
676 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
677 _drag = new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
678 _drag->start_grab (event);
682 case FadeOutHandleItem:
685 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
686 _drag = new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
687 _drag->start_grab (event);
692 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
693 start_region_copy_grab (item, event, clicked_regionview);
694 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
695 start_region_brush_grab (item, event, clicked_regionview);
697 start_region_grab (item, event, clicked_regionview);
701 case RegionViewNameHighlight:
704 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
705 _drag = new TrimDrag (this, item, clicked_regionview, s.by_layer());
706 _drag->start_grab (event);
713 /* rename happens on edit clicks */
715 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
716 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer());
717 _drag->start_grab (event);
722 case ControlPointItem:
724 _drag = new ControlPointDrag (this, item);
725 _drag->start_grab (event);
729 case AutomationLineItem:
731 _drag = new LineDrag (this, item);
732 _drag->start_grab (event);
737 if (internal_editing()) {
739 _drag = new RegionCreateDrag (this, item, clicked_axisview);
740 _drag->start_grab (event);
744 case AutomationTrackItem:
746 _drag = new RubberbandSelectDrag (this, item);
747 _drag->start_grab (event);
751 case ImageFrameHandleStartItem:
752 imageframe_start_handle_op(item, event) ;
755 case ImageFrameHandleEndItem:
756 imageframe_end_handle_op(item, event) ;
759 case MarkerViewHandleStartItem:
760 markerview_item_start_handle_op(item, event) ;
763 case MarkerViewHandleEndItem:
764 markerview_item_end_handle_op(item, event) ;
768 start_markerview_grab(item, event) ;
771 start_imageframe_grab(item, event) ;
789 /* start a grab so that if we finish after moving
790 we can tell what happened.
793 _drag = new RegionGainDrag (this, item);
794 _drag->start_grab (event, current_canvas_cursor);
799 _drag = new LineDrag (this, item);
800 _drag->start_grab (event);
803 case ControlPointItem:
805 _drag = new ControlPointDrag (this, item);
806 _drag->start_grab (event);
817 case ControlPointItem:
819 _drag = new ControlPointDrag (this, item);
820 _drag->start_grab (event);
823 case AutomationLineItem:
825 _drag = new LineDrag (this, item);
826 _drag->start_grab (event);
830 // XXX need automation mode to identify which
832 // start_line_grab_from_regionview (item, event);
842 if (event->type == GDK_BUTTON_PRESS) {
844 _drag = new MouseZoomDrag (this, item);
845 _drag->start_grab (event);
852 if (internal_editing() && item_type == NoteItem) {
854 _drag = new NoteResizeDrag (this, item);
855 _drag->start_grab (event);
857 } else if (!internal_editing() && item_type == RegionItem) {
859 _drag = new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer());
860 _drag->start_grab (event);
866 _drag = new ScrubDrag (this, item);
867 _drag->start_grab (event);
869 scrub_reverse_distance = 0;
870 last_scrub_x = event->button.x;
871 scrubbing_direction = 0;
872 track_canvas->get_window()->set_cursor (*transparent_cursor);
884 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
886 switch (mouse_mode) {
890 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
891 start_region_copy_grab (item, event, clicked_regionview);
893 start_region_grab (item, event, clicked_regionview);
897 case ControlPointItem:
899 _drag = new ControlPointDrag (this, item);
900 _drag->start_grab (event);
909 case RegionViewNameHighlight:
911 _drag = new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer());
912 _drag->start_grab (event);
918 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer());
919 _drag->start_grab (event);
930 /* relax till release */
936 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
937 temporal_zoom_session();
939 temporal_zoom_to_frame (true, event_frame(event));
952 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
954 if (event->type != GDK_BUTTON_PRESS) {
958 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
961 Glib::RefPtr<const Gdk::Window> pointer_window;
964 Gdk::ModifierType mask;
966 pointer_window = canvas_window->get_pointer (x, y, mask);
968 if (pointer_window == track_canvas->get_bin_window()) {
969 track_canvas->window_to_world (x, y, wx, wy);
970 allow_vertical_scroll = true;
972 allow_vertical_scroll = false;
976 track_canvas->grab_focus();
978 if (session && session->actively_recording()) {
982 button_selection (item, event, item_type);
985 (Keyboard::is_delete_event (&event->button) ||
986 Keyboard::is_context_menu_event (&event->button) ||
987 Keyboard::is_edit_event (&event->button))) {
989 /* handled by button release */
993 switch (event->button.button) {
995 return button_press_handler_1 (item, event, item_type);
999 return button_press_handler_2 (item, event, item_type);
1014 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1016 nframes64_t where = event_frame (event, 0, 0);
1017 AutomationTimeAxisView* atv = 0;
1019 /* no action if we're recording */
1021 if (session && session->actively_recording()) {
1025 /* first, see if we're finishing a drag ... */
1027 bool were_dragging = false;
1029 bool const r = _drag->end_grab (event);
1033 /* grab dragged, so do nothing else */
1037 were_dragging = true;
1040 button_selection (item, event, item_type);
1042 /* edit events get handled here */
1044 if (_drag == 0 && Keyboard::is_edit_event (&event->button)) {
1045 switch (item_type) {
1050 case TempoMarkerItem:
1051 edit_tempo_marker (item);
1054 case MeterMarkerItem:
1055 edit_meter_marker (item);
1058 case RegionViewName:
1059 if (clicked_regionview->name_active()) {
1060 return mouse_rename_region (item, event);
1064 case ControlPointItem:
1065 edit_control_point (item);
1074 /* context menu events get handled here */
1076 if (Keyboard::is_context_menu_event (&event->button)) {
1080 /* no matter which button pops up the context menu, tell the menu
1081 widget to use button 1 to drive menu selection.
1084 switch (item_type) {
1086 case FadeInHandleItem:
1088 case FadeOutHandleItem:
1089 popup_fade_context_menu (1, event->button.time, item, item_type);
1093 popup_track_context_menu (1, event->button.time, item_type, false, where);
1097 case RegionViewNameHighlight:
1098 case RegionViewName:
1099 popup_track_context_menu (1, event->button.time, item_type, false, where);
1103 popup_track_context_menu (1, event->button.time, item_type, true, where);
1106 case AutomationTrackItem:
1107 popup_track_context_menu (1, event->button.time, item_type, false, where);
1111 case RangeMarkerBarItem:
1112 case TransportMarkerBarItem:
1113 case CdMarkerBarItem:
1116 popup_ruler_menu (where, item_type);
1120 marker_context_menu (&event->button, item);
1123 case TempoMarkerItem:
1124 tm_marker_context_menu (&event->button, item);
1127 case MeterMarkerItem:
1128 tm_marker_context_menu (&event->button, item);
1131 case CrossfadeViewItem:
1132 popup_track_context_menu (1, event->button.time, item_type, false, where);
1136 case ImageFrameItem:
1137 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1139 case ImageFrameTimeAxisItem:
1140 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1142 case MarkerViewItem:
1143 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1145 case MarkerTimeAxisItem:
1146 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1158 /* delete events get handled here */
1160 if (_drag == 0 && Keyboard::is_delete_event (&event->button)) {
1162 switch (item_type) {
1163 case TempoMarkerItem:
1164 remove_tempo_marker (item);
1167 case MeterMarkerItem:
1168 remove_meter_marker (item);
1172 remove_marker (*item, event);
1176 if (mouse_mode == MouseObject) {
1177 remove_clicked_region ();
1181 case ControlPointItem:
1182 if (mouse_mode == MouseGain) {
1183 remove_gain_control_point (item, event);
1185 remove_control_point (item, event);
1195 switch (event->button.button) {
1198 switch (item_type) {
1199 /* see comments in button_press_handler */
1200 case PlayheadCursorItem:
1203 case AutomationLineItem:
1204 case StartSelectionTrimItem:
1205 case EndSelectionTrimItem:
1209 if (!_dragging_playhead) {
1210 snap_to_with_modifier (where, event, 0, true);
1211 mouse_add_new_marker (where);
1215 case CdMarkerBarItem:
1216 if (!_dragging_playhead) {
1217 // if we get here then a dragged range wasn't done
1218 snap_to_with_modifier (where, event, 0, true);
1219 mouse_add_new_marker (where, true);
1224 if (!_dragging_playhead) {
1225 snap_to_with_modifier (where, event);
1226 mouse_add_new_tempo_event (where);
1231 if (!_dragging_playhead) {
1232 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1241 switch (mouse_mode) {
1243 switch (item_type) {
1244 case AutomationTrackItem:
1245 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1247 atv->add_automation_event (item, event, where, event->button.y);
1258 // Gain only makes sense for audio regions
1260 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1264 switch (item_type) {
1266 /* check that we didn't drag before releasing, since
1267 its really annoying to create new control
1268 points when doing this.
1270 if (were_dragging) {
1271 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1276 case AutomationTrackItem:
1277 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1278 add_automation_event (item, event, where, event->button.y);
1287 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1288 if (scrubbing_direction == 0) {
1289 /* no drag, just a click */
1290 switch (item_type) {
1292 play_selected_region ();
1298 /* make sure we stop */
1299 session->request_transport_speed (0.0);
1313 switch (mouse_mode) {
1316 switch (item_type) {
1318 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1320 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1323 // Button2 click is unused
1336 // x_style_paste (where, 1.0);
1356 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1362 if (last_item_entered != item) {
1363 last_item_entered = item;
1364 last_item_entered_n = 0;
1367 switch (item_type) {
1368 case ControlPointItem:
1369 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1370 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1371 cp->set_visible (true);
1375 at_y = cp->get_y ();
1376 cp->item()->i2w (at_x, at_y);
1380 fraction = 1.0 - (cp->get_y() / cp->line().height());
1382 if (is_drawable() && dynamic_cast<ScrubDrag*> (_drag) == 0) {
1383 track_canvas->get_window()->set_cursor (*fader_cursor);
1386 last_item_entered_n++;
1387 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1388 if (last_item_entered_n < 10) {
1389 show_verbose_canvas_cursor ();
1395 if (mouse_mode == MouseGain) {
1396 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1398 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1399 if (is_drawable()) {
1400 track_canvas->get_window()->set_cursor (*fader_cursor);
1405 case AutomationLineItem:
1406 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1408 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1410 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1412 if (is_drawable()) {
1413 track_canvas->get_window()->set_cursor (*fader_cursor);
1418 case RegionViewNameHighlight:
1419 if (is_drawable() && mouse_mode == MouseObject) {
1420 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1424 case StartSelectionTrimItem:
1425 case EndSelectionTrimItem:
1428 case ImageFrameHandleStartItem:
1429 case ImageFrameHandleEndItem:
1430 case MarkerViewHandleStartItem:
1431 case MarkerViewHandleEndItem:
1434 if (is_drawable()) {
1435 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1439 case PlayheadCursorItem:
1440 if (is_drawable()) {
1441 switch (_edit_point) {
1443 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1446 track_canvas->get_window()->set_cursor (*grabber_cursor);
1452 case RegionViewName:
1454 /* when the name is not an active item, the entire name highlight is for trimming */
1456 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1457 if (mouse_mode == MouseObject && is_drawable()) {
1458 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1464 case AutomationTrackItem:
1465 if (is_drawable()) {
1466 Gdk::Cursor *cursor;
1467 switch (mouse_mode) {
1469 cursor = selector_cursor;
1472 cursor = zoom_cursor;
1475 cursor = cross_hair_cursor;
1479 track_canvas->get_window()->set_cursor (*cursor);
1481 AutomationTimeAxisView* atv;
1482 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1483 clear_entered_track = false;
1484 set_entered_track (atv);
1490 case RangeMarkerBarItem:
1491 case TransportMarkerBarItem:
1492 case CdMarkerBarItem:
1495 if (is_drawable()) {
1496 track_canvas->get_window()->set_cursor (*timebar_cursor);
1501 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1504 entered_marker = marker;
1505 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1507 case MeterMarkerItem:
1508 case TempoMarkerItem:
1509 if (is_drawable()) {
1510 track_canvas->get_window()->set_cursor (*timebar_cursor);
1513 case FadeInHandleItem:
1514 case FadeOutHandleItem:
1515 if (mouse_mode == MouseObject) {
1516 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1518 rect->property_fill_color_rgba() = 0;
1519 rect->property_outline_pixels() = 1;
1528 /* second pass to handle entered track status in a comprehensible way.
1531 switch (item_type) {
1533 case AutomationLineItem:
1534 case ControlPointItem:
1535 /* these do not affect the current entered track state */
1536 clear_entered_track = false;
1539 case AutomationTrackItem:
1540 /* handled above already */
1544 set_entered_track (0);
1552 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1561 switch (item_type) {
1562 case ControlPointItem:
1563 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1564 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1565 if (cp->line().npoints() > 1 && !cp->selected()) {
1566 cp->set_visible (false);
1570 if (is_drawable()) {
1571 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1574 hide_verbose_canvas_cursor ();
1577 case RegionViewNameHighlight:
1578 case StartSelectionTrimItem:
1579 case EndSelectionTrimItem:
1580 case PlayheadCursorItem:
1583 case ImageFrameHandleStartItem:
1584 case ImageFrameHandleEndItem:
1585 case MarkerViewHandleStartItem:
1586 case MarkerViewHandleEndItem:
1589 if (is_drawable()) {
1590 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1595 case AutomationLineItem:
1596 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1598 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1600 line->property_fill_color_rgba() = al->get_line_color();
1602 if (is_drawable()) {
1603 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1607 case RegionViewName:
1608 /* see enter_handler() for notes */
1609 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1610 if (is_drawable() && mouse_mode == MouseObject) {
1611 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1616 case RangeMarkerBarItem:
1617 case TransportMarkerBarItem:
1618 case CdMarkerBarItem:
1622 if (is_drawable()) {
1623 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1628 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1632 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1633 location_flags_changed (loc, this);
1636 case MeterMarkerItem:
1637 case TempoMarkerItem:
1639 if (is_drawable()) {
1640 track_canvas->get_window()->set_cursor (*timebar_cursor);
1645 case FadeInHandleItem:
1646 case FadeOutHandleItem:
1647 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1649 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1651 rect->property_fill_color_rgba() = rv->get_fill_color();
1652 rect->property_outline_pixels() = 0;
1657 case AutomationTrackItem:
1658 if (is_drawable()) {
1659 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1660 clear_entered_track = true;
1661 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1673 Editor::left_automation_track ()
1675 if (clear_entered_track) {
1676 set_entered_track (0);
1677 clear_entered_track = false;
1687 if (scrubbing_direction == 0) {
1689 session->request_locate (_drag->current_pointer_frame(), false);
1690 session->request_transport_speed (0.1);
1691 scrubbing_direction = 1;
1695 if (last_scrub_x > _drag->current_pointer_x()) {
1697 /* pointer moved to the left */
1699 if (scrubbing_direction > 0) {
1701 /* we reversed direction to go backwards */
1704 scrub_reverse_distance += (int) (last_scrub_x - _drag->current_pointer_x());
1708 /* still moving to the left (backwards) */
1710 scrub_reversals = 0;
1711 scrub_reverse_distance = 0;
1713 delta = 0.01 * (last_scrub_x - _drag->current_pointer_x());
1714 session->request_transport_speed (session->transport_speed() - delta);
1718 /* pointer moved to the right */
1720 if (scrubbing_direction < 0) {
1721 /* we reversed direction to go forward */
1724 scrub_reverse_distance += (int) (_drag->current_pointer_x() - last_scrub_x);
1727 /* still moving to the right */
1729 scrub_reversals = 0;
1730 scrub_reverse_distance = 0;
1732 delta = 0.01 * (_drag->current_pointer_x() - last_scrub_x);
1733 session->request_transport_speed (session->transport_speed() + delta);
1737 /* if there have been more than 2 opposite motion moves detected, or one that moves
1738 back more than 10 pixels, reverse direction
1741 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1743 if (scrubbing_direction > 0) {
1744 /* was forwards, go backwards */
1745 session->request_transport_speed (-0.1);
1746 scrubbing_direction = -1;
1748 /* was backwards, go forwards */
1749 session->request_transport_speed (0.1);
1750 scrubbing_direction = 1;
1753 scrub_reverse_distance = 0;
1754 scrub_reversals = 0;
1758 last_scrub_x = _drag->current_pointer_x();
1762 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1764 if (event->motion.is_hint) {
1767 /* We call this so that MOTION_NOTIFY events continue to be
1768 delivered to the canvas. We need to do this because we set
1769 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1770 the density of the events, at the expense of a round-trip
1771 to the server. Given that this will mostly occur on cases
1772 where DISPLAY = :0.0, and given the cost of what the motion
1773 event might do, its a good tradeoff.
1776 track_canvas->get_pointer (x, y);
1779 if (current_stepping_trackview) {
1780 /* don't keep the persistent stepped trackview if the mouse moves */
1781 current_stepping_trackview = 0;
1782 step_timeout.disconnect ();
1785 if (session && session->actively_recording()) {
1786 /* Sorry. no dragging stuff around while we record */
1790 bool handled = false;
1792 handled = _drag->motion_handler (event, from_autoscroll);
1799 track_canvas_motion (event);
1804 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1806 ControlPoint* control_point;
1808 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1809 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1813 // We shouldn't remove the first or last gain point
1814 if (control_point->line().is_last_point(*control_point) ||
1815 control_point->line().is_first_point(*control_point)) {
1819 control_point->line().remove_point (*control_point);
1823 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1825 ControlPoint* control_point;
1827 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1828 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1832 control_point->line().remove_point (*control_point);
1836 Editor::edit_control_point (ArdourCanvas::Item* item)
1838 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1841 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1845 ControlPointDialog d (p);
1846 d.set_position (Gtk::WIN_POS_MOUSE);
1849 if (d.run () != RESPONSE_ACCEPT) {
1853 p->line().modify_point_y (*p, d.get_y_fraction ());
1858 Editor::visible_order_range (int* low, int* high) const
1860 *low = TimeAxisView::max_order ();
1863 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1865 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1867 if (!rtv->hidden()) {
1869 if (*high < rtv->order()) {
1870 *high = rtv->order ();
1873 if (*low > rtv->order()) {
1874 *low = rtv->order ();
1881 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1883 /* Either add to or set the set the region selection, unless
1884 this is an alignment click (control used)
1887 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1888 TimeAxisView* tv = &rv.get_time_axis_view();
1889 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1891 if (rtv && rtv->is_track()) {
1892 speed = rtv->get_diskstream()->speed();
1895 nframes64_t where = get_preferred_edit_position();
1899 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1901 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
1903 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1905 align_region (rv.region(), End, (nframes64_t) (where * speed));
1909 align_region (rv.region(), Start, (nframes64_t) (where * speed));
1916 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
1919 Timecode::Time timecode;
1922 nframes64_t frame_rate;
1931 if (Profile->get_sae() || Profile->get_small_screen()) {
1932 m = ARDOUR_UI::instance()->primary_clock.mode();
1934 m = ARDOUR_UI::instance()->secondary_clock.mode();
1938 case AudioClock::BBT:
1939 session->bbt_time (frame, bbt);
1940 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
1943 case AudioClock::Timecode:
1944 session->timecode_time (frame, timecode);
1945 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1948 case AudioClock::MinSec:
1949 /* XXX this is copied from show_verbose_duration_cursor() */
1950 frame_rate = session->frame_rate();
1951 hours = frame / (frame_rate * 3600);
1952 frame = frame % (frame_rate * 3600);
1953 mins = frame / (frame_rate * 60);
1954 frame = frame % (frame_rate * 60);
1955 secs = (float) frame / (float) frame_rate;
1956 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
1960 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
1964 if (xpos >= 0 && ypos >=0) {
1965 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
1967 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);
1969 show_verbose_canvas_cursor ();
1973 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
1976 Timecode::Time timecode;
1980 nframes64_t distance, frame_rate;
1982 Meter meter_at_start(session->tempo_map().meter_at(start));
1990 if (Profile->get_sae() || Profile->get_small_screen()) {
1991 m = ARDOUR_UI::instance()->primary_clock.mode ();
1993 m = ARDOUR_UI::instance()->secondary_clock.mode ();
1997 case AudioClock::BBT:
1998 session->bbt_time (start, sbbt);
1999 session->bbt_time (end, ebbt);
2002 /* XXX this computation won't work well if the
2003 user makes a selection that spans any meter changes.
2006 ebbt.bars -= sbbt.bars;
2007 if (ebbt.beats >= sbbt.beats) {
2008 ebbt.beats -= sbbt.beats;
2011 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2013 if (ebbt.ticks >= sbbt.ticks) {
2014 ebbt.ticks -= sbbt.ticks;
2017 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2020 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2023 case AudioClock::Timecode:
2024 session->timecode_duration (end - start, timecode);
2025 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2028 case AudioClock::MinSec:
2029 /* XXX this stuff should be elsewhere.. */
2030 distance = end - start;
2031 frame_rate = session->frame_rate();
2032 hours = distance / (frame_rate * 3600);
2033 distance = distance % (frame_rate * 3600);
2034 mins = distance / (frame_rate * 60);
2035 distance = distance % (frame_rate * 60);
2036 secs = (float) distance / (float) frame_rate;
2037 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2041 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2045 if (xpos >= 0 && ypos >=0) {
2046 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2049 set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset, _drag->current_pointer_y() + offset);
2052 show_verbose_canvas_cursor ();
2056 Editor::collect_new_region_view (RegionView* rv)
2058 latest_regionviews.push_back (rv);
2062 Editor::collect_and_select_new_region_view (RegionView* rv)
2065 latest_regionviews.push_back (rv);
2069 Editor::cancel_selection ()
2071 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2072 (*i)->hide_selection ();
2075 selection->clear ();
2076 clicked_selection = 0;
2081 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
2083 boost::shared_ptr<Region> region (rv.region());
2085 if (region->locked()) {
2089 nframes64_t new_bound;
2092 TimeAxisView* tvp = clicked_axisview;
2093 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2095 if (tv && tv->is_track()) {
2096 speed = tv->get_diskstream()->speed();
2099 if (left_direction) {
2100 if (swap_direction) {
2101 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2103 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2106 if (swap_direction) {
2107 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2109 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2114 snap_to (new_bound);
2116 region->trim_start ((nframes64_t) (new_bound * speed), this);
2117 rv.region_changed (StartChanged);
2121 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2123 boost::shared_ptr<Region> region (rv.region());
2125 if (region->locked()) {
2129 nframes64_t new_bound;
2132 TimeAxisView* tvp = clicked_axisview;
2133 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2135 if (tv && tv->is_track()) {
2136 speed = tv->get_diskstream()->speed();
2139 if (left_direction) {
2140 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2142 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2146 snap_to (new_bound, (left_direction ? 0 : 1));
2149 nframes64_t pre_trim_first_frame = region->first_frame();
2151 region->trim_front ((nframes64_t) (new_bound * speed), this);
2154 //Get the next region on the left of this region and shrink/expand it.
2155 boost::shared_ptr<Playlist> playlist (region->playlist());
2156 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2158 bool regions_touching = false;
2160 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2161 regions_touching = true;
2164 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2165 if (region_left != 0 &&
2166 (region_left->last_frame() > region->first_frame() || regions_touching))
2168 region_left->trim_end(region->first_frame() - 1, 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 ();