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"
34 #include "pbd/stateful_diff_command.h"
36 #include "ardour_ui.h"
39 #include "time_axis_view.h"
40 #include "audio_time_axis.h"
41 #include "audio_region_view.h"
42 #include "midi_region_view.h"
44 #include "streamview.h"
45 #include "region_gain_line.h"
46 #include "automation_time_axis.h"
47 #include "control_point.h"
50 #include "selection.h"
53 #include "rgb_macros.h"
54 #include "control_point_dialog.h"
55 #include "editor_drag.h"
57 #include "ardour/types.h"
58 #include "ardour/profile.h"
59 #include "ardour/route.h"
60 #include "ardour/audio_track.h"
61 #include "ardour/audio_diskstream.h"
62 #include "ardour/midi_diskstream.h"
63 #include "ardour/playlist.h"
64 #include "ardour/audioplaylist.h"
65 #include "ardour/audioregion.h"
66 #include "ardour/midi_region.h"
67 #include "ardour/dB.h"
68 #include "ardour/utils.h"
69 #include "ardour/region_factory.h"
70 #include "ardour/source_factory.h"
71 #include "ardour/session.h"
78 using namespace ARDOUR;
81 using namespace Editing;
82 using Gtkmm2ext::Keyboard;
85 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
89 Gdk::ModifierType mask;
90 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
91 Glib::RefPtr<const Gdk::Window> pointer_window;
97 pointer_window = canvas_window->get_pointer (x, y, mask);
99 if (pointer_window == track_canvas->get_bin_window()) {
102 in_track_canvas = true;
105 in_track_canvas = false;
110 event.type = GDK_BUTTON_RELEASE;
114 where = event_frame (&event, 0, 0);
119 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
133 switch (event->type) {
134 case GDK_BUTTON_RELEASE:
135 case GDK_BUTTON_PRESS:
136 case GDK_2BUTTON_PRESS:
137 case GDK_3BUTTON_PRESS:
138 *pcx = event->button.x;
139 *pcy = event->button.y;
140 _trackview_group->w2i(*pcx, *pcy);
142 case GDK_MOTION_NOTIFY:
143 *pcx = event->motion.x;
144 *pcy = event->motion.y;
145 _trackview_group->w2i(*pcx, *pcy);
147 case GDK_ENTER_NOTIFY:
148 case GDK_LEAVE_NOTIFY:
149 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
152 case GDK_KEY_RELEASE:
153 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
156 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
160 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
161 position is negative (as can be the case with motion events in particular),
162 the frame location is always positive.
165 return pixel_to_frame (*pcx);
169 Editor::which_grabber_cursor ()
171 Gdk::Cursor* c = grabber_cursor;
173 if (_internal_editing) {
174 switch (mouse_mode) {
176 c = midi_pencil_cursor;
184 c = midi_resize_cursor;
193 switch (_edit_point) {
195 c = grabber_edit_point_cursor;
206 Editor::set_canvas_cursor ()
208 if (_internal_editing) {
210 switch (mouse_mode) {
212 current_canvas_cursor = midi_pencil_cursor;
216 current_canvas_cursor = which_grabber_cursor();
220 current_canvas_cursor = midi_resize_cursor;
229 switch (mouse_mode) {
231 current_canvas_cursor = selector_cursor;
235 current_canvas_cursor = which_grabber_cursor();
239 current_canvas_cursor = cross_hair_cursor;
243 current_canvas_cursor = zoom_cursor;
247 current_canvas_cursor = time_fx_cursor; // just use playhead
251 current_canvas_cursor = speaker_cursor;
256 switch (_join_object_range_state) {
257 case JOIN_OBJECT_RANGE_NONE:
259 case JOIN_OBJECT_RANGE_OBJECT:
260 current_canvas_cursor = which_grabber_cursor ();
262 case JOIN_OBJECT_RANGE_RANGE:
263 current_canvas_cursor = selector_cursor;
268 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
273 Editor::set_mouse_mode (MouseMode m, bool force)
275 if (_drags->active ()) {
279 if (!force && m == mouse_mode) {
283 Glib::RefPtr<Action> act;
287 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
291 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
295 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
299 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
303 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
307 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
313 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
316 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
317 tact->set_active (false);
318 tact->set_active (true);
322 Editor::mouse_mode_toggled (MouseMode m)
328 if (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
330 /* in all modes except range and joined object/range, hide the range selection,
331 show the object (region) selection.
334 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
335 (*i)->set_should_show_selection (true);
337 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
338 (*i)->hide_selection ();
344 in range or object/range mode, show the range selection.
347 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
348 (*i)->show_selection (selection->time);
352 set_canvas_cursor ();
356 Editor::step_mouse_mode (bool next)
358 switch (current_mouse_mode()) {
361 if (Profile->get_sae()) {
362 set_mouse_mode (MouseZoom);
364 set_mouse_mode (MouseRange);
367 set_mouse_mode (MouseTimeFX);
372 if (next) set_mouse_mode (MouseZoom);
373 else set_mouse_mode (MouseObject);
378 if (Profile->get_sae()) {
379 set_mouse_mode (MouseTimeFX);
381 set_mouse_mode (MouseGain);
384 if (Profile->get_sae()) {
385 set_mouse_mode (MouseObject);
387 set_mouse_mode (MouseRange);
393 if (next) set_mouse_mode (MouseTimeFX);
394 else set_mouse_mode (MouseZoom);
399 set_mouse_mode (MouseAudition);
401 if (Profile->get_sae()) {
402 set_mouse_mode (MouseZoom);
404 set_mouse_mode (MouseGain);
410 if (next) set_mouse_mode (MouseObject);
411 else set_mouse_mode (MouseTimeFX);
417 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
419 /* in object/audition/timefx/gain-automation mode,
420 any button press sets the selection if the object
421 can be selected. this is a bit of hack, because
422 we want to avoid this if the mouse operation is a
425 note: not dbl-click or triple-click
428 if (((mouse_mode != MouseObject) &&
429 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
430 (mouse_mode != MouseAudition || item_type != RegionItem) &&
431 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
432 (mouse_mode != MouseGain) &&
433 (mouse_mode != MouseRange)) ||
435 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
440 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
442 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
444 /* almost no selection action on modified button-2 or button-3 events */
446 if (item_type != RegionItem && event->button.button != 2) {
452 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
453 bool press = (event->type == GDK_BUTTON_PRESS);
455 // begin_reversible_command (_("select on click"));
459 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
460 set_selected_regionview_from_click (press, op, true);
461 } else if (event->type == GDK_BUTTON_PRESS) {
462 selection->clear_tracks ();
463 set_selected_track_as_side_effect (true);
465 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
466 clicked_selection = select_range_around_region (selection->regions.front());
471 case RegionViewNameHighlight:
473 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
474 set_selected_regionview_from_click (press, op, true);
475 } else if (event->type == GDK_BUTTON_PRESS) {
476 set_selected_track_as_side_effect ();
481 case FadeInHandleItem:
483 case FadeOutHandleItem:
485 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
486 set_selected_regionview_from_click (press, op, true);
487 } else if (event->type == GDK_BUTTON_PRESS) {
488 set_selected_track_as_side_effect ();
492 case ControlPointItem:
493 set_selected_track_as_side_effect ();
494 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
495 set_selected_control_point_from_click (op, false);
500 /* for context click, select track */
501 if (event->button.button == 3) {
502 set_selected_track_as_side_effect ();
506 case AutomationTrackItem:
507 set_selected_track_as_side_effect (true);
516 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
518 if (_drags->active ()) {
522 /* single mouse clicks on any of these item types operate
523 independent of mouse mode, mostly because they are
524 not on the main track canvas or because we want
529 case PlayheadCursorItem:
530 _drags->set (new CursorDrag (this, item, true), event);
534 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
535 hide_marker (item, event);
537 _drags->set (new MarkerDrag (this, item), event);
541 case TempoMarkerItem:
543 new TempoMarkerDrag (
546 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
552 case MeterMarkerItem:
554 new MeterMarkerDrag (
557 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
566 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
567 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
573 case RangeMarkerBarItem:
574 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
575 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
577 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
582 case CdMarkerBarItem:
583 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
584 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
586 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
591 case TransportMarkerBarItem:
592 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
593 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
595 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
604 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
605 /* special case: allow trim of range selections in joined object mode;
606 in theory eff should equal MouseRange in this case, but it doesn't
607 because entering the range selection canvas item results in entered_regionview
608 being set to 0, so update_join_object_range_location acts as if we aren't
611 if (item_type == StartSelectionTrimItem) {
612 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
613 } else if (item_type == EndSelectionTrimItem) {
614 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
618 Editing::MouseMode eff = effective_mouse_mode ();
620 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
621 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
628 case StartSelectionTrimItem:
629 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
632 case EndSelectionTrimItem:
633 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
637 if (Keyboard::modifier_state_contains
638 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
639 // contains and not equals because I can't use alt as a modifier alone.
640 start_selection_grab (item, event);
641 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
642 /* grab selection for moving */
643 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
645 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
646 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
648 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
649 if (join_object_range_button.get_active() && atv) {
650 /* smart "join" mode: drag automation */
651 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
653 /* this was debated, but decided the more common action was to
654 make a new selection */
655 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
662 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
670 if (internal_editing()) {
671 /* Note: we don't get here if not in internal_editing() mode */
672 _drags->set (new NoteDrag (this, item), event);
681 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
682 event->type == GDK_BUTTON_PRESS) {
684 _drags->set (new RubberbandSelectDrag (this, item), event);
686 } else if (event->type == GDK_BUTTON_PRESS) {
689 case FadeInHandleItem:
691 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
692 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
696 case FadeOutHandleItem:
698 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
699 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
704 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
705 add_region_copy_drag (item, event, clicked_regionview);
706 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
707 add_region_brush_drag (item, event, clicked_regionview);
709 add_region_drag (item, event, clicked_regionview);
712 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
713 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
716 _drags->start_grab (event);
719 case RegionViewNameHighlight:
721 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
722 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
729 /* rename happens on edit clicks */
730 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
731 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
736 case ControlPointItem:
737 _drags->set (new ControlPointDrag (this, item), event);
741 case AutomationLineItem:
742 _drags->set (new LineDrag (this, item), event);
747 if (internal_editing()) {
748 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
751 _drags->set (new RubberbandSelectDrag (this, item), event);
755 case AutomationTrackItem:
756 /* rubberband drag to select automation points */
757 _drags->set (new RubberbandSelectDrag (this, item), event);
762 if (join_object_range_button.get_active()) {
763 /* we're in "smart" joined mode, and we've clicked on a Selection */
764 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
765 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
767 /* if we're over an automation track, start a drag of its data */
768 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
770 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
773 /* if we're over a track and a region, and in the `object' part of a region,
774 put a selection around the region and drag both
776 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
777 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
778 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
780 boost::shared_ptr<Playlist> pl = t->diskstream()->playlist ();
783 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
785 RegionView* rv = rtv->view()->find_view (r);
786 clicked_selection = select_range_around_region (rv);
787 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
788 list<RegionView*> rvs;
790 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
791 _drags->start_grab (event);
802 case ImageFrameHandleStartItem:
803 imageframe_start_handle_op(item, event) ;
806 case ImageFrameHandleEndItem:
807 imageframe_end_handle_op(item, event) ;
810 case MarkerViewHandleStartItem:
811 markerview_item_start_handle_op(item, event) ;
814 case MarkerViewHandleEndItem:
815 markerview_item_end_handle_op(item, event) ;
819 start_markerview_grab(item, event) ;
822 start_imageframe_grab(item, event) ;
840 /* start a grab so that if we finish after moving
841 we can tell what happened.
843 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
847 _drags->set (new LineDrag (this, item), event);
850 case ControlPointItem:
851 _drags->set (new ControlPointDrag (this, item), event);
862 case ControlPointItem:
863 _drags->set (new ControlPointDrag (this, item), event);
866 case AutomationLineItem:
867 _drags->set (new LineDrag (this, item), event);
871 // XXX need automation mode to identify which
873 // start_line_grab_from_regionview (item, event);
883 if (event->type == GDK_BUTTON_PRESS) {
884 _drags->set (new MouseZoomDrag (this, item), event);
891 if (internal_editing() && item_type == NoteItem) {
892 _drags->set (new NoteResizeDrag (this, item), event);
894 } else if (!internal_editing() && item_type == RegionItem) {
895 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
901 _drags->set (new ScrubDrag (this, item), event);
903 scrub_reverse_distance = 0;
904 last_scrub_x = event->button.x;
905 scrubbing_direction = 0;
906 track_canvas->get_window()->set_cursor (*transparent_cursor);
918 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
920 Editing::MouseMode const eff = effective_mouse_mode ();
925 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
926 add_region_copy_drag (item, event, clicked_regionview);
928 add_region_drag (item, event, clicked_regionview);
930 _drags->start_grab (event);
933 case ControlPointItem:
934 _drags->set (new ControlPointDrag (this, item), event);
943 case RegionViewNameHighlight:
944 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
949 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
960 /* relax till release */
966 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
967 temporal_zoom_session();
969 temporal_zoom_to_frame (true, event_frame(event));
982 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
984 if (event->type != GDK_BUTTON_PRESS) {
988 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
991 Glib::RefPtr<const Gdk::Window> pointer_window;
994 Gdk::ModifierType mask;
996 pointer_window = canvas_window->get_pointer (x, y, mask);
998 if (pointer_window == track_canvas->get_bin_window()) {
999 track_canvas->window_to_world (x, y, wx, wy);
1000 allow_vertical_scroll = true;
1002 allow_vertical_scroll = false;
1006 track_canvas->grab_focus();
1008 if (_session && _session->actively_recording()) {
1012 button_selection (item, event, item_type);
1014 if (!_drags->active () &&
1015 (Keyboard::is_delete_event (&event->button) ||
1016 Keyboard::is_context_menu_event (&event->button) ||
1017 Keyboard::is_edit_event (&event->button))) {
1019 /* handled by button release */
1023 switch (event->button.button) {
1025 return button_press_handler_1 (item, event, item_type);
1029 return button_press_handler_2 (item, event, item_type);
1044 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1046 nframes64_t where = event_frame (event, 0, 0);
1047 AutomationTimeAxisView* atv = 0;
1049 /* no action if we're recording */
1051 if (_session && _session->actively_recording()) {
1055 /* first, see if we're finishing a drag ... */
1057 bool were_dragging = false;
1058 if (_drags->active ()) {
1059 bool const r = _drags->end_grab (event);
1061 /* grab dragged, so do nothing else */
1065 were_dragging = true;
1068 button_selection (item, event, item_type);
1070 /* edit events get handled here */
1072 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1073 switch (item_type) {
1078 case TempoMarkerItem:
1079 edit_tempo_marker (item);
1082 case MeterMarkerItem:
1083 edit_meter_marker (item);
1086 case RegionViewName:
1087 if (clicked_regionview->name_active()) {
1088 return mouse_rename_region (item, event);
1092 case ControlPointItem:
1093 edit_control_point (item);
1102 /* context menu events get handled here */
1104 if (Keyboard::is_context_menu_event (&event->button)) {
1106 if (!_drags->active ()) {
1108 /* no matter which button pops up the context menu, tell the menu
1109 widget to use button 1 to drive menu selection.
1112 switch (item_type) {
1114 case FadeInHandleItem:
1116 case FadeOutHandleItem:
1117 popup_fade_context_menu (1, event->button.time, item, item_type);
1121 popup_track_context_menu (1, event->button.time, item_type, false, where);
1125 case RegionViewNameHighlight:
1126 case RegionViewName:
1127 popup_track_context_menu (1, event->button.time, item_type, false, where);
1131 popup_track_context_menu (1, event->button.time, item_type, true, where);
1134 case AutomationTrackItem:
1135 popup_track_context_menu (1, event->button.time, item_type, false, where);
1139 case RangeMarkerBarItem:
1140 case TransportMarkerBarItem:
1141 case CdMarkerBarItem:
1144 popup_ruler_menu (where, item_type);
1148 marker_context_menu (&event->button, item);
1151 case TempoMarkerItem:
1152 tm_marker_context_menu (&event->button, item);
1155 case MeterMarkerItem:
1156 tm_marker_context_menu (&event->button, item);
1159 case CrossfadeViewItem:
1160 popup_track_context_menu (1, event->button.time, item_type, false, where);
1164 case ImageFrameItem:
1165 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1167 case ImageFrameTimeAxisItem:
1168 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1170 case MarkerViewItem:
1171 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1173 case MarkerTimeAxisItem:
1174 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1186 /* delete events get handled here */
1188 Editing::MouseMode const eff = effective_mouse_mode ();
1190 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1192 switch (item_type) {
1193 case TempoMarkerItem:
1194 remove_tempo_marker (item);
1197 case MeterMarkerItem:
1198 remove_meter_marker (item);
1202 remove_marker (*item, event);
1206 if (eff == MouseObject) {
1207 remove_clicked_region ();
1211 case ControlPointItem:
1212 if (eff == MouseGain) {
1213 remove_gain_control_point (item, event);
1215 remove_control_point (item, event);
1225 switch (event->button.button) {
1228 switch (item_type) {
1229 /* see comments in button_press_handler */
1230 case PlayheadCursorItem:
1233 case AutomationLineItem:
1234 case StartSelectionTrimItem:
1235 case EndSelectionTrimItem:
1239 if (!_dragging_playhead) {
1240 snap_to_with_modifier (where, event, 0, true);
1241 mouse_add_new_marker (where);
1245 case CdMarkerBarItem:
1246 if (!_dragging_playhead) {
1247 // if we get here then a dragged range wasn't done
1248 snap_to_with_modifier (where, event, 0, true);
1249 mouse_add_new_marker (where, true);
1254 if (!_dragging_playhead) {
1255 snap_to_with_modifier (where, event);
1256 mouse_add_new_tempo_event (where);
1261 if (!_dragging_playhead) {
1262 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1273 switch (item_type) {
1274 case AutomationTrackItem:
1275 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1277 atv->add_automation_event (item, event, where, event->button.y);
1288 // Gain only makes sense for audio regions
1290 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1294 switch (item_type) {
1296 /* check that we didn't drag before releasing, since
1297 its really annoying to create new control
1298 points when doing this.
1300 if (were_dragging) {
1301 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1306 case AutomationTrackItem:
1307 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1308 add_automation_event (item, event, where, event->button.y);
1317 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1318 if (scrubbing_direction == 0) {
1319 /* no drag, just a click */
1320 switch (item_type) {
1322 play_selected_region ();
1328 /* make sure we stop */
1329 _session->request_transport_speed (0.0);
1346 switch (item_type) {
1348 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1350 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1353 // Button2 click is unused
1366 // x_style_paste (where, 1.0);
1386 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1392 if (last_item_entered != item) {
1393 last_item_entered = item;
1394 last_item_entered_n = 0;
1397 switch (item_type) {
1398 case ControlPointItem:
1399 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1400 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1401 cp->set_visible (true);
1405 at_y = cp->get_y ();
1406 cp->i2w (at_x, at_y);
1410 fraction = 1.0 - (cp->get_y() / cp->line().height());
1412 if (is_drawable() && !_drags->active ()) {
1413 track_canvas->get_window()->set_cursor (*fader_cursor);
1416 last_item_entered_n++;
1417 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1418 if (last_item_entered_n < 10) {
1419 show_verbose_canvas_cursor ();
1425 if (mouse_mode == MouseGain) {
1426 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1428 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1429 if (is_drawable()) {
1430 track_canvas->get_window()->set_cursor (*fader_cursor);
1435 case AutomationLineItem:
1436 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1438 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1440 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1442 if (is_drawable()) {
1443 track_canvas->get_window()->set_cursor (*fader_cursor);
1448 case RegionViewNameHighlight:
1449 if (is_drawable() && mouse_mode == MouseObject) {
1450 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1454 case StartSelectionTrimItem:
1455 case EndSelectionTrimItem:
1458 case ImageFrameHandleStartItem:
1459 case ImageFrameHandleEndItem:
1460 case MarkerViewHandleStartItem:
1461 case MarkerViewHandleEndItem:
1464 if (is_drawable()) {
1465 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1469 case PlayheadCursorItem:
1470 if (is_drawable()) {
1471 switch (_edit_point) {
1473 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1476 track_canvas->get_window()->set_cursor (*grabber_cursor);
1482 case RegionViewName:
1484 /* when the name is not an active item, the entire name highlight is for trimming */
1486 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1487 if (mouse_mode == MouseObject && is_drawable()) {
1488 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1494 case AutomationTrackItem:
1495 if (is_drawable()) {
1496 Gdk::Cursor *cursor;
1497 switch (mouse_mode) {
1499 cursor = selector_cursor;
1502 cursor = zoom_cursor;
1505 cursor = cross_hair_cursor;
1509 track_canvas->get_window()->set_cursor (*cursor);
1511 AutomationTimeAxisView* atv;
1512 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1513 clear_entered_track = false;
1514 set_entered_track (atv);
1520 case RangeMarkerBarItem:
1521 case TransportMarkerBarItem:
1522 case CdMarkerBarItem:
1525 if (is_drawable()) {
1526 track_canvas->get_window()->set_cursor (*timebar_cursor);
1531 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1534 entered_marker = marker;
1535 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1537 case MeterMarkerItem:
1538 case TempoMarkerItem:
1539 if (is_drawable()) {
1540 track_canvas->get_window()->set_cursor (*timebar_cursor);
1543 case FadeInHandleItem:
1544 case FadeOutHandleItem:
1545 if (mouse_mode == MouseObject) {
1546 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1548 rect->property_fill_color_rgba() = 0;
1549 rect->property_outline_pixels() = 1;
1551 track_canvas->get_window()->set_cursor (*grabber_cursor);
1559 /* second pass to handle entered track status in a comprehensible way.
1562 switch (item_type) {
1564 case AutomationLineItem:
1565 case ControlPointItem:
1566 /* these do not affect the current entered track state */
1567 clear_entered_track = false;
1570 case AutomationTrackItem:
1571 /* handled above already */
1575 set_entered_track (0);
1583 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1592 switch (item_type) {
1593 case ControlPointItem:
1594 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1595 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1596 if (cp->line().npoints() > 1 && !cp->selected()) {
1597 cp->set_visible (false);
1601 if (is_drawable()) {
1602 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1605 hide_verbose_canvas_cursor ();
1608 case RegionViewNameHighlight:
1609 case StartSelectionTrimItem:
1610 case EndSelectionTrimItem:
1611 case PlayheadCursorItem:
1614 case ImageFrameHandleStartItem:
1615 case ImageFrameHandleEndItem:
1616 case MarkerViewHandleStartItem:
1617 case MarkerViewHandleEndItem:
1620 if (is_drawable()) {
1621 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1626 case AutomationLineItem:
1627 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1629 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1631 line->property_fill_color_rgba() = al->get_line_color();
1633 if (is_drawable()) {
1634 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1638 case RegionViewName:
1639 /* see enter_handler() for notes */
1640 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1641 if (is_drawable() && mouse_mode == MouseObject) {
1642 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1647 case RangeMarkerBarItem:
1648 case TransportMarkerBarItem:
1649 case CdMarkerBarItem:
1653 if (is_drawable()) {
1654 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1659 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1663 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1664 location_flags_changed (loc, this);
1667 case MeterMarkerItem:
1668 case TempoMarkerItem:
1670 if (is_drawable()) {
1671 track_canvas->get_window()->set_cursor (*timebar_cursor);
1676 case FadeInHandleItem:
1677 case FadeOutHandleItem:
1678 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1680 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1682 rect->property_fill_color_rgba() = rv->get_fill_color();
1683 rect->property_outline_pixels() = 0;
1686 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1689 case AutomationTrackItem:
1690 if (is_drawable()) {
1691 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1692 clear_entered_track = true;
1693 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1705 Editor::left_automation_track ()
1707 if (clear_entered_track) {
1708 set_entered_track (0);
1709 clear_entered_track = false;
1715 Editor::scrub (nframes64_t frame, double current_x)
1719 if (scrubbing_direction == 0) {
1721 _session->request_locate (frame, false);
1722 _session->request_transport_speed (0.1);
1723 scrubbing_direction = 1;
1727 if (last_scrub_x > current_x) {
1729 /* pointer moved to the left */
1731 if (scrubbing_direction > 0) {
1733 /* we reversed direction to go backwards */
1736 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1740 /* still moving to the left (backwards) */
1742 scrub_reversals = 0;
1743 scrub_reverse_distance = 0;
1745 delta = 0.01 * (last_scrub_x - current_x);
1746 _session->request_transport_speed (_session->transport_speed() - delta);
1750 /* pointer moved to the right */
1752 if (scrubbing_direction < 0) {
1753 /* we reversed direction to go forward */
1756 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1759 /* still moving to the right */
1761 scrub_reversals = 0;
1762 scrub_reverse_distance = 0;
1764 delta = 0.01 * (current_x - last_scrub_x);
1765 _session->request_transport_speed (_session->transport_speed() + delta);
1769 /* if there have been more than 2 opposite motion moves detected, or one that moves
1770 back more than 10 pixels, reverse direction
1773 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1775 if (scrubbing_direction > 0) {
1776 /* was forwards, go backwards */
1777 _session->request_transport_speed (-0.1);
1778 scrubbing_direction = -1;
1780 /* was backwards, go forwards */
1781 _session->request_transport_speed (0.1);
1782 scrubbing_direction = 1;
1785 scrub_reverse_distance = 0;
1786 scrub_reversals = 0;
1790 last_scrub_x = current_x;
1794 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1796 if (event->motion.is_hint) {
1799 /* We call this so that MOTION_NOTIFY events continue to be
1800 delivered to the canvas. We need to do this because we set
1801 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1802 the density of the events, at the expense of a round-trip
1803 to the server. Given that this will mostly occur on cases
1804 where DISPLAY = :0.0, and given the cost of what the motion
1805 event might do, its a good tradeoff.
1808 track_canvas->get_pointer (x, y);
1811 if (current_stepping_trackview) {
1812 /* don't keep the persistent stepped trackview if the mouse moves */
1813 current_stepping_trackview = 0;
1814 step_timeout.disconnect ();
1817 if (_session && _session->actively_recording()) {
1818 /* Sorry. no dragging stuff around while we record */
1822 JoinObjectRangeState const old = _join_object_range_state;
1823 update_join_object_range_location (event->motion.x, event->motion.y);
1824 if (_join_object_range_state != old) {
1825 set_canvas_cursor ();
1828 bool handled = false;
1829 if (_drags->active ()) {
1830 handled = _drags->motion_handler (event, from_autoscroll);
1837 track_canvas_motion (event);
1842 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1844 ControlPoint* control_point;
1846 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1847 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1851 // We shouldn't remove the first or last gain point
1852 if (control_point->line().is_last_point(*control_point) ||
1853 control_point->line().is_first_point(*control_point)) {
1857 control_point->line().remove_point (*control_point);
1861 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1863 ControlPoint* control_point;
1865 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1866 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1870 control_point->line().remove_point (*control_point);
1874 Editor::edit_control_point (ArdourCanvas::Item* item)
1876 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1879 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1883 ControlPointDialog d (p);
1884 d.set_position (Gtk::WIN_POS_MOUSE);
1887 if (d.run () != RESPONSE_ACCEPT) {
1891 p->line().modify_point_y (*p, d.get_y_fraction ());
1896 Editor::visible_order_range (int* low, int* high) const
1898 *low = TimeAxisView::max_order ();
1901 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1903 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1905 if (!rtv->hidden()) {
1907 if (*high < rtv->order()) {
1908 *high = rtv->order ();
1911 if (*low > rtv->order()) {
1912 *low = rtv->order ();
1919 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1921 /* Either add to or set the set the region selection, unless
1922 this is an alignment click (control used)
1925 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1926 TimeAxisView* tv = &rv.get_time_axis_view();
1927 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1929 if (rtv && rtv->is_track()) {
1930 speed = rtv->get_diskstream()->speed();
1933 nframes64_t where = get_preferred_edit_position();
1937 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1939 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
1941 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1943 align_region (rv.region(), End, (nframes64_t) (where * speed));
1947 align_region (rv.region(), Start, (nframes64_t) (where * speed));
1954 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
1957 Timecode::Time timecode;
1960 nframes64_t frame_rate;
1963 if (_session == 0) {
1969 if (Profile->get_sae() || Profile->get_small_screen()) {
1970 m = ARDOUR_UI::instance()->primary_clock.mode();
1972 m = ARDOUR_UI::instance()->secondary_clock.mode();
1976 case AudioClock::BBT:
1977 _session->bbt_time (frame, bbt);
1978 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
1981 case AudioClock::Timecode:
1982 _session->timecode_time (frame, timecode);
1983 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1986 case AudioClock::MinSec:
1987 /* XXX this is copied from show_verbose_duration_cursor() */
1988 frame_rate = _session->frame_rate();
1989 hours = frame / (frame_rate * 3600);
1990 frame = frame % (frame_rate * 3600);
1991 mins = frame / (frame_rate * 60);
1992 frame = frame % (frame_rate * 60);
1993 secs = (float) frame / (float) frame_rate;
1994 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
1998 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2002 if (xpos >= 0 && ypos >=0) {
2003 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2005 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_adjustment.get_value(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2007 show_verbose_canvas_cursor ();
2011 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
2014 Timecode::Time timecode;
2018 nframes64_t distance, frame_rate;
2020 Meter meter_at_start(_session->tempo_map().meter_at(start));
2022 if (_session == 0) {
2028 if (Profile->get_sae() || Profile->get_small_screen()) {
2029 m = ARDOUR_UI::instance()->primary_clock.mode ();
2031 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2035 case AudioClock::BBT:
2036 _session->bbt_time (start, sbbt);
2037 _session->bbt_time (end, ebbt);
2040 /* XXX this computation won't work well if the
2041 user makes a selection that spans any meter changes.
2044 ebbt.bars -= sbbt.bars;
2045 if (ebbt.beats >= sbbt.beats) {
2046 ebbt.beats -= sbbt.beats;
2049 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2051 if (ebbt.ticks >= sbbt.ticks) {
2052 ebbt.ticks -= sbbt.ticks;
2055 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2058 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2061 case AudioClock::Timecode:
2062 _session->timecode_duration (end - start, timecode);
2063 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2066 case AudioClock::MinSec:
2067 /* XXX this stuff should be elsewhere.. */
2068 distance = end - start;
2069 frame_rate = _session->frame_rate();
2070 hours = distance / (frame_rate * 3600);
2071 distance = distance % (frame_rate * 3600);
2072 mins = distance / (frame_rate * 60);
2073 distance = distance % (frame_rate * 60);
2074 secs = (float) distance / (float) frame_rate;
2075 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2079 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2083 if (xpos >= 0 && ypos >=0) {
2084 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2087 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2090 show_verbose_canvas_cursor ();
2094 Editor::collect_new_region_view (RegionView* rv)
2096 latest_regionviews.push_back (rv);
2100 Editor::collect_and_select_new_region_view (RegionView* rv)
2103 latest_regionviews.push_back (rv);
2107 Editor::cancel_selection ()
2109 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2110 (*i)->hide_selection ();
2113 selection->clear ();
2114 clicked_selection = 0;
2119 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
2121 boost::shared_ptr<Region> region (rv.region());
2123 if (region->locked()) {
2127 nframes64_t new_bound;
2130 TimeAxisView* tvp = clicked_axisview;
2131 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2133 if (tv && tv->is_track()) {
2134 speed = tv->get_diskstream()->speed();
2137 if (left_direction) {
2138 if (swap_direction) {
2139 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2141 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2144 if (swap_direction) {
2145 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2147 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2152 snap_to (new_bound);
2154 region->trim_start ((nframes64_t) (new_bound * speed), this);
2155 rv.region_changed (PropertyChange (ARDOUR::Properties::start));
2159 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2161 boost::shared_ptr<Region> region (rv.region());
2163 if (region->locked()) {
2167 nframes64_t new_bound;
2170 TimeAxisView* tvp = clicked_axisview;
2171 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2173 if (tv && tv->is_track()) {
2174 speed = tv->get_diskstream()->speed();
2177 if (left_direction) {
2178 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2180 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2184 snap_to (new_bound, (left_direction ? 0 : 1));
2187 nframes64_t pre_trim_first_frame = region->first_frame();
2189 region->trim_front ((nframes64_t) (new_bound * speed), this);
2192 //Get the next region on the left of this region and shrink/expand it.
2193 boost::shared_ptr<Playlist> playlist (region->playlist());
2194 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2196 bool regions_touching = false;
2198 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2199 regions_touching = true;
2202 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2203 if (region_left != 0 &&
2204 (region_left->last_frame() > region->first_frame() || regions_touching))
2206 region_left->trim_end(region->first_frame() - 1, this);
2210 rv.region_changed (ARDOUR::bounds_change);
2214 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2216 boost::shared_ptr<Region> region (rv.region());
2218 if (region->locked()) {
2222 nframes64_t new_bound;
2225 TimeAxisView* tvp = clicked_axisview;
2226 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2228 if (tv && tv->is_track()) {
2229 speed = tv->get_diskstream()->speed();
2232 if (left_direction) {
2233 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
2235 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
2239 snap_to (new_bound);
2242 nframes64_t pre_trim_last_frame = region->last_frame();
2244 region->trim_end ((nframes64_t) (new_bound * speed), this);
2247 //Get the next region on the right of this region and shrink/expand it.
2248 boost::shared_ptr<Playlist> playlist (region->playlist());
2249 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2251 bool regions_touching = false;
2253 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)){
2254 regions_touching = true;
2257 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2258 if (region_right != 0 &&
2259 (region_right->first_frame() < region->last_frame() || regions_touching))
2261 region_right->trim_front(region->last_frame() + 1, this);
2264 rv.region_changed (ARDOUR::bounds_change);
2267 rv.region_changed (PropertyChange (ARDOUR::Properties::length));
2273 Editor::point_trim (GdkEvent* event, nframes64_t new_bound)
2275 RegionView* rv = clicked_regionview;
2277 /* Choose action dependant on which button was pressed */
2278 switch (event->button.button) {
2280 begin_reversible_command (_("Start point trim"));
2282 if (selection->selected (rv)) {
2283 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2284 i != selection->regions.by_layer().end(); ++i)
2287 cerr << "region view contains null region" << endl;
2290 if (!(*i)->region()->locked()) {
2291 (*i)->region()->clear_history ();
2292 (*i)->region()->trim_front (new_bound, this);
2293 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2298 if (!rv->region()->locked()) {
2299 rv->region()->clear_history ();
2300 rv->region()->trim_front (new_bound, this);
2301 _session->add_command(new StatefulDiffCommand (rv->region()));
2305 commit_reversible_command();
2309 begin_reversible_command (_("End point trim"));
2311 if (selection->selected (rv)) {
2313 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2315 if (!(*i)->region()->locked()) {
2316 (*i)->region()->clear_history();
2317 (*i)->region()->trim_end (new_bound, this);
2318 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2324 if (!rv->region()->locked()) {
2325 rv->region()->clear_history ();
2326 rv->region()->trim_end (new_bound, this);
2327 _session->add_command (new StatefulDiffCommand (rv->region()));
2331 commit_reversible_command();
2340 Editor::thaw_region_after_trim (RegionView& rv)
2342 boost::shared_ptr<Region> region (rv.region());
2344 if (region->locked()) {
2348 region->resume_property_changes ();
2350 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2353 arv->unhide_envelope ();
2358 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2363 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2364 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2368 Location* location = find_location_from_marker (marker, is_start);
2369 location->set_hidden (true, this);
2374 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2376 double x1 = frame_to_pixel (start);
2377 double x2 = frame_to_pixel (end);
2378 double y2 = full_canvas_height - 1.0;
2380 zoom_rect->property_x1() = x1;
2381 zoom_rect->property_y1() = 1.0;
2382 zoom_rect->property_x2() = x2;
2383 zoom_rect->property_y2() = y2;
2388 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2390 using namespace Gtkmm2ext;
2392 ArdourPrompter prompter (false);
2394 prompter.set_prompt (_("Name for region:"));
2395 prompter.set_initial_text (clicked_regionview->region()->name());
2396 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2397 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2398 prompter.show_all ();
2399 switch (prompter.run ()) {
2400 case Gtk::RESPONSE_ACCEPT:
2402 prompter.get_result(str);
2404 clicked_regionview->region()->set_name (str);
2413 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2415 /* no brushing without a useful snap setting */
2417 switch (_snap_mode) {
2419 return; /* can't work because it allows region to be placed anywhere */
2424 switch (_snap_type) {
2432 /* don't brush a copy over the original */
2434 if (pos == rv->region()->position()) {
2438 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2440 if (rtv == 0 || !rtv->is_track()) {
2444 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2445 double speed = rtv->get_diskstream()->speed();
2447 playlist->clear_history ();
2448 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
2449 playlist->add_region (new_region, (nframes64_t) (pos * speed));
2450 _session->add_command (new StatefulDiffCommand (playlist));
2452 // playlist is frozen, so we have to update manually XXX this is disgusting
2454 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2458 Editor::track_height_step_timeout ()
2460 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2461 current_stepping_trackview = 0;
2468 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2470 assert (region_view);
2472 _region_motion_group->raise_to_top ();
2474 if (Config->get_edit_mode() == Splice) {
2475 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2477 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2478 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2481 /* sync the canvas to what we think is its current state */
2482 update_canvas_now();
2486 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2488 assert (region_view);
2490 _region_motion_group->raise_to_top ();
2492 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2493 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2497 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2499 assert (region_view);
2501 if (Config->get_edit_mode() == Splice) {
2505 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2506 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2508 begin_reversible_command (_("Drag region brush"));
2511 /** Start a grab where a time range is selected, track(s) are selected, and the
2512 * user clicks and drags a region with a modifier in order to create a new region containing
2513 * the section of the clicked region that lies within the time range.
2516 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2518 if (clicked_regionview == 0) {
2522 /* lets try to create new Region for the selection */
2524 vector<boost::shared_ptr<Region> > new_regions;
2525 create_region_from_selection (new_regions);
2527 if (new_regions.empty()) {
2531 /* XXX fix me one day to use all new regions */
2533 boost::shared_ptr<Region> region (new_regions.front());
2535 /* add it to the current stream/playlist.
2537 tricky: the streamview for the track will add a new regionview. we will
2538 catch the signal it sends when it creates the regionview to
2539 set the regionview we want to then drag.
2542 latest_regionviews.clear();
2543 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2545 /* A selection grab currently creates two undo/redo operations, one for
2546 creating the new region and another for moving it.
2549 begin_reversible_command (_("selection grab"));
2551 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2553 playlist->clear_history ();
2554 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2555 _session->add_command(new StatefulDiffCommand (playlist));
2557 commit_reversible_command ();
2561 if (latest_regionviews.empty()) {
2562 /* something went wrong */
2566 /* we need to deselect all other regionviews, and select this one
2567 i'm ignoring undo stuff, because the region creation will take care of it
2569 selection->set (latest_regionviews);
2571 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2577 if (_drags->active ()) {
2578 _drags->break_drag ();
2580 selection->clear ();
2585 Editor::set_internal_edit (bool yn)
2587 _internal_editing = yn;
2590 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2591 mouse_select_button.get_image ()->show ();
2593 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2594 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2596 mtv->start_step_editing ();
2599 start_step_editing ();
2603 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2604 mouse_select_button.get_image ()->show ();
2605 stop_step_editing ();
2607 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2608 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2610 mtv->stop_step_editing ();
2615 set_canvas_cursor ();
2620 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2621 * used by the `join object/range' tool mode.
2624 Editor::update_join_object_range_location (double x, double y)
2626 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2627 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2628 that we're over requires searching the playlist.
2631 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2632 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2636 if (mouse_mode == MouseObject) {
2637 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2638 } else if (mouse_mode == MouseRange) {
2639 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2642 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2643 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2647 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2652 rtv->canvas_display()->w2i (cx, cy);
2654 bool const top_half = cy < rtv->current_height () / 2;
2656 _join_object_range_state = top_half ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2662 Editor::effective_mouse_mode () const
2664 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2666 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {