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 (!internal_editing()) {
329 if (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
331 /* in all modes except range and joined object/range, hide the range selection,
332 show the object (region) selection.
335 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
336 (*i)->set_should_show_selection (true);
338 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
339 (*i)->hide_selection ();
345 in range or object/range mode, show the range selection.
348 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
349 (*i)->show_selection (selection->time);
354 set_canvas_cursor ();
358 Editor::step_mouse_mode (bool next)
360 switch (current_mouse_mode()) {
363 if (Profile->get_sae()) {
364 set_mouse_mode (MouseZoom);
366 set_mouse_mode (MouseRange);
369 set_mouse_mode (MouseTimeFX);
374 if (next) set_mouse_mode (MouseZoom);
375 else set_mouse_mode (MouseObject);
380 if (Profile->get_sae()) {
381 set_mouse_mode (MouseTimeFX);
383 set_mouse_mode (MouseGain);
386 if (Profile->get_sae()) {
387 set_mouse_mode (MouseObject);
389 set_mouse_mode (MouseRange);
395 if (next) set_mouse_mode (MouseTimeFX);
396 else set_mouse_mode (MouseZoom);
401 set_mouse_mode (MouseAudition);
403 if (Profile->get_sae()) {
404 set_mouse_mode (MouseZoom);
406 set_mouse_mode (MouseGain);
412 if (next) set_mouse_mode (MouseObject);
413 else set_mouse_mode (MouseTimeFX);
419 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
421 /* in object/audition/timefx/gain-automation mode,
422 any button press sets the selection if the object
423 can be selected. this is a bit of hack, because
424 we want to avoid this if the mouse operation is a
427 note: not dbl-click or triple-click
430 if (((mouse_mode != MouseObject) &&
431 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
432 (mouse_mode != MouseAudition || item_type != RegionItem) &&
433 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
434 (mouse_mode != MouseGain) &&
435 (mouse_mode != MouseRange)) ||
437 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
442 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
444 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
446 /* almost no selection action on modified button-2 or button-3 events */
448 if (item_type != RegionItem && event->button.button != 2) {
454 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
455 bool press = (event->type == GDK_BUTTON_PRESS);
457 // begin_reversible_command (_("select on click"));
461 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
462 set_selected_regionview_from_click (press, op, true);
463 } else if (event->type == GDK_BUTTON_PRESS) {
464 selection->clear_tracks ();
465 set_selected_track_as_side_effect (true);
467 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
468 clicked_selection = select_range_around_region (selection->regions.front());
473 case RegionViewNameHighlight:
475 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
476 set_selected_regionview_from_click (press, op, true);
477 } else if (event->type == GDK_BUTTON_PRESS) {
478 set_selected_track_as_side_effect ();
483 case FadeInHandleItem:
485 case FadeOutHandleItem:
487 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
488 set_selected_regionview_from_click (press, op, true);
489 } else if (event->type == GDK_BUTTON_PRESS) {
490 set_selected_track_as_side_effect ();
494 case ControlPointItem:
495 set_selected_track_as_side_effect ();
496 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
497 set_selected_control_point_from_click (op, false);
502 /* for context click, select track */
503 if (event->button.button == 3) {
504 selection->clear_tracks ();
505 set_selected_track_as_side_effect (true);
509 case AutomationTrackItem:
510 set_selected_track_as_side_effect (true);
519 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
521 if (_drags->active ()) {
525 /* single mouse clicks on any of these item types operate
526 independent of mouse mode, mostly because they are
527 not on the main track canvas or because we want
532 case PlayheadCursorItem:
533 _drags->set (new CursorDrag (this, item, true), event);
537 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
538 hide_marker (item, event);
540 _drags->set (new MarkerDrag (this, item), event);
544 case TempoMarkerItem:
546 new TempoMarkerDrag (
549 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
555 case MeterMarkerItem:
557 new MeterMarkerDrag (
560 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
569 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
570 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
576 case RangeMarkerBarItem:
577 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
578 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
580 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
585 case CdMarkerBarItem:
586 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
587 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
589 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
594 case TransportMarkerBarItem:
595 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
596 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
598 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
607 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
608 /* special case: allow trim of range selections in joined object mode;
609 in theory eff should equal MouseRange in this case, but it doesn't
610 because entering the range selection canvas item results in entered_regionview
611 being set to 0, so update_join_object_range_location acts as if we aren't
614 if (item_type == StartSelectionTrimItem) {
615 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
616 } else if (item_type == EndSelectionTrimItem) {
617 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
621 Editing::MouseMode eff = effective_mouse_mode ();
623 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
624 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
631 case StartSelectionTrimItem:
632 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
635 case EndSelectionTrimItem:
636 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
640 if (Keyboard::modifier_state_contains
641 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
642 // contains and not equals because I can't use alt as a modifier alone.
643 start_selection_grab (item, event);
644 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
645 /* grab selection for moving */
646 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
648 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
649 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
651 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
652 if (join_object_range_button.get_active() && atv) {
653 /* smart "join" mode: drag automation */
654 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
656 /* this was debated, but decided the more common action was to
657 make a new selection */
658 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
665 if (internal_editing()) {
666 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
672 if (!internal_editing()) {
673 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
682 if (internal_editing()) {
683 _drags->set (new NoteDrag (this, item), event);
692 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
693 event->type == GDK_BUTTON_PRESS) {
695 _drags->set (new RubberbandSelectDrag (this, item), event);
697 } else if (event->type == GDK_BUTTON_PRESS) {
700 case FadeInHandleItem:
702 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
703 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
707 case FadeOutHandleItem:
709 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
710 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
715 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
716 add_region_copy_drag (item, event, clicked_regionview);
717 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
718 add_region_brush_drag (item, event, clicked_regionview);
720 add_region_drag (item, event, clicked_regionview);
723 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
724 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
727 _drags->start_grab (event);
730 case RegionViewNameHighlight:
732 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
733 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
740 /* rename happens on edit clicks */
741 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
742 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
747 case ControlPointItem:
748 _drags->set (new ControlPointDrag (this, item), event);
752 case AutomationLineItem:
753 _drags->set (new LineDrag (this, item), event);
758 if (internal_editing()) {
759 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
762 _drags->set (new RubberbandSelectDrag (this, item), event);
766 case AutomationTrackItem:
767 /* rubberband drag to select automation points */
768 _drags->set (new RubberbandSelectDrag (this, item), event);
773 if (join_object_range_button.get_active()) {
774 /* we're in "smart" joined mode, and we've clicked on a Selection */
775 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
776 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
778 /* if we're over an automation track, start a drag of its data */
779 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
781 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
784 /* if we're over a track and a region, and in the `object' part of a region,
785 put a selection around the region and drag both
787 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
788 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
789 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
791 boost::shared_ptr<Playlist> pl = t->playlist ();
794 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
796 RegionView* rv = rtv->view()->find_view (r);
797 clicked_selection = select_range_around_region (rv);
798 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
799 list<RegionView*> rvs;
801 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
802 _drags->start_grab (event);
813 case ImageFrameHandleStartItem:
814 imageframe_start_handle_op(item, event) ;
817 case ImageFrameHandleEndItem:
818 imageframe_end_handle_op(item, event) ;
821 case MarkerViewHandleStartItem:
822 markerview_item_start_handle_op(item, event) ;
825 case MarkerViewHandleEndItem:
826 markerview_item_end_handle_op(item, event) ;
830 start_markerview_grab(item, event) ;
833 start_imageframe_grab(item, event) ;
851 /* start a grab so that if we finish after moving
852 we can tell what happened.
854 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
858 _drags->set (new LineDrag (this, item), event);
861 case ControlPointItem:
862 _drags->set (new ControlPointDrag (this, item), event);
873 case ControlPointItem:
874 _drags->set (new ControlPointDrag (this, item), event);
877 case AutomationLineItem:
878 _drags->set (new LineDrag (this, item), event);
882 // XXX need automation mode to identify which
884 // start_line_grab_from_regionview (item, event);
894 if (event->type == GDK_BUTTON_PRESS) {
895 _drags->set (new MouseZoomDrag (this, item), event);
902 if (internal_editing() && item_type == NoteItem) {
903 /* drag notes if we're in internal edit mode */
904 _drags->set (new NoteResizeDrag (this, item), event);
906 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
907 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
908 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
914 _drags->set (new ScrubDrag (this, item), event);
916 scrub_reverse_distance = 0;
917 last_scrub_x = event->button.x;
918 scrubbing_direction = 0;
919 track_canvas->get_window()->set_cursor (*transparent_cursor);
931 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
933 Editing::MouseMode const eff = effective_mouse_mode ();
938 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
939 add_region_copy_drag (item, event, clicked_regionview);
941 add_region_drag (item, event, clicked_regionview);
943 _drags->start_grab (event);
946 case ControlPointItem:
947 _drags->set (new ControlPointDrag (this, item), event);
956 case RegionViewNameHighlight:
957 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
962 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
973 /* relax till release */
979 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
980 temporal_zoom_session();
982 temporal_zoom_to_frame (true, event_frame(event));
995 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
997 if (event->type != GDK_BUTTON_PRESS) {
1001 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1003 if (canvas_window) {
1004 Glib::RefPtr<const Gdk::Window> pointer_window;
1007 Gdk::ModifierType mask;
1009 pointer_window = canvas_window->get_pointer (x, y, mask);
1011 if (pointer_window == track_canvas->get_bin_window()) {
1012 track_canvas->window_to_world (x, y, wx, wy);
1013 allow_vertical_scroll = true;
1015 allow_vertical_scroll = false;
1019 track_canvas->grab_focus();
1021 if (_session && _session->actively_recording()) {
1025 button_selection (item, event, item_type);
1027 if (!_drags->active () &&
1028 (Keyboard::is_delete_event (&event->button) ||
1029 Keyboard::is_context_menu_event (&event->button) ||
1030 Keyboard::is_edit_event (&event->button))) {
1032 /* handled by button release */
1036 switch (event->button.button) {
1038 return button_press_handler_1 (item, event, item_type);
1042 return button_press_handler_2 (item, event, item_type);
1057 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1059 nframes64_t where = event_frame (event, 0, 0);
1060 AutomationTimeAxisView* atv = 0;
1062 /* no action if we're recording */
1064 if (_session && _session->actively_recording()) {
1068 /* first, see if we're finishing a drag ... */
1070 bool were_dragging = false;
1071 if (_drags->active ()) {
1072 bool const r = _drags->end_grab (event);
1074 /* grab dragged, so do nothing else */
1078 were_dragging = true;
1081 button_selection (item, event, item_type);
1083 /* edit events get handled here */
1085 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1086 switch (item_type) {
1091 case TempoMarkerItem:
1092 edit_tempo_marker (item);
1095 case MeterMarkerItem:
1096 edit_meter_marker (item);
1099 case RegionViewName:
1100 if (clicked_regionview->name_active()) {
1101 return mouse_rename_region (item, event);
1105 case ControlPointItem:
1106 edit_control_point (item);
1115 /* context menu events get handled here */
1117 if (Keyboard::is_context_menu_event (&event->button)) {
1119 if (!_drags->active ()) {
1121 /* no matter which button pops up the context menu, tell the menu
1122 widget to use button 1 to drive menu selection.
1125 switch (item_type) {
1127 case FadeInHandleItem:
1129 case FadeOutHandleItem:
1130 popup_fade_context_menu (1, event->button.time, item, item_type);
1134 popup_track_context_menu (1, event->button.time, item_type, false, where);
1138 case RegionViewNameHighlight:
1139 case RegionViewName:
1140 popup_track_context_menu (1, event->button.time, item_type, false, where);
1144 popup_track_context_menu (1, event->button.time, item_type, true, where);
1147 case AutomationTrackItem:
1148 popup_track_context_menu (1, event->button.time, item_type, false, where);
1152 case RangeMarkerBarItem:
1153 case TransportMarkerBarItem:
1154 case CdMarkerBarItem:
1157 popup_ruler_menu (where, item_type);
1161 marker_context_menu (&event->button, item);
1164 case TempoMarkerItem:
1165 tempo_or_meter_marker_context_menu (&event->button, item);
1168 case MeterMarkerItem:
1169 tempo_or_meter_marker_context_menu (&event->button, item);
1172 case CrossfadeViewItem:
1173 popup_track_context_menu (1, event->button.time, item_type, false, where);
1177 case ImageFrameItem:
1178 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1180 case ImageFrameTimeAxisItem:
1181 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1183 case MarkerViewItem:
1184 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1186 case MarkerTimeAxisItem:
1187 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1199 /* delete events get handled here */
1201 Editing::MouseMode const eff = effective_mouse_mode ();
1203 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1205 switch (item_type) {
1206 case TempoMarkerItem:
1207 remove_tempo_marker (item);
1210 case MeterMarkerItem:
1211 remove_meter_marker (item);
1215 remove_marker (*item, event);
1219 if (eff == MouseObject) {
1220 remove_clicked_region ();
1224 case ControlPointItem:
1225 if (eff == MouseGain) {
1226 remove_gain_control_point (item, event);
1228 remove_control_point (item, event);
1233 remove_midi_note (item, event);
1242 switch (event->button.button) {
1245 switch (item_type) {
1246 /* see comments in button_press_handler */
1247 case PlayheadCursorItem:
1250 case AutomationLineItem:
1251 case StartSelectionTrimItem:
1252 case EndSelectionTrimItem:
1256 if (!_dragging_playhead) {
1257 snap_to_with_modifier (where, event, 0, true);
1258 mouse_add_new_marker (where);
1262 case CdMarkerBarItem:
1263 if (!_dragging_playhead) {
1264 // if we get here then a dragged range wasn't done
1265 snap_to_with_modifier (where, event, 0, true);
1266 mouse_add_new_marker (where, true);
1271 if (!_dragging_playhead) {
1272 snap_to_with_modifier (where, event);
1273 mouse_add_new_tempo_event (where);
1278 if (!_dragging_playhead) {
1279 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1290 switch (item_type) {
1291 case AutomationTrackItem:
1292 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1294 atv->add_automation_event (item, event, where, event->button.y);
1305 switch (item_type) {
1308 /* check that we didn't drag before releasing, since
1309 its really annoying to create new control
1310 points when doing this.
1312 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1313 if (were_dragging && arv) {
1314 arv->add_gain_point_event (item, event);
1320 case AutomationTrackItem:
1321 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1322 add_automation_event (item, event, where, event->button.y);
1331 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1332 if (scrubbing_direction == 0) {
1333 /* no drag, just a click */
1334 switch (item_type) {
1336 play_selected_region ();
1342 /* make sure we stop */
1343 _session->request_transport_speed (0.0);
1360 switch (item_type) {
1362 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1364 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1367 // Button2 click is unused
1380 // x_style_paste (where, 1.0);
1400 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1406 if (last_item_entered != item) {
1407 last_item_entered = item;
1408 last_item_entered_n = 0;
1411 switch (item_type) {
1412 case ControlPointItem:
1413 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1414 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1415 cp->set_visible (true);
1419 at_y = cp->get_y ();
1420 cp->i2w (at_x, at_y);
1424 fraction = 1.0 - (cp->get_y() / cp->line().height());
1426 if (is_drawable() && !_drags->active ()) {
1427 track_canvas->get_window()->set_cursor (*fader_cursor);
1430 last_item_entered_n++;
1431 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1432 if (last_item_entered_n < 10) {
1433 show_verbose_canvas_cursor ();
1439 if (mouse_mode == MouseGain) {
1440 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1442 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1443 if (is_drawable()) {
1444 track_canvas->get_window()->set_cursor (*fader_cursor);
1449 case AutomationLineItem:
1450 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1452 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1454 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1456 if (is_drawable()) {
1457 track_canvas->get_window()->set_cursor (*fader_cursor);
1462 case RegionViewNameHighlight:
1463 if (is_drawable() && mouse_mode == MouseObject) {
1464 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1468 case StartSelectionTrimItem:
1469 case EndSelectionTrimItem:
1472 case ImageFrameHandleStartItem:
1473 case ImageFrameHandleEndItem:
1474 case MarkerViewHandleStartItem:
1475 case MarkerViewHandleEndItem:
1478 if (is_drawable()) {
1479 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1483 case PlayheadCursorItem:
1484 if (is_drawable()) {
1485 switch (_edit_point) {
1487 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1490 track_canvas->get_window()->set_cursor (*grabber_cursor);
1496 case RegionViewName:
1498 /* when the name is not an active item, the entire name highlight is for trimming */
1500 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1501 if (mouse_mode == MouseObject && is_drawable()) {
1502 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1508 case AutomationTrackItem:
1509 if (is_drawable()) {
1510 Gdk::Cursor *cursor;
1511 switch (mouse_mode) {
1513 cursor = selector_cursor;
1516 cursor = zoom_cursor;
1519 cursor = cross_hair_cursor;
1523 track_canvas->get_window()->set_cursor (*cursor);
1525 AutomationTimeAxisView* atv;
1526 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1527 clear_entered_track = false;
1528 set_entered_track (atv);
1534 case RangeMarkerBarItem:
1535 case TransportMarkerBarItem:
1536 case CdMarkerBarItem:
1539 if (is_drawable()) {
1540 track_canvas->get_window()->set_cursor (*timebar_cursor);
1545 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1548 entered_marker = marker;
1549 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1551 case MeterMarkerItem:
1552 case TempoMarkerItem:
1553 if (is_drawable()) {
1554 track_canvas->get_window()->set_cursor (*timebar_cursor);
1557 case FadeInHandleItem:
1558 case FadeOutHandleItem:
1559 if (mouse_mode == MouseObject) {
1560 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1562 rect->property_fill_color_rgba() = 0;
1563 rect->property_outline_pixels() = 1;
1565 track_canvas->get_window()->set_cursor (*grabber_cursor);
1573 /* second pass to handle entered track status in a comprehensible way.
1576 switch (item_type) {
1578 case AutomationLineItem:
1579 case ControlPointItem:
1580 /* these do not affect the current entered track state */
1581 clear_entered_track = false;
1584 case AutomationTrackItem:
1585 /* handled above already */
1589 set_entered_track (0);
1597 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1606 switch (item_type) {
1607 case ControlPointItem:
1608 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1609 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1610 if (cp->line().npoints() > 1 && !cp->selected()) {
1611 cp->set_visible (false);
1615 if (is_drawable()) {
1616 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1619 hide_verbose_canvas_cursor ();
1622 case RegionViewNameHighlight:
1623 case StartSelectionTrimItem:
1624 case EndSelectionTrimItem:
1625 case PlayheadCursorItem:
1628 case ImageFrameHandleStartItem:
1629 case ImageFrameHandleEndItem:
1630 case MarkerViewHandleStartItem:
1631 case MarkerViewHandleEndItem:
1634 if (is_drawable()) {
1635 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1640 case AutomationLineItem:
1641 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1643 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1645 line->property_fill_color_rgba() = al->get_line_color();
1647 if (is_drawable()) {
1648 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1652 case RegionViewName:
1653 /* see enter_handler() for notes */
1654 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1655 if (is_drawable() && mouse_mode == MouseObject) {
1656 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1661 case RangeMarkerBarItem:
1662 case TransportMarkerBarItem:
1663 case CdMarkerBarItem:
1667 if (is_drawable()) {
1668 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1673 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1677 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1678 location_flags_changed (loc, this);
1681 case MeterMarkerItem:
1682 case TempoMarkerItem:
1684 if (is_drawable()) {
1685 track_canvas->get_window()->set_cursor (*timebar_cursor);
1690 case FadeInHandleItem:
1691 case FadeOutHandleItem:
1692 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1694 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1696 rect->property_fill_color_rgba() = rv->get_fill_color();
1697 rect->property_outline_pixels() = 0;
1700 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1703 case AutomationTrackItem:
1704 if (is_drawable()) {
1705 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1706 clear_entered_track = true;
1707 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1719 Editor::left_automation_track ()
1721 if (clear_entered_track) {
1722 set_entered_track (0);
1723 clear_entered_track = false;
1729 Editor::scrub (nframes64_t frame, double current_x)
1733 if (scrubbing_direction == 0) {
1735 _session->request_locate (frame, false);
1736 _session->request_transport_speed (0.1);
1737 scrubbing_direction = 1;
1741 if (last_scrub_x > current_x) {
1743 /* pointer moved to the left */
1745 if (scrubbing_direction > 0) {
1747 /* we reversed direction to go backwards */
1750 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1754 /* still moving to the left (backwards) */
1756 scrub_reversals = 0;
1757 scrub_reverse_distance = 0;
1759 delta = 0.01 * (last_scrub_x - current_x);
1760 _session->request_transport_speed (_session->transport_speed() - delta);
1764 /* pointer moved to the right */
1766 if (scrubbing_direction < 0) {
1767 /* we reversed direction to go forward */
1770 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1773 /* still moving to the right */
1775 scrub_reversals = 0;
1776 scrub_reverse_distance = 0;
1778 delta = 0.01 * (current_x - last_scrub_x);
1779 _session->request_transport_speed (_session->transport_speed() + delta);
1783 /* if there have been more than 2 opposite motion moves detected, or one that moves
1784 back more than 10 pixels, reverse direction
1787 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1789 if (scrubbing_direction > 0) {
1790 /* was forwards, go backwards */
1791 _session->request_transport_speed (-0.1);
1792 scrubbing_direction = -1;
1794 /* was backwards, go forwards */
1795 _session->request_transport_speed (0.1);
1796 scrubbing_direction = 1;
1799 scrub_reverse_distance = 0;
1800 scrub_reversals = 0;
1804 last_scrub_x = current_x;
1808 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1810 if (event->motion.is_hint) {
1813 /* We call this so that MOTION_NOTIFY events continue to be
1814 delivered to the canvas. We need to do this because we set
1815 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1816 the density of the events, at the expense of a round-trip
1817 to the server. Given that this will mostly occur on cases
1818 where DISPLAY = :0.0, and given the cost of what the motion
1819 event might do, its a good tradeoff.
1822 track_canvas->get_pointer (x, y);
1825 if (current_stepping_trackview) {
1826 /* don't keep the persistent stepped trackview if the mouse moves */
1827 current_stepping_trackview = 0;
1828 step_timeout.disconnect ();
1831 if (_session && _session->actively_recording()) {
1832 /* Sorry. no dragging stuff around while we record */
1836 JoinObjectRangeState const old = _join_object_range_state;
1837 update_join_object_range_location (event->motion.x, event->motion.y);
1838 if (_join_object_range_state != old) {
1839 set_canvas_cursor ();
1842 bool handled = false;
1843 if (_drags->active ()) {
1844 handled = _drags->motion_handler (event, from_autoscroll);
1851 track_canvas_motion (event);
1856 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1858 ControlPoint* control_point;
1860 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1861 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1865 // We shouldn't remove the first or last gain point
1866 if (control_point->line().is_last_point(*control_point) ||
1867 control_point->line().is_first_point(*control_point)) {
1871 control_point->line().remove_point (*control_point);
1875 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1877 ControlPoint* control_point;
1879 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1880 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1884 control_point->line().remove_point (*control_point);
1888 Editor::edit_control_point (ArdourCanvas::Item* item)
1890 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1893 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1897 ControlPointDialog d (p);
1898 d.set_position (Gtk::WIN_POS_MOUSE);
1901 if (d.run () != RESPONSE_ACCEPT) {
1905 p->line().modify_point_y (*p, d.get_y_fraction ());
1910 Editor::visible_order_range (int* low, int* high) const
1912 *low = TimeAxisView::max_order ();
1915 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1917 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1919 if (!rtv->hidden()) {
1921 if (*high < rtv->order()) {
1922 *high = rtv->order ();
1925 if (*low > rtv->order()) {
1926 *low = rtv->order ();
1933 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1935 /* Either add to or set the set the region selection, unless
1936 this is an alignment click (control used)
1939 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1940 TimeAxisView* tv = &rv.get_time_axis_view();
1941 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1943 if (rtv && rtv->is_track()) {
1944 speed = rtv->track()->speed();
1947 nframes64_t where = get_preferred_edit_position();
1951 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1953 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
1955 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1957 align_region (rv.region(), End, (nframes64_t) (where * speed));
1961 align_region (rv.region(), Start, (nframes64_t) (where * speed));
1968 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
1971 Timecode::Time timecode;
1974 nframes64_t frame_rate;
1977 if (_session == 0) {
1983 if (Profile->get_sae() || Profile->get_small_screen()) {
1984 m = ARDOUR_UI::instance()->primary_clock.mode();
1986 m = ARDOUR_UI::instance()->secondary_clock.mode();
1990 case AudioClock::BBT:
1991 _session->bbt_time (frame, bbt);
1992 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
1995 case AudioClock::Timecode:
1996 _session->timecode_time (frame, timecode);
1997 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2000 case AudioClock::MinSec:
2001 /* XXX this is copied from show_verbose_duration_cursor() */
2002 frame_rate = _session->frame_rate();
2003 hours = frame / (frame_rate * 3600);
2004 frame = frame % (frame_rate * 3600);
2005 mins = frame / (frame_rate * 60);
2006 frame = frame % (frame_rate * 60);
2007 secs = (float) frame / (float) frame_rate;
2008 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2012 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2016 if (xpos >= 0 && ypos >=0) {
2017 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2019 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_position(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2021 show_verbose_canvas_cursor ();
2025 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
2028 Timecode::Time timecode;
2032 nframes64_t distance, frame_rate;
2034 Meter meter_at_start(_session->tempo_map().meter_at(start));
2036 if (_session == 0) {
2042 if (Profile->get_sae() || Profile->get_small_screen()) {
2043 m = ARDOUR_UI::instance()->primary_clock.mode ();
2045 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2049 case AudioClock::BBT:
2050 _session->bbt_time (start, sbbt);
2051 _session->bbt_time (end, ebbt);
2054 /* XXX this computation won't work well if the
2055 user makes a selection that spans any meter changes.
2058 ebbt.bars -= sbbt.bars;
2059 if (ebbt.beats >= sbbt.beats) {
2060 ebbt.beats -= sbbt.beats;
2063 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2065 if (ebbt.ticks >= sbbt.ticks) {
2066 ebbt.ticks -= sbbt.ticks;
2069 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2072 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2075 case AudioClock::Timecode:
2076 _session->timecode_duration (end - start, timecode);
2077 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2080 case AudioClock::MinSec:
2081 /* XXX this stuff should be elsewhere.. */
2082 distance = end - start;
2083 frame_rate = _session->frame_rate();
2084 hours = distance / (frame_rate * 3600);
2085 distance = distance % (frame_rate * 3600);
2086 mins = distance / (frame_rate * 60);
2087 distance = distance % (frame_rate * 60);
2088 secs = (float) distance / (float) frame_rate;
2089 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2093 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2097 if (xpos >= 0 && ypos >=0) {
2098 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2101 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2104 show_verbose_canvas_cursor ();
2108 Editor::collect_new_region_view (RegionView* rv)
2110 latest_regionviews.push_back (rv);
2114 Editor::collect_and_select_new_region_view (RegionView* rv)
2117 latest_regionviews.push_back (rv);
2121 Editor::cancel_selection ()
2123 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2124 (*i)->hide_selection ();
2127 selection->clear ();
2128 clicked_selection = 0;
2133 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction)
2135 boost::shared_ptr<Region> region (rv.region());
2137 if (region->locked()) {
2141 nframes64_t new_bound;
2144 TimeAxisView* tvp = clicked_axisview;
2145 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2147 if (tv && tv->is_track()) {
2148 speed = tv->track()->speed();
2151 if (left_direction) {
2152 if (swap_direction) {
2153 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2155 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2158 if (swap_direction) {
2159 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2161 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2165 region->trim_start ((nframes64_t) (new_bound * speed), this);
2166 rv.region_changed (PropertyChange (ARDOUR::Properties::start));
2170 Editor::single_start_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2172 boost::shared_ptr<Region> region (rv.region());
2174 if (region->locked()) {
2179 TimeAxisView* tvp = clicked_axisview;
2180 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2182 if (tv && tv->is_track()) {
2183 speed = tv->track()->speed();
2186 nframes64_t pre_trim_first_frame = region->first_frame();
2188 region->trim_front ((nframes64_t) (new_bound * speed), this);
2191 //Get the next region on the left of this region and shrink/expand it.
2192 boost::shared_ptr<Playlist> playlist (region->playlist());
2193 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2195 bool regions_touching = false;
2197 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2198 regions_touching = true;
2201 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2202 if (region_left != 0 &&
2203 (region_left->last_frame() > region->first_frame() || regions_touching))
2205 region_left->trim_end(region->first_frame() - 1, this);
2209 rv.region_changed (ARDOUR::bounds_change);
2213 Editor::single_end_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2215 boost::shared_ptr<Region> region (rv.region());
2217 if (region->locked()) {
2222 TimeAxisView* tvp = clicked_axisview;
2223 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2225 if (tv && tv->is_track()) {
2226 speed = tv->track()->speed();
2229 nframes64_t pre_trim_last_frame = region->last_frame();
2231 region->trim_end ((nframes64_t) (new_bound * speed), this);
2234 //Get the next region on the right of this region and shrink/expand it.
2235 boost::shared_ptr<Playlist> playlist (region->playlist());
2236 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2238 bool regions_touching = false;
2240 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)) {
2241 regions_touching = true;
2244 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2245 if (region_right != 0 &&
2246 (region_right->first_frame() < region->last_frame() || regions_touching))
2248 region_right->trim_front(region->last_frame() + 1, this);
2251 rv.region_changed (ARDOUR::bounds_change);
2254 rv.region_changed (PropertyChange (ARDOUR::Properties::length));
2260 Editor::point_trim (GdkEvent* event, nframes64_t new_bound)
2262 RegionView* rv = clicked_regionview;
2264 /* Choose action dependant on which button was pressed */
2265 switch (event->button.button) {
2267 begin_reversible_command (_("Start point trim"));
2269 if (selection->selected (rv)) {
2270 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2271 i != selection->regions.by_layer().end(); ++i)
2274 cerr << "region view contains null region" << endl;
2277 if (!(*i)->region()->locked()) {
2278 (*i)->region()->clear_history ();
2279 (*i)->region()->trim_front (new_bound, this);
2280 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2285 if (!rv->region()->locked()) {
2286 rv->region()->clear_history ();
2287 rv->region()->trim_front (new_bound, this);
2288 _session->add_command(new StatefulDiffCommand (rv->region()));
2292 commit_reversible_command();
2296 begin_reversible_command (_("End point trim"));
2298 if (selection->selected (rv)) {
2300 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2302 if (!(*i)->region()->locked()) {
2303 (*i)->region()->clear_history();
2304 (*i)->region()->trim_end (new_bound, this);
2305 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2311 if (!rv->region()->locked()) {
2312 rv->region()->clear_history ();
2313 rv->region()->trim_end (new_bound, this);
2314 _session->add_command (new StatefulDiffCommand (rv->region()));
2318 commit_reversible_command();
2327 Editor::thaw_region_after_trim (RegionView& rv)
2329 boost::shared_ptr<Region> region (rv.region());
2331 if (region->locked()) {
2335 region->resume_property_changes ();
2337 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2340 arv->unhide_envelope ();
2345 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2350 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2351 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2355 Location* location = find_location_from_marker (marker, is_start);
2356 location->set_hidden (true, this);
2361 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2363 double x1 = frame_to_pixel (start);
2364 double x2 = frame_to_pixel (end);
2365 double y2 = full_canvas_height - 1.0;
2367 zoom_rect->property_x1() = x1;
2368 zoom_rect->property_y1() = 1.0;
2369 zoom_rect->property_x2() = x2;
2370 zoom_rect->property_y2() = y2;
2375 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2377 using namespace Gtkmm2ext;
2379 ArdourPrompter prompter (false);
2381 prompter.set_prompt (_("Name for region:"));
2382 prompter.set_initial_text (clicked_regionview->region()->name());
2383 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2384 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2385 prompter.show_all ();
2386 switch (prompter.run ()) {
2387 case Gtk::RESPONSE_ACCEPT:
2389 prompter.get_result(str);
2391 clicked_regionview->region()->set_name (str);
2400 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2402 /* no brushing without a useful snap setting */
2404 switch (_snap_mode) {
2406 return; /* can't work because it allows region to be placed anywhere */
2411 switch (_snap_type) {
2419 /* don't brush a copy over the original */
2421 if (pos == rv->region()->position()) {
2425 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2427 if (rtv == 0 || !rtv->is_track()) {
2431 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2432 double speed = rtv->track()->speed();
2434 playlist->clear_history ();
2435 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
2436 playlist->add_region (new_region, (nframes64_t) (pos * speed));
2437 _session->add_command (new StatefulDiffCommand (playlist));
2439 // playlist is frozen, so we have to update manually XXX this is disgusting
2441 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2445 Editor::track_height_step_timeout ()
2447 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2448 current_stepping_trackview = 0;
2455 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2457 assert (region_view);
2459 _region_motion_group->raise_to_top ();
2461 if (Config->get_edit_mode() == Splice) {
2462 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2464 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2465 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2468 /* sync the canvas to what we think is its current state */
2469 update_canvas_now();
2473 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2475 assert (region_view);
2477 _region_motion_group->raise_to_top ();
2479 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2480 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2484 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2486 assert (region_view);
2488 if (Config->get_edit_mode() == Splice) {
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(), true, false));
2495 begin_reversible_command (_("Drag region brush"));
2498 /** Start a grab where a time range is selected, track(s) are selected, and the
2499 * user clicks and drags a region with a modifier in order to create a new region containing
2500 * the section of the clicked region that lies within the time range.
2503 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2505 if (clicked_regionview == 0) {
2509 /* lets try to create new Region for the selection */
2511 vector<boost::shared_ptr<Region> > new_regions;
2512 create_region_from_selection (new_regions);
2514 if (new_regions.empty()) {
2518 /* XXX fix me one day to use all new regions */
2520 boost::shared_ptr<Region> region (new_regions.front());
2522 /* add it to the current stream/playlist.
2524 tricky: the streamview for the track will add a new regionview. we will
2525 catch the signal it sends when it creates the regionview to
2526 set the regionview we want to then drag.
2529 latest_regionviews.clear();
2530 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2532 /* A selection grab currently creates two undo/redo operations, one for
2533 creating the new region and another for moving it.
2536 begin_reversible_command (_("selection grab"));
2538 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2540 playlist->clear_history ();
2541 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2542 _session->add_command(new StatefulDiffCommand (playlist));
2544 commit_reversible_command ();
2548 if (latest_regionviews.empty()) {
2549 /* something went wrong */
2553 /* we need to deselect all other regionviews, and select this one
2554 i'm ignoring undo stuff, because the region creation will take care of it
2556 selection->set (latest_regionviews);
2558 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2564 if (_drags->active ()) {
2567 selection->clear ();
2572 Editor::set_internal_edit (bool yn)
2574 _internal_editing = yn;
2577 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2578 mouse_select_button.get_image ()->show ();
2579 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2581 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2582 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2584 mtv->start_step_editing ();
2588 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2589 (*i)->hide_selection ();
2592 start_step_editing ();
2593 set_canvas_cursor ();
2597 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2598 mouse_select_button.get_image ()->show ();
2599 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Select/Move Ranges"));
2600 stop_step_editing ();
2602 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2603 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2605 mtv->stop_step_editing ();
2609 mouse_mode_toggled (mouse_mode);
2613 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2614 * used by the `join object/range' tool mode.
2617 Editor::update_join_object_range_location (double x, double y)
2619 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2620 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2621 that we're over requires searching the playlist.
2624 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2625 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2629 if (mouse_mode == MouseObject) {
2630 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2631 } else if (mouse_mode == MouseRange) {
2632 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2635 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2636 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2640 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2645 rtv->canvas_display()->w2i (cx, cy);
2647 bool const top_half = cy < rtv->current_height () / 2;
2649 _join_object_range_state = top_half ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2655 Editor::effective_mouse_mode () const
2657 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2659 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2667 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2669 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2672 e->region_view().delete_note (e->note ());