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 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1185 snap_to (where, 0, true);
1187 mouse_add_new_marker (where);
1191 case CdMarkerBarItem:
1192 if (!_dragging_playhead) {
1193 // if we get here then a dragged range wasn't done
1194 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1195 snap_to (where, 0, true);
1197 mouse_add_new_marker (where, true);
1202 if (!_dragging_playhead) {
1203 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1206 mouse_add_new_tempo_event (where);
1211 if (!_dragging_playhead) {
1212 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1221 switch (mouse_mode) {
1223 switch (item_type) {
1224 case AutomationTrackItem:
1225 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1227 atv->add_automation_event (item, event, where, event->button.y);
1239 // Gain only makes sense for audio regions
1241 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1245 switch (item_type) {
1247 /* check that we didn't drag before releasing, since
1248 its really annoying to create new control
1249 points when doing this.
1251 if (were_dragging) {
1252 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1257 case AutomationTrackItem:
1258 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1259 add_automation_event (item, event, where, event->button.y);
1268 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1269 if (scrubbing_direction == 0) {
1270 /* no drag, just a click */
1271 switch (item_type) {
1273 play_selected_region ();
1279 /* make sure we stop */
1280 session->request_transport_speed (0.0);
1294 switch (mouse_mode) {
1297 switch (item_type) {
1299 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1301 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1304 // Button2 click is unused
1317 // x_style_paste (where, 1.0);
1337 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1343 if (last_item_entered != item) {
1344 last_item_entered = item;
1345 last_item_entered_n = 0;
1348 switch (item_type) {
1349 case ControlPointItem:
1350 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1351 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1352 cp->set_visible (true);
1356 at_y = cp->get_y ();
1357 cp->item()->i2w (at_x, at_y);
1361 fraction = 1.0 - (cp->get_y() / cp->line().height());
1363 if (is_drawable() && dynamic_cast<ScrubDrag*> (_drag) == 0) {
1364 track_canvas->get_window()->set_cursor (*fader_cursor);
1367 last_item_entered_n++;
1368 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1369 if (last_item_entered_n < 10) {
1370 show_verbose_canvas_cursor ();
1376 if (mouse_mode == MouseGain) {
1377 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1379 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1380 if (is_drawable()) {
1381 track_canvas->get_window()->set_cursor (*fader_cursor);
1386 case AutomationLineItem:
1387 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1389 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1391 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1393 if (is_drawable()) {
1394 track_canvas->get_window()->set_cursor (*fader_cursor);
1399 case RegionViewNameHighlight:
1400 if (is_drawable() && mouse_mode == MouseObject) {
1401 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1405 case StartSelectionTrimItem:
1406 case EndSelectionTrimItem:
1409 case ImageFrameHandleStartItem:
1410 case ImageFrameHandleEndItem:
1411 case MarkerViewHandleStartItem:
1412 case MarkerViewHandleEndItem:
1415 if (is_drawable()) {
1416 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1420 case PlayheadCursorItem:
1421 if (is_drawable()) {
1422 switch (_edit_point) {
1424 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1427 track_canvas->get_window()->set_cursor (*grabber_cursor);
1433 case RegionViewName:
1435 /* when the name is not an active item, the entire name highlight is for trimming */
1437 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1438 if (mouse_mode == MouseObject && is_drawable()) {
1439 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1445 case AutomationTrackItem:
1446 if (is_drawable()) {
1447 Gdk::Cursor *cursor;
1448 switch (mouse_mode) {
1450 cursor = selector_cursor;
1453 cursor = zoom_cursor;
1456 cursor = cross_hair_cursor;
1460 track_canvas->get_window()->set_cursor (*cursor);
1462 AutomationTimeAxisView* atv;
1463 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1464 clear_entered_track = false;
1465 set_entered_track (atv);
1471 case RangeMarkerBarItem:
1472 case TransportMarkerBarItem:
1473 case CdMarkerBarItem:
1476 if (is_drawable()) {
1477 track_canvas->get_window()->set_cursor (*timebar_cursor);
1482 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1485 entered_marker = marker;
1486 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1488 case MeterMarkerItem:
1489 case TempoMarkerItem:
1490 if (is_drawable()) {
1491 track_canvas->get_window()->set_cursor (*timebar_cursor);
1494 case FadeInHandleItem:
1495 case FadeOutHandleItem:
1496 if (mouse_mode == MouseObject) {
1497 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1499 rect->property_fill_color_rgba() = 0;
1500 rect->property_outline_pixels() = 1;
1509 /* second pass to handle entered track status in a comprehensible way.
1512 switch (item_type) {
1514 case AutomationLineItem:
1515 case ControlPointItem:
1516 /* these do not affect the current entered track state */
1517 clear_entered_track = false;
1520 case AutomationTrackItem:
1521 /* handled above already */
1525 set_entered_track (0);
1533 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1542 switch (item_type) {
1543 case ControlPointItem:
1544 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1545 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1546 if (cp->line().npoints() > 1 && !cp->selected()) {
1547 cp->set_visible (false);
1551 if (is_drawable()) {
1552 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1555 hide_verbose_canvas_cursor ();
1558 case RegionViewNameHighlight:
1559 case StartSelectionTrimItem:
1560 case EndSelectionTrimItem:
1561 case PlayheadCursorItem:
1564 case ImageFrameHandleStartItem:
1565 case ImageFrameHandleEndItem:
1566 case MarkerViewHandleStartItem:
1567 case MarkerViewHandleEndItem:
1570 if (is_drawable()) {
1571 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1576 case AutomationLineItem:
1577 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1579 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1581 line->property_fill_color_rgba() = al->get_line_color();
1583 if (is_drawable()) {
1584 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1588 case RegionViewName:
1589 /* see enter_handler() for notes */
1590 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1591 if (is_drawable() && mouse_mode == MouseObject) {
1592 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1597 case RangeMarkerBarItem:
1598 case TransportMarkerBarItem:
1599 case CdMarkerBarItem:
1603 if (is_drawable()) {
1604 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1609 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1613 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1614 location_flags_changed (loc, this);
1617 case MeterMarkerItem:
1618 case TempoMarkerItem:
1620 if (is_drawable()) {
1621 track_canvas->get_window()->set_cursor (*timebar_cursor);
1626 case FadeInHandleItem:
1627 case FadeOutHandleItem:
1628 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1630 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1632 rect->property_fill_color_rgba() = rv->get_fill_color();
1633 rect->property_outline_pixels() = 0;
1638 case AutomationTrackItem:
1639 if (is_drawable()) {
1640 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1641 clear_entered_track = true;
1642 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1654 Editor::left_automation_track ()
1656 if (clear_entered_track) {
1657 set_entered_track (0);
1658 clear_entered_track = false;
1668 if (scrubbing_direction == 0) {
1670 session->request_locate (_drag->current_pointer_frame(), false);
1671 session->request_transport_speed (0.1);
1672 scrubbing_direction = 1;
1676 if (last_scrub_x > _drag->current_pointer_x()) {
1678 /* pointer moved to the left */
1680 if (scrubbing_direction > 0) {
1682 /* we reversed direction to go backwards */
1685 scrub_reverse_distance += (int) (last_scrub_x - _drag->current_pointer_x());
1689 /* still moving to the left (backwards) */
1691 scrub_reversals = 0;
1692 scrub_reverse_distance = 0;
1694 delta = 0.01 * (last_scrub_x - _drag->current_pointer_x());
1695 session->request_transport_speed (session->transport_speed() - delta);
1699 /* pointer moved to the right */
1701 if (scrubbing_direction < 0) {
1702 /* we reversed direction to go forward */
1705 scrub_reverse_distance += (int) (_drag->current_pointer_x() - last_scrub_x);
1708 /* still moving to the right */
1710 scrub_reversals = 0;
1711 scrub_reverse_distance = 0;
1713 delta = 0.01 * (_drag->current_pointer_x() - last_scrub_x);
1714 session->request_transport_speed (session->transport_speed() + delta);
1718 /* if there have been more than 2 opposite motion moves detected, or one that moves
1719 back more than 10 pixels, reverse direction
1722 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1724 if (scrubbing_direction > 0) {
1725 /* was forwards, go backwards */
1726 session->request_transport_speed (-0.1);
1727 scrubbing_direction = -1;
1729 /* was backwards, go forwards */
1730 session->request_transport_speed (0.1);
1731 scrubbing_direction = 1;
1734 scrub_reverse_distance = 0;
1735 scrub_reversals = 0;
1739 last_scrub_x = _drag->current_pointer_x();
1743 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1745 if (event->motion.is_hint) {
1748 /* We call this so that MOTION_NOTIFY events continue to be
1749 delivered to the canvas. We need to do this because we set
1750 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1751 the density of the events, at the expense of a round-trip
1752 to the server. Given that this will mostly occur on cases
1753 where DISPLAY = :0.0, and given the cost of what the motion
1754 event might do, its a good tradeoff.
1757 track_canvas->get_pointer (x, y);
1760 if (current_stepping_trackview) {
1761 /* don't keep the persistent stepped trackview if the mouse moves */
1762 current_stepping_trackview = 0;
1763 step_timeout.disconnect ();
1766 if (session && session->actively_recording()) {
1767 /* Sorry. no dragging stuff around while we record */
1771 bool handled = false;
1773 handled = _drag->motion_handler (event, from_autoscroll);
1780 track_canvas_motion (event);
1785 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1787 ControlPoint* control_point;
1789 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1790 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1794 // We shouldn't remove the first or last gain point
1795 if (control_point->line().is_last_point(*control_point) ||
1796 control_point->line().is_first_point(*control_point)) {
1800 control_point->line().remove_point (*control_point);
1804 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1806 ControlPoint* control_point;
1808 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1809 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1813 control_point->line().remove_point (*control_point);
1817 Editor::edit_control_point (ArdourCanvas::Item* item)
1819 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1822 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1826 ControlPointDialog d (p);
1827 d.set_position (Gtk::WIN_POS_MOUSE);
1830 if (d.run () != RESPONSE_ACCEPT) {
1834 p->line().modify_point_y (*p, d.get_y_fraction ());
1839 Editor::visible_order_range (int* low, int* high) const
1841 *low = TimeAxisView::max_order ();
1844 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1846 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1848 if (!rtv->hidden()) {
1850 if (*high < rtv->order()) {
1851 *high = rtv->order ();
1854 if (*low > rtv->order()) {
1855 *low = rtv->order ();
1862 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1864 /* Either add to or set the set the region selection, unless
1865 this is an alignment click (control used)
1868 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1869 TimeAxisView* tv = &rv.get_time_axis_view();
1870 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1872 if (rtv && rtv->is_track()) {
1873 speed = rtv->get_diskstream()->speed();
1876 nframes64_t where = get_preferred_edit_position();
1880 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1882 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
1884 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1886 align_region (rv.region(), End, (nframes64_t) (where * speed));
1890 align_region (rv.region(), Start, (nframes64_t) (where * speed));
1897 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
1903 nframes64_t frame_rate;
1912 if (Profile->get_sae() || Profile->get_small_screen()) {
1913 m = ARDOUR_UI::instance()->primary_clock.mode();
1915 m = ARDOUR_UI::instance()->secondary_clock.mode();
1919 case AudioClock::BBT:
1920 session->bbt_time (frame, bbt);
1921 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
1924 case AudioClock::SMPTE:
1925 session->smpte_time (frame, smpte);
1926 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
1929 case AudioClock::MinSec:
1930 /* XXX this is copied from show_verbose_duration_cursor() */
1931 frame_rate = session->frame_rate();
1932 hours = frame / (frame_rate * 3600);
1933 frame = frame % (frame_rate * 3600);
1934 mins = frame / (frame_rate * 60);
1935 frame = frame % (frame_rate * 60);
1936 secs = (float) frame / (float) frame_rate;
1937 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
1941 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
1945 if (xpos >= 0 && ypos >=0) {
1946 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
1949 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);
1951 show_verbose_canvas_cursor ();
1955 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
1962 nframes64_t distance, frame_rate;
1964 Meter meter_at_start(session->tempo_map().meter_at(start));
1972 if (Profile->get_sae() || Profile->get_small_screen()) {
1973 m = ARDOUR_UI::instance()->primary_clock.mode ();
1975 m = ARDOUR_UI::instance()->secondary_clock.mode ();
1979 case AudioClock::BBT:
1980 session->bbt_time (start, sbbt);
1981 session->bbt_time (end, ebbt);
1984 /* XXX this computation won't work well if the
1985 user makes a selection that spans any meter changes.
1988 ebbt.bars -= sbbt.bars;
1989 if (ebbt.beats >= sbbt.beats) {
1990 ebbt.beats -= sbbt.beats;
1993 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
1995 if (ebbt.ticks >= sbbt.ticks) {
1996 ebbt.ticks -= sbbt.ticks;
1999 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2002 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2005 case AudioClock::SMPTE:
2006 session->smpte_duration (end - start, smpte);
2007 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
2010 case AudioClock::MinSec:
2011 /* XXX this stuff should be elsewhere.. */
2012 distance = end - start;
2013 frame_rate = session->frame_rate();
2014 hours = distance / (frame_rate * 3600);
2015 distance = distance % (frame_rate * 3600);
2016 mins = distance / (frame_rate * 60);
2017 distance = distance % (frame_rate * 60);
2018 secs = (float) distance / (float) frame_rate;
2019 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2023 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2027 if (xpos >= 0 && ypos >=0) {
2028 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2031 set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset, _drag->current_pointer_y() + offset);
2034 show_verbose_canvas_cursor ();
2038 Editor::collect_new_region_view (RegionView* rv)
2040 latest_regionviews.push_back (rv);
2044 Editor::collect_and_select_new_region_view (RegionView* rv)
2047 latest_regionviews.push_back (rv);
2051 Editor::cancel_selection ()
2053 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2054 (*i)->hide_selection ();
2057 selection->clear ();
2058 clicked_selection = 0;
2063 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
2065 boost::shared_ptr<Region> region (rv.region());
2067 if (region->locked()) {
2071 nframes64_t new_bound;
2074 TimeAxisView* tvp = clicked_axisview;
2075 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2077 if (tv && tv->is_track()) {
2078 speed = tv->get_diskstream()->speed();
2081 if (left_direction) {
2082 if (swap_direction) {
2083 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2085 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2088 if (swap_direction) {
2089 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2091 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2096 snap_to (new_bound);
2098 region->trim_start ((nframes64_t) (new_bound * speed), this);
2099 rv.region_changed (StartChanged);
2103 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2105 boost::shared_ptr<Region> region (rv.region());
2107 if (region->locked()) {
2111 nframes64_t new_bound;
2114 TimeAxisView* tvp = clicked_axisview;
2115 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2117 if (tv && tv->is_track()) {
2118 speed = tv->get_diskstream()->speed();
2121 if (left_direction) {
2122 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2124 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2128 snap_to (new_bound, (left_direction ? 0 : 1));
2131 nframes64_t pre_trim_first_frame = region->first_frame();
2133 region->trim_front ((nframes64_t) (new_bound * speed), this);
2136 //Get the next region on the left of this region and shrink/expand it.
2137 boost::shared_ptr<Playlist> playlist (region->playlist());
2138 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2140 bool regions_touching = false;
2142 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2143 regions_touching = true;
2146 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2147 if (region_left != 0 &&
2148 (region_left->last_frame() > region->first_frame() || regions_touching))
2150 region_left->trim_end(region->first_frame(), this);
2156 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2160 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2162 boost::shared_ptr<Region> region (rv.region());
2164 if (region->locked()) {
2168 nframes64_t new_bound;
2171 TimeAxisView* tvp = clicked_axisview;
2172 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2174 if (tv && tv->is_track()) {
2175 speed = tv->get_diskstream()->speed();
2178 if (left_direction) {
2179 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
2181 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
2185 snap_to (new_bound);
2188 nframes64_t pre_trim_last_frame = region->last_frame();
2190 region->trim_end ((nframes64_t) (new_bound * speed), this);
2193 //Get the next region on the right of this region and shrink/expand it.
2194 boost::shared_ptr<Playlist> playlist (region->playlist());
2195 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2197 bool regions_touching = false;
2199 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)){
2200 regions_touching = true;
2203 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2204 if (region_right != 0 &&
2205 (region_right->first_frame() < region->last_frame() || regions_touching))
2207 region_right->trim_front(region->last_frame() + 1, this);
2210 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2213 rv.region_changed (LengthChanged);
2219 Editor::point_trim (GdkEvent* event)
2221 RegionView* rv = clicked_regionview;
2223 nframes64_t new_bound = _drag->current_pointer_frame();
2225 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2226 snap_to (new_bound);
2229 /* Choose action dependant on which button was pressed */
2230 switch (event->button.button) {
2232 begin_reversible_command (_("Start point trim"));
2234 if (selection->selected (rv)) {
2235 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2236 i != selection->regions.by_layer().end(); ++i)
2239 cerr << "region view contains null region" << endl;
2242 if (!(*i)->region()->locked()) {
2243 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2244 XMLNode &before = pl->get_state();
2246 (*i)->region()->trim_front (new_bound, this);
2248 XMLNode &after = pl->get_state();
2249 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2254 if (!rv->region()->locked()) {
2255 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2256 XMLNode &before = pl->get_state();
2257 rv->region()->trim_front (new_bound, this);
2258 XMLNode &after = pl->get_state();
2259 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2263 commit_reversible_command();
2267 begin_reversible_command (_("End point trim"));
2269 if (selection->selected (rv)) {
2271 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2273 if (!(*i)->region()->locked()) {
2274 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2275 XMLNode &before = pl->get_state();
2276 (*i)->region()->trim_end (new_bound, this);
2277 XMLNode &after = pl->get_state();
2278 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2284 if (!rv->region()->locked()) {
2285 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2286 XMLNode &before = pl->get_state();
2287 rv->region()->trim_end (new_bound, this);
2288 XMLNode &after = pl->get_state();
2289 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
2293 commit_reversible_command();
2302 Editor::thaw_region_after_trim (RegionView& rv)
2304 boost::shared_ptr<Region> region (rv.region());
2306 if (region->locked()) {
2310 region->thaw (_("trimmed region"));
2312 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2315 arv->unhide_envelope ();
2320 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2325 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2326 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2330 Location* location = find_location_from_marker (marker, is_start);
2331 location->set_hidden (true, this);
2336 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2338 double x1 = frame_to_pixel (start);
2339 double x2 = frame_to_pixel (end);
2340 double y2 = full_canvas_height - 1.0;
2342 zoom_rect->property_x1() = x1;
2343 zoom_rect->property_y1() = 1.0;
2344 zoom_rect->property_x2() = x2;
2345 zoom_rect->property_y2() = y2;
2350 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2352 using namespace Gtkmm2ext;
2354 ArdourPrompter prompter (false);
2356 prompter.set_prompt (_("Name for region:"));
2357 prompter.set_initial_text (clicked_regionview->region()->name());
2358 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2359 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2360 prompter.show_all ();
2361 switch (prompter.run ()) {
2362 case Gtk::RESPONSE_ACCEPT:
2364 prompter.get_result(str);
2366 clicked_regionview->region()->set_name (str);
2375 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2377 /* no brushing without a useful snap setting */
2379 switch (snap_mode) {
2381 return; /* can't work because it allows region to be placed anywhere */
2386 switch (snap_type) {
2394 /* don't brush a copy over the original */
2396 if (pos == rv->region()->position()) {
2400 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2402 if (rtv == 0 || !rtv->is_track()) {
2406 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2407 double speed = rtv->get_diskstream()->speed();
2409 XMLNode &before = playlist->get_state();
2410 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
2411 XMLNode &after = playlist->get_state();
2412 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
2414 // playlist is frozen, so we have to update manually
2416 playlist->Modified(); /* EMIT SIGNAL */
2420 Editor::track_height_step_timeout ()
2422 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2423 current_stepping_trackview = 0;
2430 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2432 assert (region_view);
2434 _region_motion_group->raise_to_top ();
2436 assert (_drag == 0);
2438 if (Config->get_edit_mode() == Splice) {
2439 _drag = new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer());
2441 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2442 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false);
2445 _drag->start_grab (event);
2447 begin_reversible_command (_("move region(s)"));
2449 /* sync the canvas to what we think is its current state */
2450 update_canvas_now();
2454 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2456 assert (region_view);
2457 assert (_drag == 0);
2459 _region_motion_group->raise_to_top ();
2461 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2462 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true);
2463 _drag->start_grab(event);
2467 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2469 assert (region_view);
2470 assert (_drag == 0);
2472 if (Config->get_edit_mode() == Splice) {
2476 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2477 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false);
2478 _drag->start_grab (event);
2480 begin_reversible_command (_("Drag region brush"));
2483 /** Start a grab where a time range is selected, track(s) are selected, and the
2484 * user clicks and drags a region with a modifier in order to create a new region containing
2485 * the section of the clicked region that lies within the time range.
2488 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2490 if (clicked_regionview == 0) {
2494 /* lets try to create new Region for the selection */
2496 vector<boost::shared_ptr<Region> > new_regions;
2497 create_region_from_selection (new_regions);
2499 if (new_regions.empty()) {
2503 /* XXX fix me one day to use all new regions */
2505 boost::shared_ptr<Region> region (new_regions.front());
2507 /* add it to the current stream/playlist.
2509 tricky: the streamview for the track will add a new regionview. we will
2510 catch the signal it sends when it creates the regionview to
2511 set the regionview we want to then drag.
2514 latest_regionviews.clear();
2515 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2517 /* A selection grab currently creates two undo/redo operations, one for
2518 creating the new region and another for moving it.
2521 begin_reversible_command (_("selection grab"));
2523 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2525 XMLNode *before = &(playlist->get_state());
2526 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2527 XMLNode *after = &(playlist->get_state());
2528 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
2530 commit_reversible_command ();
2534 if (latest_regionviews.empty()) {
2535 /* something went wrong */
2539 /* we need to deselect all other regionviews, and select this one
2540 i'm ignoring undo stuff, because the region creation will take care of it
2542 selection->set (latest_regionviews);
2544 assert (_drag == 0);
2545 _drag = new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false);
2546 _drag->start_grab (event);
2550 Editor::break_drag ()
2553 _drag->break_drag ();
2558 Editor::set_internal_edit (bool yn)
2560 _internal_editing = yn;
2561 set_canvas_cursor ();