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;
180 c = midi_select_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 = midi_select_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);
304 tact->set_active (true);
308 Editor::mouse_mode_toggled (MouseMode m)
314 if (mouse_mode != MouseRange) {
316 /* in all modes except range, hide the range selection,
317 show the object (region) selection.
320 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
321 (*i)->set_should_show_selection (true);
323 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
324 (*i)->hide_selection ();
330 in range mode,show the range selection.
333 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
334 if ((*i)->get_selected()) {
335 (*i)->show_selection (selection->time);
340 set_canvas_cursor ();
344 Editor::step_mouse_mode (bool next)
346 switch (current_mouse_mode()) {
349 if (Profile->get_sae()) {
350 set_mouse_mode (MouseZoom);
352 set_mouse_mode (MouseRange);
355 set_mouse_mode (MouseTimeFX);
360 if (next) set_mouse_mode (MouseZoom);
361 else set_mouse_mode (MouseObject);
366 if (Profile->get_sae()) {
367 set_mouse_mode (MouseTimeFX);
369 set_mouse_mode (MouseGain);
372 if (Profile->get_sae()) {
373 set_mouse_mode (MouseObject);
375 set_mouse_mode (MouseRange);
381 if (next) set_mouse_mode (MouseTimeFX);
382 else set_mouse_mode (MouseZoom);
387 set_mouse_mode (MouseAudition);
389 if (Profile->get_sae()) {
390 set_mouse_mode (MouseZoom);
392 set_mouse_mode (MouseGain);
398 if (next) set_mouse_mode (MouseObject);
399 else set_mouse_mode (MouseTimeFX);
405 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
407 /* in object/audition/timefx/gain-automation mode,
408 any button press sets the selection if the object
409 can be selected. this is a bit of hack, because
410 we want to avoid this if the mouse operation is a
413 note: not dbl-click or triple-click
416 if (((mouse_mode != MouseObject) &&
417 (mouse_mode != MouseAudition || item_type != RegionItem) &&
418 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
419 (mouse_mode != MouseGain) &&
420 (mouse_mode != MouseRange)) ||
422 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
427 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
429 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
431 /* almost no selection action on modified button-2 or button-3 events */
433 if (item_type != RegionItem && event->button.button != 2) {
439 Selection::Operation op = Keyboard::selection_type (event->button.state);
440 bool press = (event->type == GDK_BUTTON_PRESS);
442 // begin_reversible_command (_("select on click"));
446 if (mouse_mode != MouseRange) {
447 set_selected_regionview_from_click (press, op, true);
448 } else if (event->type == GDK_BUTTON_PRESS) {
449 set_selected_track_as_side_effect ();
453 case RegionViewNameHighlight:
455 if (mouse_mode != MouseRange) {
456 set_selected_regionview_from_click (press, op, true);
457 } else if (event->type == GDK_BUTTON_PRESS) {
458 set_selected_track_as_side_effect ();
463 case FadeInHandleItem:
465 case FadeOutHandleItem:
467 if (mouse_mode != MouseRange) {
468 set_selected_regionview_from_click (press, op, true);
469 } else if (event->type == GDK_BUTTON_PRESS) {
470 set_selected_track_as_side_effect ();
474 case ControlPointItem:
475 set_selected_track_as_side_effect ();
476 if (mouse_mode != MouseRange) {
477 set_selected_control_point_from_click (op, false);
482 /* for context click or range selection, select track */
483 if (event->button.button == 3) {
484 set_selected_track_as_side_effect ();
485 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
486 set_selected_track_as_side_effect ();
490 case AutomationTrackItem:
491 set_selected_track_as_side_effect (true);
500 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
502 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
505 Glib::RefPtr<const Gdk::Window> pointer_window;
508 Gdk::ModifierType mask;
510 pointer_window = canvas_window->get_pointer (x, y, mask);
512 if (pointer_window == track_canvas->get_bin_window()) {
513 track_canvas->window_to_world (x, y, wx, wy);
514 allow_vertical_scroll = true;
516 allow_vertical_scroll = false;
520 track_canvas->grab_focus();
522 if (session && session->actively_recording()) {
526 button_selection (item, event, item_type);
529 (Keyboard::is_delete_event (&event->button) ||
530 Keyboard::is_context_menu_event (&event->button) ||
531 Keyboard::is_edit_event (&event->button))) {
533 /* handled by button release */
537 switch (event->button.button) {
540 if (event->type == GDK_BUTTON_PRESS) {
543 _drag->item()->ungrab (event->button.time);
546 /* single mouse clicks on any of these item types operate
547 independent of mouse mode, mostly because they are
548 not on the main track canvas or because we want
553 case PlayheadCursorItem:
555 _drag = new CursorDrag (this, item, true);
556 _drag->start_grab (event);
560 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
561 hide_marker (item, event);
564 _drag = new MarkerDrag (this, item);
565 _drag->start_grab (event);
569 case TempoMarkerItem:
571 _drag = new TempoMarkerDrag (
574 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
576 _drag->start_grab (event);
579 case MeterMarkerItem:
582 _drag = new MeterMarkerDrag (
585 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
588 _drag->start_grab (event);
594 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
596 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
597 _drag->start_grab (event);
603 case RangeMarkerBarItem:
605 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
606 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
608 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker);
610 _drag->start_grab (event);
614 case CdMarkerBarItem:
616 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
617 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
619 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker);
621 _drag->start_grab (event);
625 case TransportMarkerBarItem:
627 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
628 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
630 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker);
632 _drag->start_grab (event);
641 if (internal_editing()) {
643 _drag = new RegionCreateDrag (this, item, clicked_axisview);
644 _drag->start_grab (event);
645 cerr << "--- DRAG START FOR RCD\n";
647 switch (mouse_mode) {
650 case StartSelectionTrimItem:
652 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim);
653 _drag->start_grab (event);
656 case EndSelectionTrimItem:
658 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim);
659 _drag->start_grab (event);
663 if (Keyboard::modifier_state_contains
664 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
665 // contains and not equals because I can't use alt as a modifier alone.
666 start_selection_grab (item, event);
667 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
668 /* grab selection for moving */
670 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionMove);
671 _drag->start_grab (event);
673 /* this was debated, but decided the more common action was to
674 make a new selection */
676 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
677 _drag->start_grab (event);
683 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
684 _drag->start_grab (event);
690 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
691 event->type == GDK_BUTTON_PRESS) {
694 _drag = new RubberbandSelectDrag (this, item);
695 _drag->start_grab (event);
697 } else if (event->type == GDK_BUTTON_PRESS) {
700 case FadeInHandleItem:
703 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
704 _drag = new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
705 _drag->start_grab (event);
709 case FadeOutHandleItem:
712 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
713 _drag = new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
714 _drag->start_grab (event);
719 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
720 start_region_copy_grab (item, event, clicked_regionview);
721 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
722 start_region_brush_grab (item, event, clicked_regionview);
724 start_region_grab (item, event, clicked_regionview);
728 case RegionViewNameHighlight:
731 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
732 _drag = new TrimDrag (this, item, clicked_regionview, s.by_layer());
733 _drag->start_grab (event);
740 /* rename happens on edit clicks */
742 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
743 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer());
744 _drag->start_grab (event);
749 case ControlPointItem:
751 _drag = new ControlPointDrag (this, item);
752 _drag->start_grab (event);
756 case AutomationLineItem:
758 _drag = new LineDrag (this, item);
759 _drag->start_grab (event);
764 case AutomationTrackItem:
766 _drag = new RubberbandSelectDrag (this, item);
767 _drag->start_grab (event);
771 case ImageFrameHandleStartItem:
772 imageframe_start_handle_op(item, event) ;
775 case ImageFrameHandleEndItem:
776 imageframe_end_handle_op(item, event) ;
779 case MarkerViewHandleStartItem:
780 markerview_item_start_handle_op(item, event) ;
783 case MarkerViewHandleEndItem:
784 markerview_item_end_handle_op(item, event) ;
788 start_markerview_grab(item, event) ;
791 start_imageframe_grab(item, event) ;
809 /* start a grab so that if we finish after moving
810 we can tell what happened.
813 _drag = new RegionGainDrag (this, item);
814 _drag->start_grab (event, current_canvas_cursor);
819 _drag = new LineDrag (this, item);
820 _drag->start_grab (event);
823 case ControlPointItem:
825 _drag = new ControlPointDrag (this, item);
826 _drag->start_grab (event);
837 case ControlPointItem:
839 _drag = new ControlPointDrag (this, item);
840 _drag->start_grab (event);
843 case AutomationLineItem:
845 _drag = new LineDrag (this, item);
846 _drag->start_grab (event);
850 // XXX need automation mode to identify which
852 // start_line_grab_from_regionview (item, event);
862 if (event->type == GDK_BUTTON_PRESS) {
864 _drag = new MouseZoomDrag (this, item);
865 _drag->start_grab (event);
872 if (item_type == RegionItem) {
874 _drag = new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer());
875 _drag->start_grab (event);
880 _drag = new ScrubDrag (this, item);
881 _drag->start_grab (event);
883 scrub_reverse_distance = 0;
884 last_scrub_x = event->button.x;
885 scrubbing_direction = 0;
886 track_canvas->get_window()->set_cursor (*transparent_cursor);
896 switch (mouse_mode) {
898 if (event->type == GDK_BUTTON_PRESS) {
901 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
902 start_region_copy_grab (item, event, clicked_regionview);
904 start_region_grab (item, event, clicked_regionview);
908 case ControlPointItem:
910 _drag = new ControlPointDrag (this, item);
911 _drag->start_grab (event);
922 case RegionViewNameHighlight:
924 _drag = new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer());
925 _drag->start_grab (event);
931 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer());
932 _drag->start_grab (event);
943 if (event->type == GDK_BUTTON_PRESS) {
944 /* relax till release */
951 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
952 temporal_zoom_session();
954 temporal_zoom_to_frame (true, event_frame(event));
977 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
979 nframes64_t where = event_frame (event, 0, 0);
980 AutomationTimeAxisView* atv = 0;
982 /* no action if we're recording */
984 if (session && session->actively_recording()) {
988 /* first, see if we're finishing a drag ... */
990 bool were_dragging = false;
992 bool const r = _drag->end_grab (event);
995 cerr << "DRAG DONE, r = " << r << endl;
997 /* grab dragged, so do nothing else */
1001 were_dragging = true;
1004 button_selection (item, event, item_type);
1006 /* edit events get handled here */
1008 if (_drag == 0 && Keyboard::is_edit_event (&event->button)) {
1009 switch (item_type) {
1014 case TempoMarkerItem:
1015 edit_tempo_marker (item);
1018 case MeterMarkerItem:
1019 edit_meter_marker (item);
1022 case RegionViewName:
1023 if (clicked_regionview->name_active()) {
1024 return mouse_rename_region (item, event);
1028 case ControlPointItem:
1029 edit_control_point (item);
1038 /* context menu events get handled here */
1040 if (Keyboard::is_context_menu_event (&event->button)) {
1044 /* no matter which button pops up the context menu, tell the menu
1045 widget to use button 1 to drive menu selection.
1048 switch (item_type) {
1050 case FadeInHandleItem:
1052 case FadeOutHandleItem:
1053 popup_fade_context_menu (1, event->button.time, item, item_type);
1057 popup_track_context_menu (1, event->button.time, item_type, false, where);
1061 case RegionViewNameHighlight:
1062 case RegionViewName:
1063 popup_track_context_menu (1, event->button.time, item_type, false, where);
1067 popup_track_context_menu (1, event->button.time, item_type, true, where);
1070 case AutomationTrackItem:
1071 popup_track_context_menu (1, event->button.time, item_type, false, where);
1075 case RangeMarkerBarItem:
1076 case TransportMarkerBarItem:
1077 case CdMarkerBarItem:
1080 popup_ruler_menu (where, item_type);
1084 marker_context_menu (&event->button, item);
1087 case TempoMarkerItem:
1088 tm_marker_context_menu (&event->button, item);
1091 case MeterMarkerItem:
1092 tm_marker_context_menu (&event->button, item);
1095 case CrossfadeViewItem:
1096 popup_track_context_menu (1, event->button.time, item_type, false, where);
1100 case ImageFrameItem:
1101 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1103 case ImageFrameTimeAxisItem:
1104 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1106 case MarkerViewItem:
1107 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1109 case MarkerTimeAxisItem:
1110 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1122 /* delete events get handled here */
1124 if (_drag == 0 && Keyboard::is_delete_event (&event->button)) {
1126 switch (item_type) {
1127 case TempoMarkerItem:
1128 remove_tempo_marker (item);
1131 case MeterMarkerItem:
1132 remove_meter_marker (item);
1136 remove_marker (*item, event);
1140 if (mouse_mode == MouseObject) {
1141 remove_clicked_region ();
1145 case ControlPointItem:
1146 if (mouse_mode == MouseGain) {
1147 remove_gain_control_point (item, event);
1149 remove_control_point (item, event);
1159 switch (event->button.button) {
1162 switch (item_type) {
1163 /* see comments in button_press_handler */
1164 case PlayheadCursorItem:
1167 case AutomationLineItem:
1168 case StartSelectionTrimItem:
1169 case EndSelectionTrimItem:
1173 if (!_dragging_playhead) {
1174 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1175 snap_to (where, 0, true);
1177 mouse_add_new_marker (where);
1181 case CdMarkerBarItem:
1182 if (!_dragging_playhead) {
1183 // if we get here then a dragged range wasn't done
1184 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1185 snap_to (where, 0, true);
1187 mouse_add_new_marker (where, true);
1192 if (!_dragging_playhead) {
1193 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1196 mouse_add_new_tempo_event (where);
1201 if (!_dragging_playhead) {
1202 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1211 switch (mouse_mode) {
1213 switch (item_type) {
1214 case AutomationTrackItem:
1215 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1217 atv->add_automation_event (item, event, where, event->button.y);
1229 // Gain only makes sense for audio regions
1231 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1235 switch (item_type) {
1237 /* check that we didn't drag before releasing, since
1238 its really annoying to create new control
1239 points when doing this.
1241 if (were_dragging) {
1242 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1247 case AutomationTrackItem:
1248 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1249 add_automation_event (item, event, where, event->button.y);
1258 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1259 if (scrubbing_direction == 0) {
1260 /* no drag, just a click */
1261 switch (item_type) {
1263 play_selected_region ();
1269 /* make sure we stop */
1270 session->request_transport_speed (0.0);
1284 switch (mouse_mode) {
1287 switch (item_type) {
1289 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1291 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1294 // Button2 click is unused
1307 // x_style_paste (where, 1.0);
1327 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1333 if (last_item_entered != item) {
1334 last_item_entered = item;
1335 last_item_entered_n = 0;
1338 switch (item_type) {
1339 case ControlPointItem:
1340 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1341 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1342 cp->set_visible (true);
1346 at_y = cp->get_y ();
1347 cp->item()->i2w (at_x, at_y);
1351 fraction = 1.0 - (cp->get_y() / cp->line().height());
1353 if (is_drawable() && dynamic_cast<ScrubDrag*> (_drag) == 0) {
1354 track_canvas->get_window()->set_cursor (*fader_cursor);
1357 last_item_entered_n++;
1358 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1359 if (last_item_entered_n < 10) {
1360 show_verbose_canvas_cursor ();
1366 if (mouse_mode == MouseGain) {
1367 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1369 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1370 if (is_drawable()) {
1371 track_canvas->get_window()->set_cursor (*fader_cursor);
1376 case AutomationLineItem:
1377 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1379 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1381 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1383 if (is_drawable()) {
1384 track_canvas->get_window()->set_cursor (*fader_cursor);
1389 case RegionViewNameHighlight:
1390 if (is_drawable() && mouse_mode == MouseObject) {
1391 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1395 case StartSelectionTrimItem:
1396 case EndSelectionTrimItem:
1399 case ImageFrameHandleStartItem:
1400 case ImageFrameHandleEndItem:
1401 case MarkerViewHandleStartItem:
1402 case MarkerViewHandleEndItem:
1405 if (is_drawable()) {
1406 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1410 case PlayheadCursorItem:
1411 if (is_drawable()) {
1412 switch (_edit_point) {
1414 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1417 track_canvas->get_window()->set_cursor (*grabber_cursor);
1423 case RegionViewName:
1425 /* when the name is not an active item, the entire name highlight is for trimming */
1427 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1428 if (mouse_mode == MouseObject && is_drawable()) {
1429 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1435 case AutomationTrackItem:
1436 if (is_drawable()) {
1437 Gdk::Cursor *cursor;
1438 switch (mouse_mode) {
1440 cursor = selector_cursor;
1443 cursor = zoom_cursor;
1446 cursor = cross_hair_cursor;
1450 track_canvas->get_window()->set_cursor (*cursor);
1452 AutomationTimeAxisView* atv;
1453 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1454 clear_entered_track = false;
1455 set_entered_track (atv);
1461 case RangeMarkerBarItem:
1462 case TransportMarkerBarItem:
1463 case CdMarkerBarItem:
1466 if (is_drawable()) {
1467 track_canvas->get_window()->set_cursor (*timebar_cursor);
1472 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1475 entered_marker = marker;
1476 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1478 case MeterMarkerItem:
1479 case TempoMarkerItem:
1480 if (is_drawable()) {
1481 track_canvas->get_window()->set_cursor (*timebar_cursor);
1484 case FadeInHandleItem:
1485 case FadeOutHandleItem:
1486 if (mouse_mode == MouseObject) {
1487 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1489 rect->property_fill_color_rgba() = 0;
1490 rect->property_outline_pixels() = 1;
1499 /* second pass to handle entered track status in a comprehensible way.
1502 switch (item_type) {
1504 case AutomationLineItem:
1505 case ControlPointItem:
1506 /* these do not affect the current entered track state */
1507 clear_entered_track = false;
1510 case AutomationTrackItem:
1511 /* handled above already */
1515 set_entered_track (0);
1523 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1532 switch (item_type) {
1533 case ControlPointItem:
1534 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1535 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1536 if (cp->line().npoints() > 1 && !cp->selected()) {
1537 cp->set_visible (false);
1541 if (is_drawable()) {
1542 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1545 hide_verbose_canvas_cursor ();
1548 case RegionViewNameHighlight:
1549 case StartSelectionTrimItem:
1550 case EndSelectionTrimItem:
1551 case PlayheadCursorItem:
1554 case ImageFrameHandleStartItem:
1555 case ImageFrameHandleEndItem:
1556 case MarkerViewHandleStartItem:
1557 case MarkerViewHandleEndItem:
1560 if (is_drawable()) {
1561 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1566 case AutomationLineItem:
1567 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1569 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1571 line->property_fill_color_rgba() = al->get_line_color();
1573 if (is_drawable()) {
1574 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1578 case RegionViewName:
1579 /* see enter_handler() for notes */
1580 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1581 if (is_drawable() && mouse_mode == MouseObject) {
1582 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1587 case RangeMarkerBarItem:
1588 case TransportMarkerBarItem:
1589 case CdMarkerBarItem:
1593 if (is_drawable()) {
1594 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1599 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1603 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1604 location_flags_changed (loc, this);
1607 case MeterMarkerItem:
1608 case TempoMarkerItem:
1610 if (is_drawable()) {
1611 track_canvas->get_window()->set_cursor (*timebar_cursor);
1616 case FadeInHandleItem:
1617 case FadeOutHandleItem:
1618 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1620 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1622 rect->property_fill_color_rgba() = rv->get_fill_color();
1623 rect->property_outline_pixels() = 0;
1628 case AutomationTrackItem:
1629 if (is_drawable()) {
1630 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1631 clear_entered_track = true;
1632 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1644 Editor::left_automation_track ()
1646 if (clear_entered_track) {
1647 set_entered_track (0);
1648 clear_entered_track = false;
1658 if (scrubbing_direction == 0) {
1660 session->request_locate (_drag->current_pointer_frame(), false);
1661 session->request_transport_speed (0.1);
1662 scrubbing_direction = 1;
1666 if (last_scrub_x > _drag->current_pointer_x()) {
1668 /* pointer moved to the left */
1670 if (scrubbing_direction > 0) {
1672 /* we reversed direction to go backwards */
1675 scrub_reverse_distance += (int) (last_scrub_x - _drag->current_pointer_x());
1679 /* still moving to the left (backwards) */
1681 scrub_reversals = 0;
1682 scrub_reverse_distance = 0;
1684 delta = 0.01 * (last_scrub_x - _drag->current_pointer_x());
1685 session->request_transport_speed (session->transport_speed() - delta);
1689 /* pointer moved to the right */
1691 if (scrubbing_direction < 0) {
1692 /* we reversed direction to go forward */
1695 scrub_reverse_distance += (int) (_drag->current_pointer_x() - last_scrub_x);
1698 /* still moving to the right */
1700 scrub_reversals = 0;
1701 scrub_reverse_distance = 0;
1703 delta = 0.01 * (_drag->current_pointer_x() - last_scrub_x);
1704 session->request_transport_speed (session->transport_speed() + delta);
1708 /* if there have been more than 2 opposite motion moves detected, or one that moves
1709 back more than 10 pixels, reverse direction
1712 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1714 if (scrubbing_direction > 0) {
1715 /* was forwards, go backwards */
1716 session->request_transport_speed (-0.1);
1717 scrubbing_direction = -1;
1719 /* was backwards, go forwards */
1720 session->request_transport_speed (0.1);
1721 scrubbing_direction = 1;
1724 scrub_reverse_distance = 0;
1725 scrub_reversals = 0;
1729 last_scrub_x = _drag->current_pointer_x();
1733 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1735 if (event->motion.is_hint) {
1738 /* We call this so that MOTION_NOTIFY events continue to be
1739 delivered to the canvas. We need to do this because we set
1740 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1741 the density of the events, at the expense of a round-trip
1742 to the server. Given that this will mostly occur on cases
1743 where DISPLAY = :0.0, and given the cost of what the motion
1744 event might do, its a good tradeoff.
1747 track_canvas->get_pointer (x, y);
1750 if (current_stepping_trackview) {
1751 /* don't keep the persistent stepped trackview if the mouse moves */
1752 current_stepping_trackview = 0;
1753 step_timeout.disconnect ();
1756 if (session && session->actively_recording()) {
1757 /* Sorry. no dragging stuff around while we record */
1761 bool handled = false;
1763 handled = _drag->motion_handler (event, from_autoscroll);
1770 track_canvas_motion (event);
1775 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1777 ControlPoint* control_point;
1779 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1780 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1784 // We shouldn't remove the first or last gain point
1785 if (control_point->line().is_last_point(*control_point) ||
1786 control_point->line().is_first_point(*control_point)) {
1790 control_point->line().remove_point (*control_point);
1794 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1796 ControlPoint* control_point;
1798 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1799 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1803 control_point->line().remove_point (*control_point);
1807 Editor::edit_control_point (ArdourCanvas::Item* item)
1809 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1812 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1816 ControlPointDialog d (p);
1817 d.set_position (Gtk::WIN_POS_MOUSE);
1820 if (d.run () != RESPONSE_ACCEPT) {
1824 p->line().modify_point_y (*p, d.get_y_fraction ());
1829 Editor::visible_order_range (int* low, int* high) const
1831 *low = TimeAxisView::max_order ();
1834 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1836 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1838 if (!rtv->hidden()) {
1840 if (*high < rtv->order()) {
1841 *high = rtv->order ();
1844 if (*low > rtv->order()) {
1845 *low = rtv->order ();
1852 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1854 /* Either add to or set the set the region selection, unless
1855 this is an alignment click (control used)
1858 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1859 TimeAxisView* tv = &rv.get_time_axis_view();
1860 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1862 if (rtv && rtv->is_track()) {
1863 speed = rtv->get_diskstream()->speed();
1866 nframes64_t where = get_preferred_edit_position();
1870 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1872 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
1874 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1876 align_region (rv.region(), End, (nframes64_t) (where * speed));
1880 align_region (rv.region(), Start, (nframes64_t) (where * speed));
1887 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
1893 nframes64_t frame_rate;
1902 if (Profile->get_sae() || Profile->get_small_screen()) {
1903 m = ARDOUR_UI::instance()->primary_clock.mode();
1905 m = ARDOUR_UI::instance()->secondary_clock.mode();
1909 case AudioClock::BBT:
1910 session->bbt_time (frame, bbt);
1911 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
1914 case AudioClock::SMPTE:
1915 session->smpte_time (frame, smpte);
1916 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
1919 case AudioClock::MinSec:
1920 /* XXX this is copied from show_verbose_duration_cursor() */
1921 frame_rate = session->frame_rate();
1922 hours = frame / (frame_rate * 3600);
1923 frame = frame % (frame_rate * 3600);
1924 mins = frame / (frame_rate * 60);
1925 frame = frame % (frame_rate * 60);
1926 secs = (float) frame / (float) frame_rate;
1927 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
1931 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
1935 if (xpos >= 0 && ypos >=0) {
1936 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
1939 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);
1941 show_verbose_canvas_cursor ();
1945 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
1952 nframes64_t distance, frame_rate;
1954 Meter meter_at_start(session->tempo_map().meter_at(start));
1962 if (Profile->get_sae() || Profile->get_small_screen()) {
1963 m = ARDOUR_UI::instance()->primary_clock.mode ();
1965 m = ARDOUR_UI::instance()->secondary_clock.mode ();
1969 case AudioClock::BBT:
1970 session->bbt_time (start, sbbt);
1971 session->bbt_time (end, ebbt);
1974 /* XXX this computation won't work well if the
1975 user makes a selection that spans any meter changes.
1978 ebbt.bars -= sbbt.bars;
1979 if (ebbt.beats >= sbbt.beats) {
1980 ebbt.beats -= sbbt.beats;
1983 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
1985 if (ebbt.ticks >= sbbt.ticks) {
1986 ebbt.ticks -= sbbt.ticks;
1989 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
1992 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
1995 case AudioClock::SMPTE:
1996 session->smpte_duration (end - start, smpte);
1997 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
2000 case AudioClock::MinSec:
2001 /* XXX this stuff should be elsewhere.. */
2002 distance = end - start;
2003 frame_rate = session->frame_rate();
2004 hours = distance / (frame_rate * 3600);
2005 distance = distance % (frame_rate * 3600);
2006 mins = distance / (frame_rate * 60);
2007 distance = distance % (frame_rate * 60);
2008 secs = (float) distance / (float) frame_rate;
2009 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2013 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2017 if (xpos >= 0 && ypos >=0) {
2018 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2021 set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset, _drag->current_pointer_y() + offset);
2024 show_verbose_canvas_cursor ();
2028 Editor::collect_new_region_view (RegionView* rv)
2030 latest_regionviews.push_back (rv);
2034 Editor::collect_and_select_new_region_view (RegionView* rv)
2037 latest_regionviews.push_back (rv);
2041 Editor::cancel_selection ()
2043 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2044 (*i)->hide_selection ();
2047 selection->clear ();
2048 clicked_selection = 0;
2053 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
2055 boost::shared_ptr<Region> region (rv.region());
2057 if (region->locked()) {
2061 nframes64_t new_bound;
2064 TimeAxisView* tvp = clicked_axisview;
2065 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2067 if (tv && tv->is_track()) {
2068 speed = tv->get_diskstream()->speed();
2071 if (left_direction) {
2072 if (swap_direction) {
2073 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2075 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2078 if (swap_direction) {
2079 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2081 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2086 snap_to (new_bound);
2088 region->trim_start ((nframes64_t) (new_bound * speed), this);
2089 rv.region_changed (StartChanged);
2093 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2095 boost::shared_ptr<Region> region (rv.region());
2097 if (region->locked()) {
2101 nframes64_t new_bound;
2104 TimeAxisView* tvp = clicked_axisview;
2105 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2107 if (tv && tv->is_track()) {
2108 speed = tv->get_diskstream()->speed();
2111 if (left_direction) {
2112 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2114 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2118 snap_to (new_bound, (left_direction ? 0 : 1));
2121 nframes64_t pre_trim_first_frame = region->first_frame();
2123 region->trim_front ((nframes64_t) (new_bound * speed), this);
2126 //Get the next region on the left of this region and shrink/expand it.
2127 boost::shared_ptr<Playlist> playlist (region->playlist());
2128 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2130 bool regions_touching = false;
2132 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2133 regions_touching = true;
2136 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2137 if (region_left != 0 &&
2138 (region_left->last_frame() > region->first_frame() || regions_touching))
2140 region_left->trim_end(region->first_frame(), this);
2146 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2150 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2152 boost::shared_ptr<Region> region (rv.region());
2154 if (region->locked()) {
2158 nframes64_t new_bound;
2161 TimeAxisView* tvp = clicked_axisview;
2162 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2164 if (tv && tv->is_track()) {
2165 speed = tv->get_diskstream()->speed();
2168 if (left_direction) {
2169 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
2171 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
2175 snap_to (new_bound);
2178 nframes64_t pre_trim_last_frame = region->last_frame();
2180 region->trim_end ((nframes64_t) (new_bound * speed), this);
2183 //Get the next region on the right of this region and shrink/expand it.
2184 boost::shared_ptr<Playlist> playlist (region->playlist());
2185 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2187 bool regions_touching = false;
2189 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)){
2190 regions_touching = true;
2193 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2194 if (region_right != 0 &&
2195 (region_right->first_frame() < region->last_frame() || regions_touching))
2197 region_right->trim_front(region->last_frame() + 1, this);
2200 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2203 rv.region_changed (LengthChanged);
2209 Editor::point_trim (GdkEvent* event)
2211 RegionView* rv = clicked_regionview;
2213 nframes64_t new_bound = _drag->current_pointer_frame();
2215 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2216 snap_to (new_bound);
2219 /* Choose action dependant on which button was pressed */
2220 switch (event->button.button) {
2222 begin_reversible_command (_("Start point trim"));
2224 if (selection->selected (rv)) {
2225 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2226 i != selection->regions.by_layer().end(); ++i)
2229 cerr << "region view contains null region" << endl;
2232 if (!(*i)->region()->locked()) {
2233 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2234 XMLNode &before = pl->get_state();
2236 (*i)->region()->trim_front (new_bound, this);
2238 XMLNode &after = pl->get_state();
2239 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2244 if (!rv->region()->locked()) {
2245 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2246 XMLNode &before = pl->get_state();
2247 rv->region()->trim_front (new_bound, this);
2248 XMLNode &after = pl->get_state();
2249 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2253 commit_reversible_command();
2257 begin_reversible_command (_("End point trim"));
2259 if (selection->selected (rv)) {
2261 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2263 if (!(*i)->region()->locked()) {
2264 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2265 XMLNode &before = pl->get_state();
2266 (*i)->region()->trim_end (new_bound, this);
2267 XMLNode &after = pl->get_state();
2268 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2274 if (!rv->region()->locked()) {
2275 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2276 XMLNode &before = pl->get_state();
2277 rv->region()->trim_end (new_bound, this);
2278 XMLNode &after = pl->get_state();
2279 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
2283 commit_reversible_command();
2292 Editor::thaw_region_after_trim (RegionView& rv)
2294 boost::shared_ptr<Region> region (rv.region());
2296 if (region->locked()) {
2300 region->thaw (_("trimmed region"));
2302 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2305 arv->unhide_envelope ();
2310 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2315 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2316 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2320 Location* location = find_location_from_marker (marker, is_start);
2321 location->set_hidden (true, this);
2326 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2328 double x1 = frame_to_pixel (start);
2329 double x2 = frame_to_pixel (end);
2330 double y2 = full_canvas_height - 1.0;
2332 zoom_rect->property_x1() = x1;
2333 zoom_rect->property_y1() = 1.0;
2334 zoom_rect->property_x2() = x2;
2335 zoom_rect->property_y2() = y2;
2340 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2342 using namespace Gtkmm2ext;
2344 ArdourPrompter prompter (false);
2346 prompter.set_prompt (_("Name for region:"));
2347 prompter.set_initial_text (clicked_regionview->region()->name());
2348 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2349 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2350 prompter.show_all ();
2351 switch (prompter.run ()) {
2352 case Gtk::RESPONSE_ACCEPT:
2354 prompter.get_result(str);
2356 clicked_regionview->region()->set_name (str);
2365 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2367 /* no brushing without a useful snap setting */
2369 switch (snap_mode) {
2371 return; /* can't work because it allows region to be placed anywhere */
2376 switch (snap_type) {
2384 /* don't brush a copy over the original */
2386 if (pos == rv->region()->position()) {
2390 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2392 if (rtv == 0 || !rtv->is_track()) {
2396 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2397 double speed = rtv->get_diskstream()->speed();
2399 XMLNode &before = playlist->get_state();
2400 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
2401 XMLNode &after = playlist->get_state();
2402 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
2404 // playlist is frozen, so we have to update manually
2406 playlist->Modified(); /* EMIT SIGNAL */
2410 Editor::track_height_step_timeout ()
2412 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2413 current_stepping_trackview = 0;
2420 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2422 assert (region_view);
2424 _region_motion_group->raise_to_top ();
2426 assert (_drag == 0);
2428 if (Config->get_edit_mode() == Splice) {
2429 _drag = new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer());
2431 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2432 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false);
2435 _drag->start_grab (event);
2437 begin_reversible_command (_("move region(s)"));
2439 /* sync the canvas to what we think is its current state */
2440 update_canvas_now();
2444 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2446 assert (region_view);
2447 assert (_drag == 0);
2449 _region_motion_group->raise_to_top ();
2451 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2452 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true);
2453 _drag->start_grab(event);
2457 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2459 assert (region_view);
2460 assert (_drag == 0);
2462 if (Config->get_edit_mode() == Splice) {
2466 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2467 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false);
2468 _drag->start_grab (event);
2470 begin_reversible_command (_("Drag region brush"));
2473 /** Start a grab where a time range is selected, track(s) are selected, and the
2474 * user clicks and drags a region with a modifier in order to create a new region containing
2475 * the section of the clicked region that lies within the time range.
2478 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2480 if (clicked_regionview == 0) {
2484 /* lets try to create new Region for the selection */
2486 vector<boost::shared_ptr<Region> > new_regions;
2487 create_region_from_selection (new_regions);
2489 if (new_regions.empty()) {
2493 /* XXX fix me one day to use all new regions */
2495 boost::shared_ptr<Region> region (new_regions.front());
2497 /* add it to the current stream/playlist.
2499 tricky: the streamview for the track will add a new regionview. we will
2500 catch the signal it sends when it creates the regionview to
2501 set the regionview we want to then drag.
2504 latest_regionviews.clear();
2505 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2507 /* A selection grab currently creates two undo/redo operations, one for
2508 creating the new region and another for moving it.
2511 begin_reversible_command (_("selection grab"));
2513 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2515 XMLNode *before = &(playlist->get_state());
2516 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2517 XMLNode *after = &(playlist->get_state());
2518 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
2520 commit_reversible_command ();
2524 if (latest_regionviews.empty()) {
2525 /* something went wrong */
2529 /* we need to deselect all other regionviews, and select this one
2530 i'm ignoring undo stuff, because the region creation will take care of it
2532 selection->set (latest_regionviews);
2534 assert (_drag == 0);
2535 _drag = new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false);
2536 _drag->start_grab (event);
2540 Editor::break_drag ()
2543 _drag->break_drag ();
2548 Editor::set_internal_edit (bool yn)
2550 _internal_editing = yn;
2551 set_canvas_cursor ();