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);
305 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
306 tact->set_active (false);
307 tact->set_active (true);
311 Editor::mouse_mode_toggled (MouseMode m)
317 if (mouse_mode != MouseRange) {
319 /* in all modes except range, hide the range selection,
320 show the object (region) selection.
323 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
324 (*i)->set_should_show_selection (true);
326 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
327 (*i)->hide_selection ();
333 in range mode,show the range selection.
336 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
337 if ((*i)->get_selected()) {
338 (*i)->show_selection (selection->time);
343 set_canvas_cursor ();
347 Editor::step_mouse_mode (bool next)
349 switch (current_mouse_mode()) {
352 if (Profile->get_sae()) {
353 set_mouse_mode (MouseZoom);
355 set_mouse_mode (MouseRange);
358 set_mouse_mode (MouseTimeFX);
363 if (next) set_mouse_mode (MouseZoom);
364 else set_mouse_mode (MouseObject);
369 if (Profile->get_sae()) {
370 set_mouse_mode (MouseTimeFX);
372 set_mouse_mode (MouseGain);
375 if (Profile->get_sae()) {
376 set_mouse_mode (MouseObject);
378 set_mouse_mode (MouseRange);
384 if (next) set_mouse_mode (MouseTimeFX);
385 else set_mouse_mode (MouseZoom);
390 set_mouse_mode (MouseAudition);
392 if (Profile->get_sae()) {
393 set_mouse_mode (MouseZoom);
395 set_mouse_mode (MouseGain);
401 if (next) set_mouse_mode (MouseObject);
402 else set_mouse_mode (MouseTimeFX);
408 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
410 /* in object/audition/timefx/gain-automation mode,
411 any button press sets the selection if the object
412 can be selected. this is a bit of hack, because
413 we want to avoid this if the mouse operation is a
416 note: not dbl-click or triple-click
419 if (((mouse_mode != MouseObject) &&
420 (mouse_mode != MouseAudition || item_type != RegionItem) &&
421 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
422 (mouse_mode != MouseGain) &&
423 (mouse_mode != MouseRange)) ||
425 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
430 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
432 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
434 /* almost no selection action on modified button-2 or button-3 events */
436 if (item_type != RegionItem && event->button.button != 2) {
442 Selection::Operation op = Keyboard::selection_type (event->button.state);
443 bool press = (event->type == GDK_BUTTON_PRESS);
445 // begin_reversible_command (_("select on click"));
449 if (mouse_mode != MouseRange) {
450 set_selected_regionview_from_click (press, op, true);
451 } else if (event->type == GDK_BUTTON_PRESS) {
452 set_selected_track_as_side_effect ();
456 case RegionViewNameHighlight:
458 if (mouse_mode != MouseRange) {
459 set_selected_regionview_from_click (press, op, true);
460 } else if (event->type == GDK_BUTTON_PRESS) {
461 set_selected_track_as_side_effect ();
466 case FadeInHandleItem:
468 case FadeOutHandleItem:
470 if (mouse_mode != MouseRange) {
471 set_selected_regionview_from_click (press, op, true);
472 } else if (event->type == GDK_BUTTON_PRESS) {
473 set_selected_track_as_side_effect ();
477 case ControlPointItem:
478 set_selected_track_as_side_effect ();
479 if (mouse_mode != MouseRange) {
480 set_selected_control_point_from_click (op, false);
485 /* for context click or range selection, select track */
486 if (event->button.button == 3) {
487 set_selected_track_as_side_effect ();
488 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
489 set_selected_track_as_side_effect ();
493 case AutomationTrackItem:
494 set_selected_track_as_side_effect (true);
503 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
506 _drag->item()->ungrab (event->button.time);
509 /* single mouse clicks on any of these item types operate
510 independent of mouse mode, mostly because they are
511 not on the main track canvas or because we want
516 case PlayheadCursorItem:
518 _drag = new CursorDrag (this, item, true);
519 _drag->start_grab (event);
523 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
524 hide_marker (item, event);
527 _drag = new MarkerDrag (this, item);
528 _drag->start_grab (event);
532 case TempoMarkerItem:
534 _drag = new TempoMarkerDrag (
537 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
539 _drag->start_grab (event);
542 case MeterMarkerItem:
544 _drag = new MeterMarkerDrag (
547 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
549 _drag->start_grab (event);
555 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
557 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
558 _drag->start_grab (event);
564 case RangeMarkerBarItem:
566 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
567 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
569 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker);
571 _drag->start_grab (event);
575 case CdMarkerBarItem:
577 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
578 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
580 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker);
582 _drag->start_grab (event);
586 case TransportMarkerBarItem:
588 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
589 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
591 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker);
593 _drag->start_grab (event);
601 if (internal_editing()) {
603 _drag = new RegionCreateDrag (this, item, clicked_axisview);
604 _drag->start_grab (event);
606 switch (mouse_mode) {
609 case StartSelectionTrimItem:
611 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim);
612 _drag->start_grab (event);
615 case EndSelectionTrimItem:
617 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim);
618 _drag->start_grab (event);
622 if (Keyboard::modifier_state_contains
623 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
624 // contains and not equals because I can't use alt as a modifier alone.
625 start_selection_grab (item, event);
626 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
627 /* grab selection for moving */
629 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionMove);
630 _drag->start_grab (event);
632 /* this was debated, but decided the more common action was to
633 make a new selection */
635 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
636 _drag->start_grab (event);
642 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
643 _drag->start_grab (event);
649 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
650 event->type == GDK_BUTTON_PRESS) {
653 _drag = new RubberbandSelectDrag (this, item);
654 _drag->start_grab (event);
656 } else if (event->type == GDK_BUTTON_PRESS) {
659 case FadeInHandleItem:
662 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
663 _drag = new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
664 _drag->start_grab (event);
668 case FadeOutHandleItem:
671 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
672 _drag = new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
673 _drag->start_grab (event);
678 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
679 start_region_copy_grab (item, event, clicked_regionview);
680 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
681 start_region_brush_grab (item, event, clicked_regionview);
683 start_region_grab (item, event, clicked_regionview);
687 case RegionViewNameHighlight:
690 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
691 _drag = new TrimDrag (this, item, clicked_regionview, s.by_layer());
692 _drag->start_grab (event);
699 /* rename happens on edit clicks */
701 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
702 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer());
703 _drag->start_grab (event);
708 case ControlPointItem:
710 _drag = new ControlPointDrag (this, item);
711 _drag->start_grab (event);
715 case AutomationLineItem:
717 _drag = new LineDrag (this, item);
718 _drag->start_grab (event);
723 case AutomationTrackItem:
725 _drag = new RubberbandSelectDrag (this, item);
726 _drag->start_grab (event);
730 case ImageFrameHandleStartItem:
731 imageframe_start_handle_op(item, event) ;
734 case ImageFrameHandleEndItem:
735 imageframe_end_handle_op(item, event) ;
738 case MarkerViewHandleStartItem:
739 markerview_item_start_handle_op(item, event) ;
742 case MarkerViewHandleEndItem:
743 markerview_item_end_handle_op(item, event) ;
747 start_markerview_grab(item, event) ;
750 start_imageframe_grab(item, event) ;
768 /* start a grab so that if we finish after moving
769 we can tell what happened.
772 _drag = new RegionGainDrag (this, item);
773 _drag->start_grab (event, current_canvas_cursor);
778 _drag = new LineDrag (this, item);
779 _drag->start_grab (event);
782 case ControlPointItem:
784 _drag = new ControlPointDrag (this, item);
785 _drag->start_grab (event);
796 case ControlPointItem:
798 _drag = new ControlPointDrag (this, item);
799 _drag->start_grab (event);
802 case AutomationLineItem:
804 _drag = new LineDrag (this, item);
805 _drag->start_grab (event);
809 // XXX need automation mode to identify which
811 // start_line_grab_from_regionview (item, event);
821 if (event->type == GDK_BUTTON_PRESS) {
823 _drag = new MouseZoomDrag (this, item);
824 _drag->start_grab (event);
831 if (item_type == RegionItem) {
833 _drag = new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer());
834 _drag->start_grab (event);
839 _drag = new ScrubDrag (this, item);
840 _drag->start_grab (event);
842 scrub_reverse_distance = 0;
843 last_scrub_x = event->button.x;
844 scrubbing_direction = 0;
845 track_canvas->get_window()->set_cursor (*transparent_cursor);
857 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
859 switch (mouse_mode) {
863 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
864 start_region_copy_grab (item, event, clicked_regionview);
866 start_region_grab (item, event, clicked_regionview);
870 case ControlPointItem:
872 _drag = new ControlPointDrag (this, item);
873 _drag->start_grab (event);
882 case RegionViewNameHighlight:
884 _drag = new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer());
885 _drag->start_grab (event);
891 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer());
892 _drag->start_grab (event);
903 /* relax till release */
909 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
910 temporal_zoom_session();
912 temporal_zoom_to_frame (true, event_frame(event));
925 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
927 if (event->type != GDK_BUTTON_PRESS) {
931 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
934 Glib::RefPtr<const Gdk::Window> pointer_window;
937 Gdk::ModifierType mask;
939 pointer_window = canvas_window->get_pointer (x, y, mask);
941 if (pointer_window == track_canvas->get_bin_window()) {
942 track_canvas->window_to_world (x, y, wx, wy);
943 allow_vertical_scroll = true;
945 allow_vertical_scroll = false;
949 track_canvas->grab_focus();
951 if (session && session->actively_recording()) {
955 button_selection (item, event, item_type);
958 (Keyboard::is_delete_event (&event->button) ||
959 Keyboard::is_context_menu_event (&event->button) ||
960 Keyboard::is_edit_event (&event->button))) {
962 /* handled by button release */
966 switch (event->button.button) {
968 return button_press_handler_1 (item, event, item_type);
972 return button_press_handler_2 (item, event, item_type);
987 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
989 nframes64_t where = event_frame (event, 0, 0);
990 AutomationTimeAxisView* atv = 0;
992 /* no action if we're recording */
994 if (session && session->actively_recording()) {
998 /* first, see if we're finishing a drag ... */
1000 bool were_dragging = false;
1002 bool const r = _drag->end_grab (event);
1005 cerr << "DRAG DONE, r = " << r << endl;
1007 /* grab dragged, so do nothing else */
1011 were_dragging = true;
1014 button_selection (item, event, item_type);
1016 /* edit events get handled here */
1018 if (_drag == 0 && Keyboard::is_edit_event (&event->button)) {
1019 switch (item_type) {
1024 case TempoMarkerItem:
1025 edit_tempo_marker (item);
1028 case MeterMarkerItem:
1029 edit_meter_marker (item);
1032 case RegionViewName:
1033 if (clicked_regionview->name_active()) {
1034 return mouse_rename_region (item, event);
1038 case ControlPointItem:
1039 edit_control_point (item);
1048 /* context menu events get handled here */
1050 if (Keyboard::is_context_menu_event (&event->button)) {
1054 /* no matter which button pops up the context menu, tell the menu
1055 widget to use button 1 to drive menu selection.
1058 switch (item_type) {
1060 case FadeInHandleItem:
1062 case FadeOutHandleItem:
1063 popup_fade_context_menu (1, event->button.time, item, item_type);
1067 popup_track_context_menu (1, event->button.time, item_type, false, where);
1071 case RegionViewNameHighlight:
1072 case RegionViewName:
1073 popup_track_context_menu (1, event->button.time, item_type, false, where);
1077 popup_track_context_menu (1, event->button.time, item_type, true, where);
1080 case AutomationTrackItem:
1081 popup_track_context_menu (1, event->button.time, item_type, false, where);
1085 case RangeMarkerBarItem:
1086 case TransportMarkerBarItem:
1087 case CdMarkerBarItem:
1090 popup_ruler_menu (where, item_type);
1094 marker_context_menu (&event->button, item);
1097 case TempoMarkerItem:
1098 tm_marker_context_menu (&event->button, item);
1101 case MeterMarkerItem:
1102 tm_marker_context_menu (&event->button, item);
1105 case CrossfadeViewItem:
1106 popup_track_context_menu (1, event->button.time, item_type, false, where);
1110 case ImageFrameItem:
1111 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1113 case ImageFrameTimeAxisItem:
1114 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1116 case MarkerViewItem:
1117 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1119 case MarkerTimeAxisItem:
1120 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1132 /* delete events get handled here */
1134 if (_drag == 0 && Keyboard::is_delete_event (&event->button)) {
1136 switch (item_type) {
1137 case TempoMarkerItem:
1138 remove_tempo_marker (item);
1141 case MeterMarkerItem:
1142 remove_meter_marker (item);
1146 remove_marker (*item, event);
1150 if (mouse_mode == MouseObject) {
1151 remove_clicked_region ();
1155 case ControlPointItem:
1156 if (mouse_mode == MouseGain) {
1157 remove_gain_control_point (item, event);
1159 remove_control_point (item, event);
1169 switch (event->button.button) {
1172 switch (item_type) {
1173 /* see comments in button_press_handler */
1174 case PlayheadCursorItem:
1177 case AutomationLineItem:
1178 case StartSelectionTrimItem:
1179 case EndSelectionTrimItem:
1183 if (!_dragging_playhead) {
1184 snap_to_with_modifier (where, event, 0, true);
1185 mouse_add_new_marker (where);
1189 case CdMarkerBarItem:
1190 if (!_dragging_playhead) {
1191 // if we get here then a dragged range wasn't done
1192 snap_to_with_modifier (where, event, 0, true);
1193 mouse_add_new_marker (where, true);
1198 if (!_dragging_playhead) {
1199 snap_to_with_modifier (where, event);
1200 mouse_add_new_tempo_event (where);
1205 if (!_dragging_playhead) {
1206 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1215 switch (mouse_mode) {
1217 switch (item_type) {
1218 case AutomationTrackItem:
1219 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1221 atv->add_automation_event (item, event, where, event->button.y);
1233 // Gain only makes sense for audio regions
1235 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1239 switch (item_type) {
1241 /* check that we didn't drag before releasing, since
1242 its really annoying to create new control
1243 points when doing this.
1245 if (were_dragging) {
1246 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1251 case AutomationTrackItem:
1252 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1253 add_automation_event (item, event, where, event->button.y);
1262 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1263 if (scrubbing_direction == 0) {
1264 /* no drag, just a click */
1265 switch (item_type) {
1267 play_selected_region ();
1273 /* make sure we stop */
1274 session->request_transport_speed (0.0);
1288 switch (mouse_mode) {
1291 switch (item_type) {
1293 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1295 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1298 // Button2 click is unused
1311 // x_style_paste (where, 1.0);
1331 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1337 if (last_item_entered != item) {
1338 last_item_entered = item;
1339 last_item_entered_n = 0;
1342 switch (item_type) {
1343 case ControlPointItem:
1344 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1345 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1346 cp->set_visible (true);
1350 at_y = cp->get_y ();
1351 cp->item()->i2w (at_x, at_y);
1355 fraction = 1.0 - (cp->get_y() / cp->line().height());
1357 if (is_drawable() && dynamic_cast<ScrubDrag*> (_drag) == 0) {
1358 track_canvas->get_window()->set_cursor (*fader_cursor);
1361 last_item_entered_n++;
1362 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1363 if (last_item_entered_n < 10) {
1364 show_verbose_canvas_cursor ();
1370 if (mouse_mode == MouseGain) {
1371 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1373 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1374 if (is_drawable()) {
1375 track_canvas->get_window()->set_cursor (*fader_cursor);
1380 case AutomationLineItem:
1381 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1383 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1385 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1387 if (is_drawable()) {
1388 track_canvas->get_window()->set_cursor (*fader_cursor);
1393 case RegionViewNameHighlight:
1394 if (is_drawable() && mouse_mode == MouseObject) {
1395 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1399 case StartSelectionTrimItem:
1400 case EndSelectionTrimItem:
1403 case ImageFrameHandleStartItem:
1404 case ImageFrameHandleEndItem:
1405 case MarkerViewHandleStartItem:
1406 case MarkerViewHandleEndItem:
1409 if (is_drawable()) {
1410 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1414 case PlayheadCursorItem:
1415 if (is_drawable()) {
1416 switch (_edit_point) {
1418 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1421 track_canvas->get_window()->set_cursor (*grabber_cursor);
1427 case RegionViewName:
1429 /* when the name is not an active item, the entire name highlight is for trimming */
1431 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1432 if (mouse_mode == MouseObject && is_drawable()) {
1433 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1439 case AutomationTrackItem:
1440 if (is_drawable()) {
1441 Gdk::Cursor *cursor;
1442 switch (mouse_mode) {
1444 cursor = selector_cursor;
1447 cursor = zoom_cursor;
1450 cursor = cross_hair_cursor;
1454 track_canvas->get_window()->set_cursor (*cursor);
1456 AutomationTimeAxisView* atv;
1457 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1458 clear_entered_track = false;
1459 set_entered_track (atv);
1465 case RangeMarkerBarItem:
1466 case TransportMarkerBarItem:
1467 case CdMarkerBarItem:
1470 if (is_drawable()) {
1471 track_canvas->get_window()->set_cursor (*timebar_cursor);
1476 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1479 entered_marker = marker;
1480 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1482 case MeterMarkerItem:
1483 case TempoMarkerItem:
1484 if (is_drawable()) {
1485 track_canvas->get_window()->set_cursor (*timebar_cursor);
1488 case FadeInHandleItem:
1489 case FadeOutHandleItem:
1490 if (mouse_mode == MouseObject) {
1491 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1493 rect->property_fill_color_rgba() = 0;
1494 rect->property_outline_pixels() = 1;
1503 /* second pass to handle entered track status in a comprehensible way.
1506 switch (item_type) {
1508 case AutomationLineItem:
1509 case ControlPointItem:
1510 /* these do not affect the current entered track state */
1511 clear_entered_track = false;
1514 case AutomationTrackItem:
1515 /* handled above already */
1519 set_entered_track (0);
1527 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1536 switch (item_type) {
1537 case ControlPointItem:
1538 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1539 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1540 if (cp->line().npoints() > 1 && !cp->selected()) {
1541 cp->set_visible (false);
1545 if (is_drawable()) {
1546 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1549 hide_verbose_canvas_cursor ();
1552 case RegionViewNameHighlight:
1553 case StartSelectionTrimItem:
1554 case EndSelectionTrimItem:
1555 case PlayheadCursorItem:
1558 case ImageFrameHandleStartItem:
1559 case ImageFrameHandleEndItem:
1560 case MarkerViewHandleStartItem:
1561 case MarkerViewHandleEndItem:
1564 if (is_drawable()) {
1565 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1570 case AutomationLineItem:
1571 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1573 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1575 line->property_fill_color_rgba() = al->get_line_color();
1577 if (is_drawable()) {
1578 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1582 case RegionViewName:
1583 /* see enter_handler() for notes */
1584 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1585 if (is_drawable() && mouse_mode == MouseObject) {
1586 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1591 case RangeMarkerBarItem:
1592 case TransportMarkerBarItem:
1593 case CdMarkerBarItem:
1597 if (is_drawable()) {
1598 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1603 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1607 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1608 location_flags_changed (loc, this);
1611 case MeterMarkerItem:
1612 case TempoMarkerItem:
1614 if (is_drawable()) {
1615 track_canvas->get_window()->set_cursor (*timebar_cursor);
1620 case FadeInHandleItem:
1621 case FadeOutHandleItem:
1622 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1624 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1626 rect->property_fill_color_rgba() = rv->get_fill_color();
1627 rect->property_outline_pixels() = 0;
1632 case AutomationTrackItem:
1633 if (is_drawable()) {
1634 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1635 clear_entered_track = true;
1636 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1648 Editor::left_automation_track ()
1650 if (clear_entered_track) {
1651 set_entered_track (0);
1652 clear_entered_track = false;
1662 if (scrubbing_direction == 0) {
1664 session->request_locate (_drag->current_pointer_frame(), false);
1665 session->request_transport_speed (0.1);
1666 scrubbing_direction = 1;
1670 if (last_scrub_x > _drag->current_pointer_x()) {
1672 /* pointer moved to the left */
1674 if (scrubbing_direction > 0) {
1676 /* we reversed direction to go backwards */
1679 scrub_reverse_distance += (int) (last_scrub_x - _drag->current_pointer_x());
1683 /* still moving to the left (backwards) */
1685 scrub_reversals = 0;
1686 scrub_reverse_distance = 0;
1688 delta = 0.01 * (last_scrub_x - _drag->current_pointer_x());
1689 session->request_transport_speed (session->transport_speed() - delta);
1693 /* pointer moved to the right */
1695 if (scrubbing_direction < 0) {
1696 /* we reversed direction to go forward */
1699 scrub_reverse_distance += (int) (_drag->current_pointer_x() - last_scrub_x);
1702 /* still moving to the right */
1704 scrub_reversals = 0;
1705 scrub_reverse_distance = 0;
1707 delta = 0.01 * (_drag->current_pointer_x() - last_scrub_x);
1708 session->request_transport_speed (session->transport_speed() + delta);
1712 /* if there have been more than 2 opposite motion moves detected, or one that moves
1713 back more than 10 pixels, reverse direction
1716 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1718 if (scrubbing_direction > 0) {
1719 /* was forwards, go backwards */
1720 session->request_transport_speed (-0.1);
1721 scrubbing_direction = -1;
1723 /* was backwards, go forwards */
1724 session->request_transport_speed (0.1);
1725 scrubbing_direction = 1;
1728 scrub_reverse_distance = 0;
1729 scrub_reversals = 0;
1733 last_scrub_x = _drag->current_pointer_x();
1737 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1739 if (event->motion.is_hint) {
1742 /* We call this so that MOTION_NOTIFY events continue to be
1743 delivered to the canvas. We need to do this because we set
1744 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1745 the density of the events, at the expense of a round-trip
1746 to the server. Given that this will mostly occur on cases
1747 where DISPLAY = :0.0, and given the cost of what the motion
1748 event might do, its a good tradeoff.
1751 track_canvas->get_pointer (x, y);
1754 if (current_stepping_trackview) {
1755 /* don't keep the persistent stepped trackview if the mouse moves */
1756 current_stepping_trackview = 0;
1757 step_timeout.disconnect ();
1760 if (session && session->actively_recording()) {
1761 /* Sorry. no dragging stuff around while we record */
1765 bool handled = false;
1767 handled = _drag->motion_handler (event, from_autoscroll);
1774 track_canvas_motion (event);
1779 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1781 ControlPoint* control_point;
1783 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1784 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1788 // We shouldn't remove the first or last gain point
1789 if (control_point->line().is_last_point(*control_point) ||
1790 control_point->line().is_first_point(*control_point)) {
1794 control_point->line().remove_point (*control_point);
1798 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1800 ControlPoint* control_point;
1802 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1803 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1807 control_point->line().remove_point (*control_point);
1811 Editor::edit_control_point (ArdourCanvas::Item* item)
1813 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1816 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1820 ControlPointDialog d (p);
1821 d.set_position (Gtk::WIN_POS_MOUSE);
1824 if (d.run () != RESPONSE_ACCEPT) {
1828 p->line().modify_point_y (*p, d.get_y_fraction ());
1833 Editor::visible_order_range (int* low, int* high) const
1835 *low = TimeAxisView::max_order ();
1838 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1840 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1842 if (!rtv->hidden()) {
1844 if (*high < rtv->order()) {
1845 *high = rtv->order ();
1848 if (*low > rtv->order()) {
1849 *low = rtv->order ();
1856 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1858 /* Either add to or set the set the region selection, unless
1859 this is an alignment click (control used)
1862 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1863 TimeAxisView* tv = &rv.get_time_axis_view();
1864 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1866 if (rtv && rtv->is_track()) {
1867 speed = rtv->get_diskstream()->speed();
1870 nframes64_t where = get_preferred_edit_position();
1874 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1876 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
1878 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1880 align_region (rv.region(), End, (nframes64_t) (where * speed));
1884 align_region (rv.region(), Start, (nframes64_t) (where * speed));
1891 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
1897 nframes64_t frame_rate;
1906 if (Profile->get_sae() || Profile->get_small_screen()) {
1907 m = ARDOUR_UI::instance()->primary_clock.mode();
1909 m = ARDOUR_UI::instance()->secondary_clock.mode();
1913 case AudioClock::BBT:
1914 session->bbt_time (frame, bbt);
1915 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
1918 case AudioClock::SMPTE:
1919 session->smpte_time (frame, smpte);
1920 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
1923 case AudioClock::MinSec:
1924 /* XXX this is copied from show_verbose_duration_cursor() */
1925 frame_rate = session->frame_rate();
1926 hours = frame / (frame_rate * 3600);
1927 frame = frame % (frame_rate * 3600);
1928 mins = frame / (frame_rate * 60);
1929 frame = frame % (frame_rate * 60);
1930 secs = (float) frame / (float) frame_rate;
1931 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
1935 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
1939 if (xpos >= 0 && ypos >=0) {
1940 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
1943 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);
1945 show_verbose_canvas_cursor ();
1949 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
1956 nframes64_t distance, frame_rate;
1958 Meter meter_at_start(session->tempo_map().meter_at(start));
1966 if (Profile->get_sae() || Profile->get_small_screen()) {
1967 m = ARDOUR_UI::instance()->primary_clock.mode ();
1969 m = ARDOUR_UI::instance()->secondary_clock.mode ();
1973 case AudioClock::BBT:
1974 session->bbt_time (start, sbbt);
1975 session->bbt_time (end, ebbt);
1978 /* XXX this computation won't work well if the
1979 user makes a selection that spans any meter changes.
1982 ebbt.bars -= sbbt.bars;
1983 if (ebbt.beats >= sbbt.beats) {
1984 ebbt.beats -= sbbt.beats;
1987 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
1989 if (ebbt.ticks >= sbbt.ticks) {
1990 ebbt.ticks -= sbbt.ticks;
1993 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
1996 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
1999 case AudioClock::SMPTE:
2000 session->smpte_duration (end - start, smpte);
2001 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
2004 case AudioClock::MinSec:
2005 /* XXX this stuff should be elsewhere.. */
2006 distance = end - start;
2007 frame_rate = session->frame_rate();
2008 hours = distance / (frame_rate * 3600);
2009 distance = distance % (frame_rate * 3600);
2010 mins = distance / (frame_rate * 60);
2011 distance = distance % (frame_rate * 60);
2012 secs = (float) distance / (float) frame_rate;
2013 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2017 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2021 if (xpos >= 0 && ypos >=0) {
2022 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2025 set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset, _drag->current_pointer_y() + offset);
2028 show_verbose_canvas_cursor ();
2032 Editor::collect_new_region_view (RegionView* rv)
2034 latest_regionviews.push_back (rv);
2038 Editor::collect_and_select_new_region_view (RegionView* rv)
2041 latest_regionviews.push_back (rv);
2045 Editor::cancel_selection ()
2047 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2048 (*i)->hide_selection ();
2051 selection->clear ();
2052 clicked_selection = 0;
2057 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
2059 boost::shared_ptr<Region> region (rv.region());
2061 if (region->locked()) {
2065 nframes64_t new_bound;
2068 TimeAxisView* tvp = clicked_axisview;
2069 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2071 if (tv && tv->is_track()) {
2072 speed = tv->get_diskstream()->speed();
2075 if (left_direction) {
2076 if (swap_direction) {
2077 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2079 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2082 if (swap_direction) {
2083 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2085 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2090 snap_to (new_bound);
2092 region->trim_start ((nframes64_t) (new_bound * speed), this);
2093 rv.region_changed (StartChanged);
2097 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2099 boost::shared_ptr<Region> region (rv.region());
2101 if (region->locked()) {
2105 nframes64_t new_bound;
2108 TimeAxisView* tvp = clicked_axisview;
2109 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2111 if (tv && tv->is_track()) {
2112 speed = tv->get_diskstream()->speed();
2115 if (left_direction) {
2116 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2118 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2122 snap_to (new_bound, (left_direction ? 0 : 1));
2125 nframes64_t pre_trim_first_frame = region->first_frame();
2127 region->trim_front ((nframes64_t) (new_bound * speed), this);
2130 //Get the next region on the left of this region and shrink/expand it.
2131 boost::shared_ptr<Playlist> playlist (region->playlist());
2132 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2134 bool regions_touching = false;
2136 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2137 regions_touching = true;
2140 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2141 if (region_left != 0 &&
2142 (region_left->last_frame() > region->first_frame() || regions_touching))
2144 region_left->trim_end(region->first_frame(), this);
2150 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2154 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2156 boost::shared_ptr<Region> region (rv.region());
2158 if (region->locked()) {
2162 nframes64_t new_bound;
2165 TimeAxisView* tvp = clicked_axisview;
2166 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2168 if (tv && tv->is_track()) {
2169 speed = tv->get_diskstream()->speed();
2172 if (left_direction) {
2173 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
2175 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
2179 snap_to (new_bound);
2182 nframes64_t pre_trim_last_frame = region->last_frame();
2184 region->trim_end ((nframes64_t) (new_bound * speed), this);
2187 //Get the next region on the right of this region and shrink/expand it.
2188 boost::shared_ptr<Playlist> playlist (region->playlist());
2189 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2191 bool regions_touching = false;
2193 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)){
2194 regions_touching = true;
2197 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2198 if (region_right != 0 &&
2199 (region_right->first_frame() < region->last_frame() || regions_touching))
2201 region_right->trim_front(region->last_frame() + 1, this);
2204 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2207 rv.region_changed (LengthChanged);
2213 Editor::point_trim (GdkEvent* event)
2215 RegionView* rv = clicked_regionview;
2217 nframes64_t new_bound = _drag->current_pointer_frame();
2219 snap_to_with_modifier (new_bound, event);
2221 /* Choose action dependant on which button was pressed */
2222 switch (event->button.button) {
2224 begin_reversible_command (_("Start point trim"));
2226 if (selection->selected (rv)) {
2227 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2228 i != selection->regions.by_layer().end(); ++i)
2231 cerr << "region view contains null region" << endl;
2234 if (!(*i)->region()->locked()) {
2235 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2236 XMLNode &before = pl->get_state();
2238 (*i)->region()->trim_front (new_bound, this);
2240 XMLNode &after = pl->get_state();
2241 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2246 if (!rv->region()->locked()) {
2247 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2248 XMLNode &before = pl->get_state();
2249 rv->region()->trim_front (new_bound, this);
2250 XMLNode &after = pl->get_state();
2251 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2255 commit_reversible_command();
2259 begin_reversible_command (_("End point trim"));
2261 if (selection->selected (rv)) {
2263 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2265 if (!(*i)->region()->locked()) {
2266 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2267 XMLNode &before = pl->get_state();
2268 (*i)->region()->trim_end (new_bound, this);
2269 XMLNode &after = pl->get_state();
2270 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2276 if (!rv->region()->locked()) {
2277 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2278 XMLNode &before = pl->get_state();
2279 rv->region()->trim_end (new_bound, this);
2280 XMLNode &after = pl->get_state();
2281 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
2285 commit_reversible_command();
2294 Editor::thaw_region_after_trim (RegionView& rv)
2296 boost::shared_ptr<Region> region (rv.region());
2298 if (region->locked()) {
2302 region->thaw (_("trimmed region"));
2304 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2307 arv->unhide_envelope ();
2312 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2317 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2318 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2322 Location* location = find_location_from_marker (marker, is_start);
2323 location->set_hidden (true, this);
2328 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2330 double x1 = frame_to_pixel (start);
2331 double x2 = frame_to_pixel (end);
2332 double y2 = full_canvas_height - 1.0;
2334 zoom_rect->property_x1() = x1;
2335 zoom_rect->property_y1() = 1.0;
2336 zoom_rect->property_x2() = x2;
2337 zoom_rect->property_y2() = y2;
2342 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2344 using namespace Gtkmm2ext;
2346 ArdourPrompter prompter (false);
2348 prompter.set_prompt (_("Name for region:"));
2349 prompter.set_initial_text (clicked_regionview->region()->name());
2350 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2351 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2352 prompter.show_all ();
2353 switch (prompter.run ()) {
2354 case Gtk::RESPONSE_ACCEPT:
2356 prompter.get_result(str);
2358 clicked_regionview->region()->set_name (str);
2367 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2369 /* no brushing without a useful snap setting */
2371 switch (snap_mode) {
2373 return; /* can't work because it allows region to be placed anywhere */
2378 switch (snap_type) {
2386 /* don't brush a copy over the original */
2388 if (pos == rv->region()->position()) {
2392 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2394 if (rtv == 0 || !rtv->is_track()) {
2398 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2399 double speed = rtv->get_diskstream()->speed();
2401 XMLNode &before = playlist->get_state();
2402 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
2403 XMLNode &after = playlist->get_state();
2404 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
2406 // playlist is frozen, so we have to update manually
2408 playlist->Modified(); /* EMIT SIGNAL */
2412 Editor::track_height_step_timeout ()
2414 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2415 current_stepping_trackview = 0;
2422 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2424 assert (region_view);
2426 _region_motion_group->raise_to_top ();
2428 assert (_drag == 0);
2430 if (Config->get_edit_mode() == Splice) {
2431 _drag = new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer());
2433 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2434 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false);
2437 _drag->start_grab (event);
2439 begin_reversible_command (_("move region(s)"));
2441 /* sync the canvas to what we think is its current state */
2442 update_canvas_now();
2446 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2448 assert (region_view);
2449 assert (_drag == 0);
2451 _region_motion_group->raise_to_top ();
2453 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2454 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true);
2455 _drag->start_grab(event);
2459 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2461 assert (region_view);
2462 assert (_drag == 0);
2464 if (Config->get_edit_mode() == Splice) {
2468 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2469 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false);
2470 _drag->start_grab (event);
2472 begin_reversible_command (_("Drag region brush"));
2475 /** Start a grab where a time range is selected, track(s) are selected, and the
2476 * user clicks and drags a region with a modifier in order to create a new region containing
2477 * the section of the clicked region that lies within the time range.
2480 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2482 if (clicked_regionview == 0) {
2486 /* lets try to create new Region for the selection */
2488 vector<boost::shared_ptr<Region> > new_regions;
2489 create_region_from_selection (new_regions);
2491 if (new_regions.empty()) {
2495 /* XXX fix me one day to use all new regions */
2497 boost::shared_ptr<Region> region (new_regions.front());
2499 /* add it to the current stream/playlist.
2501 tricky: the streamview for the track will add a new regionview. we will
2502 catch the signal it sends when it creates the regionview to
2503 set the regionview we want to then drag.
2506 latest_regionviews.clear();
2507 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2509 /* A selection grab currently creates two undo/redo operations, one for
2510 creating the new region and another for moving it.
2513 begin_reversible_command (_("selection grab"));
2515 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2517 XMLNode *before = &(playlist->get_state());
2518 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2519 XMLNode *after = &(playlist->get_state());
2520 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
2522 commit_reversible_command ();
2526 if (latest_regionviews.empty()) {
2527 /* something went wrong */
2531 /* we need to deselect all other regionviews, and select this one
2532 i'm ignoring undo stuff, because the region creation will take care of it
2534 selection->set (latest_regionviews);
2536 assert (_drag == 0);
2537 _drag = new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false);
2538 _drag->start_grab (event);
2542 Editor::break_drag ()
2545 _drag->break_drag ();
2550 Editor::set_internal_edit (bool yn)
2552 _internal_editing = yn;
2555 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_select")))));
2556 mouse_move_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2558 mouse_select_button.set_image (*(manage (new Image (::get_xpm("tool_range.xpm")))));
2559 mouse_move_button.set_image (*(manage (new Image (::get_icon("tool_object")))));
2562 set_canvas_cursor ();