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"
56 #include "automation_region_view.h"
57 #include "edit_note_dialog.h"
59 #include "ardour/types.h"
60 #include "ardour/profile.h"
61 #include "ardour/route.h"
62 #include "ardour/audio_track.h"
63 #include "ardour/audio_diskstream.h"
64 #include "ardour/midi_diskstream.h"
65 #include "ardour/playlist.h"
66 #include "ardour/audioplaylist.h"
67 #include "ardour/audioregion.h"
68 #include "ardour/midi_region.h"
69 #include "ardour/dB.h"
70 #include "ardour/utils.h"
71 #include "ardour/region_factory.h"
72 #include "ardour/source_factory.h"
73 #include "ardour/session.h"
80 using namespace ARDOUR;
83 using namespace Editing;
84 using Gtkmm2ext::Keyboard;
87 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
91 Gdk::ModifierType mask;
92 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
93 Glib::RefPtr<const Gdk::Window> pointer_window;
99 pointer_window = canvas_window->get_pointer (x, y, mask);
101 if (pointer_window == track_canvas->get_bin_window()) {
104 in_track_canvas = true;
107 in_track_canvas = false;
112 event.type = GDK_BUTTON_RELEASE;
116 where = event_frame (&event, 0, 0);
121 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
135 switch (event->type) {
136 case GDK_BUTTON_RELEASE:
137 case GDK_BUTTON_PRESS:
138 case GDK_2BUTTON_PRESS:
139 case GDK_3BUTTON_PRESS:
140 *pcx = event->button.x;
141 *pcy = event->button.y;
142 _trackview_group->w2i(*pcx, *pcy);
144 case GDK_MOTION_NOTIFY:
145 *pcx = event->motion.x;
146 *pcy = event->motion.y;
147 _trackview_group->w2i(*pcx, *pcy);
149 case GDK_ENTER_NOTIFY:
150 case GDK_LEAVE_NOTIFY:
151 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
154 case GDK_KEY_RELEASE:
155 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
158 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
162 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
163 position is negative (as can be the case with motion events in particular),
164 the frame location is always positive.
167 return pixel_to_frame (*pcx);
171 Editor::which_grabber_cursor ()
173 Gdk::Cursor* c = grabber_cursor;
175 if (_internal_editing) {
176 switch (mouse_mode) {
178 c = midi_pencil_cursor;
182 c = grabber_note_cursor;
186 c = midi_resize_cursor;
195 switch (_edit_point) {
197 c = grabber_edit_point_cursor;
208 Editor::set_canvas_cursor ()
210 if (_internal_editing) {
212 switch (mouse_mode) {
214 current_canvas_cursor = midi_pencil_cursor;
218 current_canvas_cursor = which_grabber_cursor();
222 current_canvas_cursor = midi_resize_cursor;
231 switch (mouse_mode) {
233 current_canvas_cursor = selector_cursor;
237 current_canvas_cursor = which_grabber_cursor();
241 current_canvas_cursor = cross_hair_cursor;
245 current_canvas_cursor = zoom_cursor;
249 current_canvas_cursor = time_fx_cursor; // just use playhead
253 current_canvas_cursor = speaker_cursor;
258 switch (_join_object_range_state) {
259 case JOIN_OBJECT_RANGE_NONE:
261 case JOIN_OBJECT_RANGE_OBJECT:
262 current_canvas_cursor = which_grabber_cursor ();
264 case JOIN_OBJECT_RANGE_RANGE:
265 current_canvas_cursor = selector_cursor;
269 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
270 if (join_object_range_button.get_active() && last_item_entered) {
271 if (last_item_entered->property_parent() && (*last_item_entered->property_parent()).get_data (X_("timeselection"))) {
272 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value() - canvas_timebars_vsize);
273 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
274 current_canvas_cursor = up_down_cursor;
280 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
285 Editor::set_mouse_mode (MouseMode m, bool force)
287 if (_drags->active ()) {
291 if (!force && m == mouse_mode) {
295 Glib::RefPtr<Action> act;
299 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
303 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
307 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
311 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
315 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
319 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
325 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
328 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
329 tact->set_active (false);
330 tact->set_active (true);
334 Editor::mouse_mode_toggled (MouseMode m)
340 if (!internal_editing()) {
341 if (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
343 /* in all modes except range and joined object/range, hide the range selection,
344 show the object (region) selection.
347 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
348 (*i)->set_should_show_selection (true);
350 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
351 (*i)->hide_selection ();
357 in range or object/range mode, show the range selection.
360 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
361 (*i)->show_selection (selection->time);
366 set_canvas_cursor ();
370 Editor::step_mouse_mode (bool next)
372 switch (current_mouse_mode()) {
375 if (Profile->get_sae()) {
376 set_mouse_mode (MouseZoom);
378 set_mouse_mode (MouseRange);
381 set_mouse_mode (MouseTimeFX);
386 if (next) set_mouse_mode (MouseZoom);
387 else set_mouse_mode (MouseObject);
392 if (Profile->get_sae()) {
393 set_mouse_mode (MouseTimeFX);
395 set_mouse_mode (MouseGain);
398 if (Profile->get_sae()) {
399 set_mouse_mode (MouseObject);
401 set_mouse_mode (MouseRange);
407 if (next) set_mouse_mode (MouseTimeFX);
408 else set_mouse_mode (MouseZoom);
413 set_mouse_mode (MouseAudition);
415 if (Profile->get_sae()) {
416 set_mouse_mode (MouseZoom);
418 set_mouse_mode (MouseGain);
424 if (next) set_mouse_mode (MouseObject);
425 else set_mouse_mode (MouseTimeFX);
431 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
433 /* in object/audition/timefx/gain-automation mode,
434 any button press sets the selection if the object
435 can be selected. this is a bit of hack, because
436 we want to avoid this if the mouse operation is a
439 note: not dbl-click or triple-click
441 Also note that there is no region selection in internal edit mode, otherwise
442 for operations operating on the selection (e.g. cut) it is not obvious whether
443 to cut notes or regions.
446 if (((mouse_mode != MouseObject) &&
447 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
448 (mouse_mode != MouseAudition || item_type != RegionItem) &&
449 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
450 (mouse_mode != MouseGain) &&
451 (mouse_mode != MouseRange)) ||
452 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
453 internal_editing()) {
458 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
460 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
462 /* almost no selection action on modified button-2 or button-3 events */
464 if (item_type != RegionItem && event->button.button != 2) {
470 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
471 bool press = (event->type == GDK_BUTTON_PRESS);
473 // begin_reversible_command (_("select on click"));
477 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
478 set_selected_regionview_from_click (press, op, true);
479 } else if (event->type == GDK_BUTTON_PRESS) {
480 selection->clear_tracks ();
481 set_selected_track_as_side_effect (true);
483 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
484 clicked_selection = select_range_around_region (selection->regions.front());
488 case RegionViewNameHighlight:
490 case LeftFrameHandle:
491 case RightFrameHandle:
492 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
493 set_selected_regionview_from_click (press, op, true);
494 } else if (event->type == GDK_BUTTON_PRESS) {
495 set_selected_track_as_side_effect ();
500 case FadeInHandleItem:
502 case FadeOutHandleItem:
504 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
505 set_selected_regionview_from_click (press, op, true);
506 } else if (event->type == GDK_BUTTON_PRESS) {
507 set_selected_track_as_side_effect ();
511 case ControlPointItem:
512 set_selected_track_as_side_effect (true);
513 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
514 set_selected_control_point_from_click (op, false);
519 /* for context click, select track */
520 if (event->button.button == 3) {
521 selection->clear_tracks ();
522 set_selected_track_as_side_effect (true);
526 case AutomationTrackItem:
527 set_selected_track_as_side_effect (true);
536 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
538 /* single mouse clicks on any of these item types operate
539 independent of mouse mode, mostly because they are
540 not on the main track canvas or because we want
545 case PlayheadCursorItem:
546 _drags->set (new CursorDrag (this, item, true), event);
550 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
551 hide_marker (item, event);
553 _drags->set (new MarkerDrag (this, item), event);
557 case TempoMarkerItem:
559 new TempoMarkerDrag (
562 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
568 case MeterMarkerItem:
570 new MeterMarkerDrag (
573 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
582 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
583 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
589 case RangeMarkerBarItem:
590 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
591 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
593 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
598 case CdMarkerBarItem:
599 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
600 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
602 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
607 case TransportMarkerBarItem:
608 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
609 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
611 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
620 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
621 /* special case: allow trim of range selections in joined object mode;
622 in theory eff should equal MouseRange in this case, but it doesn't
623 because entering the range selection canvas item results in entered_regionview
624 being set to 0, so update_join_object_range_location acts as if we aren't
627 if (item_type == StartSelectionTrimItem) {
628 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
629 } else if (item_type == EndSelectionTrimItem) {
630 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
634 Editing::MouseMode eff = effective_mouse_mode ();
636 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
637 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
644 case StartSelectionTrimItem:
645 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
648 case EndSelectionTrimItem:
649 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
653 if (Keyboard::modifier_state_contains
654 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
655 // contains and not equals because I can't use alt as a modifier alone.
656 start_selection_grab (item, event);
657 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
658 /* grab selection for moving */
659 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
661 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
662 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
664 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
665 if (join_object_range_button.get_active() && atv) {
666 /* smart "join" mode: drag automation */
667 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, up_down_cursor);
669 /* this was debated, but decided the more common action was to
670 make a new selection */
671 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
678 if (internal_editing()) {
679 /* trim notes if we're in internal edit mode and near the ends of the note */
680 _drags->set (new NoteResizeDrag (this, item), event);
685 if (internal_editing()) {
686 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
687 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
691 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
696 case RegionViewNameHighlight:
697 case LeftFrameHandle:
698 case RightFrameHandle:
700 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
701 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
707 if (!internal_editing()) {
708 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
717 if (internal_editing()) {
718 _drags->set (new NoteDrag (this, item), event);
727 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
728 event->type == GDK_BUTTON_PRESS) {
730 _drags->set (new RubberbandSelectDrag (this, item), event);
732 } else if (event->type == GDK_BUTTON_PRESS) {
735 case FadeInHandleItem:
737 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
738 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
742 case FadeOutHandleItem:
744 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
745 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
749 case FeatureLineItem:
751 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
752 remove_transient(item);
756 _drags->set (new FeatureLineDrag (this, item), event);
762 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
763 /* click on an automation region view; do nothing here and let the ARV's signal handler
769 /* click on a normal region view */
770 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
771 add_region_copy_drag (item, event, clicked_regionview);
773 else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
774 add_region_brush_drag (item, event, clicked_regionview);
776 add_region_drag (item, event, clicked_regionview);
779 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
780 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
783 _drags->start_grab (event);
786 case RegionViewNameHighlight:
787 case LeftFrameHandle:
788 case RightFrameHandle:
789 if (!internal_editing ()) {
790 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
791 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
798 /* rename happens on edit clicks */
799 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
800 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
805 case ControlPointItem:
806 _drags->set (new ControlPointDrag (this, item), event);
810 case AutomationLineItem:
811 _drags->set (new LineDrag (this, item), event);
816 if (internal_editing()) {
817 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
818 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
822 _drags->set (new RubberbandSelectDrag (this, item), event);
826 case AutomationTrackItem:
827 /* rubberband drag to select automation points */
828 _drags->set (new RubberbandSelectDrag (this, item), event);
833 if (join_object_range_button.get_active()) {
834 /* we're in "smart" joined mode, and we've clicked on a Selection */
835 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
836 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
838 /* if we're over an automation track, start a drag of its data */
839 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
841 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, up_down_cursor);
844 /* if we're over a track and a region, and in the `object' part of a region,
845 put a selection around the region and drag both
847 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
848 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
849 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
851 boost::shared_ptr<Playlist> pl = t->playlist ();
854 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
856 RegionView* rv = rtv->view()->find_view (r);
857 clicked_selection = select_range_around_region (rv);
858 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
859 list<RegionView*> rvs;
861 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
862 _drags->start_grab (event);
873 case ImageFrameHandleStartItem:
874 imageframe_start_handle_op(item, event) ;
877 case ImageFrameHandleEndItem:
878 imageframe_end_handle_op(item, event) ;
881 case MarkerViewHandleStartItem:
882 markerview_item_start_handle_op(item, event) ;
885 case MarkerViewHandleEndItem:
886 markerview_item_end_handle_op(item, event) ;
890 start_markerview_grab(item, event) ;
893 start_imageframe_grab(item, event) ;
911 /* start a grab so that if we finish after moving
912 we can tell what happened.
914 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
918 _drags->set (new LineDrag (this, item), event);
921 case ControlPointItem:
922 _drags->set (new ControlPointDrag (this, item), event);
933 case ControlPointItem:
934 _drags->set (new ControlPointDrag (this, item), event);
937 case AutomationLineItem:
938 _drags->set (new LineDrag (this, item), event);
942 // XXX need automation mode to identify which
944 // start_line_grab_from_regionview (item, event);
954 if (event->type == GDK_BUTTON_PRESS) {
955 _drags->set (new MouseZoomDrag (this, item), event);
962 if (internal_editing() && item_type == NoteItem) {
963 /* drag notes if we're in internal edit mode */
964 _drags->set (new NoteResizeDrag (this, item), event);
966 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
967 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
968 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
974 _drags->set (new ScrubDrag (this, item), event);
976 scrub_reverse_distance = 0;
977 last_scrub_x = event->button.x;
978 scrubbing_direction = 0;
979 track_canvas->get_window()->set_cursor (*transparent_cursor);
991 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
993 Editing::MouseMode const eff = effective_mouse_mode ();
998 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
999 add_region_copy_drag (item, event, clicked_regionview);
1001 add_region_drag (item, event, clicked_regionview);
1003 _drags->start_grab (event);
1006 case ControlPointItem:
1007 _drags->set (new ControlPointDrag (this, item), event);
1015 switch (item_type) {
1016 case RegionViewNameHighlight:
1017 case LeftFrameHandle:
1018 case RightFrameHandle:
1019 if (!internal_editing ()) {
1020 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1025 case RegionViewName:
1026 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1037 /* relax till release */
1043 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1044 temporal_zoom_session();
1046 temporal_zoom_to_frame (true, event_frame(event));
1059 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1061 if (event->type != GDK_BUTTON_PRESS) {
1065 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1067 if (canvas_window) {
1068 Glib::RefPtr<const Gdk::Window> pointer_window;
1071 Gdk::ModifierType mask;
1073 pointer_window = canvas_window->get_pointer (x, y, mask);
1075 if (pointer_window == track_canvas->get_bin_window()) {
1076 track_canvas->window_to_world (x, y, wx, wy);
1080 track_canvas->grab_focus();
1082 if (_session && _session->actively_recording()) {
1086 button_selection (item, event, item_type);
1088 if (!_drags->active () &&
1089 (Keyboard::is_delete_event (&event->button) ||
1090 Keyboard::is_context_menu_event (&event->button) ||
1091 Keyboard::is_edit_event (&event->button))) {
1093 /* handled by button release */
1097 switch (event->button.button) {
1099 return button_press_handler_1 (item, event, item_type);
1103 return button_press_handler_2 (item, event, item_type);
1118 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1120 nframes64_t where = event_frame (event, 0, 0);
1121 AutomationTimeAxisView* atv = 0;
1123 /* no action if we're recording */
1125 if (_session && _session->actively_recording()) {
1129 /* see if we're finishing a drag */
1131 bool were_dragging = false;
1132 if (_drags->active ()) {
1133 bool const r = _drags->end_grab (event);
1135 /* grab dragged, so do nothing else */
1139 were_dragging = true;
1142 /* edit events get handled here */
1144 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1145 switch (item_type) {
1150 case TempoMarkerItem:
1151 edit_tempo_marker (item);
1154 case MeterMarkerItem:
1155 edit_meter_marker (item);
1158 case RegionViewName:
1159 if (clicked_regionview->name_active()) {
1160 return mouse_rename_region (item, event);
1164 case ControlPointItem:
1165 edit_control_point (item);
1178 /* context menu events get handled here */
1180 if (Keyboard::is_context_menu_event (&event->button)) {
1182 if (!_drags->active ()) {
1184 /* no matter which button pops up the context menu, tell the menu
1185 widget to use button 1 to drive menu selection.
1188 switch (item_type) {
1190 case FadeInHandleItem:
1192 case FadeOutHandleItem:
1193 popup_fade_context_menu (1, event->button.time, item, item_type);
1197 popup_track_context_menu (1, event->button.time, item_type, false, where);
1201 case RegionViewNameHighlight:
1202 case LeftFrameHandle:
1203 case RightFrameHandle:
1204 case RegionViewName:
1205 popup_track_context_menu (1, event->button.time, item_type, false, where);
1209 popup_track_context_menu (1, event->button.time, item_type, true, where);
1212 case AutomationTrackItem:
1213 popup_track_context_menu (1, event->button.time, item_type, false, where);
1217 case RangeMarkerBarItem:
1218 case TransportMarkerBarItem:
1219 case CdMarkerBarItem:
1222 popup_ruler_menu (where, item_type);
1226 marker_context_menu (&event->button, item);
1229 case TempoMarkerItem:
1230 tempo_or_meter_marker_context_menu (&event->button, item);
1233 case MeterMarkerItem:
1234 tempo_or_meter_marker_context_menu (&event->button, item);
1237 case CrossfadeViewItem:
1238 popup_track_context_menu (1, event->button.time, item_type, false, where);
1242 case ImageFrameItem:
1243 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1245 case ImageFrameTimeAxisItem:
1246 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1248 case MarkerViewItem:
1249 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1251 case MarkerTimeAxisItem:
1252 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1264 /* delete events get handled here */
1266 Editing::MouseMode const eff = effective_mouse_mode ();
1268 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1270 switch (item_type) {
1271 case TempoMarkerItem:
1272 remove_tempo_marker (item);
1275 case MeterMarkerItem:
1276 remove_meter_marker (item);
1280 remove_marker (*item, event);
1284 if (eff == MouseObject) {
1285 remove_clicked_region ();
1289 case ControlPointItem:
1290 if (eff == MouseGain) {
1291 remove_gain_control_point (item, event);
1293 remove_control_point (item, event);
1298 remove_midi_note (item, event);
1307 switch (event->button.button) {
1310 switch (item_type) {
1311 /* see comments in button_press_handler */
1312 case PlayheadCursorItem:
1315 case AutomationLineItem:
1316 case StartSelectionTrimItem:
1317 case EndSelectionTrimItem:
1321 if (!_dragging_playhead) {
1322 snap_to_with_modifier (where, event, 0, true);
1323 mouse_add_new_marker (where);
1327 case CdMarkerBarItem:
1328 if (!_dragging_playhead) {
1329 // if we get here then a dragged range wasn't done
1330 snap_to_with_modifier (where, event, 0, true);
1331 mouse_add_new_marker (where, true);
1336 if (!_dragging_playhead) {
1337 snap_to_with_modifier (where, event);
1338 mouse_add_new_tempo_event (where);
1343 if (!_dragging_playhead) {
1344 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1355 switch (item_type) {
1356 case AutomationTrackItem:
1357 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1359 atv->add_automation_event (item, event, where, event->button.y);
1370 switch (item_type) {
1373 /* check that we didn't drag before releasing, since
1374 its really annoying to create new control
1375 points when doing this.
1377 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1378 if (were_dragging && arv) {
1379 arv->add_gain_point_event (item, event);
1385 case AutomationTrackItem:
1386 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1387 add_automation_event (item, event, where, event->button.y);
1396 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1397 if (scrubbing_direction == 0) {
1398 /* no drag, just a click */
1399 switch (item_type) {
1401 play_selected_region ();
1407 /* make sure we stop */
1408 _session->request_transport_speed (0.0);
1425 switch (item_type) {
1427 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1429 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1432 // Button2 click is unused
1445 // x_style_paste (where, 1.0);
1465 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1472 if (last_item_entered != item) {
1473 last_item_entered = item;
1474 last_item_entered_n = 0;
1477 switch (item_type) {
1478 case ControlPointItem:
1479 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1480 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1481 cp->set_visible (true);
1485 at_y = cp->get_y ();
1486 cp->i2w (at_x, at_y);
1490 fraction = 1.0 - (cp->get_y() / cp->line().height());
1492 if (is_drawable() && !_drags->active ()) {
1493 track_canvas->get_window()->set_cursor (*fader_cursor);
1496 last_item_entered_n++;
1497 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1498 if (last_item_entered_n < 10) {
1499 show_verbose_canvas_cursor ();
1505 if (mouse_mode == MouseGain) {
1506 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1508 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1509 if (is_drawable()) {
1510 track_canvas->get_window()->set_cursor (*fader_cursor);
1515 case AutomationLineItem:
1516 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1518 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1520 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1522 if (is_drawable()) {
1523 track_canvas->get_window()->set_cursor (*fader_cursor);
1528 case RegionViewNameHighlight:
1529 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1530 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1534 case LeftFrameHandle:
1535 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1536 track_canvas->get_window()->set_cursor (*left_side_trim_cursor);
1540 case RightFrameHandle:
1541 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1542 track_canvas->get_window()->set_cursor (*right_side_trim_cursor);
1546 case StartSelectionTrimItem:
1547 case EndSelectionTrimItem:
1550 case ImageFrameHandleStartItem:
1551 case ImageFrameHandleEndItem:
1552 case MarkerViewHandleStartItem:
1553 case MarkerViewHandleEndItem:
1556 if (is_drawable()) {
1557 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1561 case PlayheadCursorItem:
1562 if (is_drawable()) {
1563 switch (_edit_point) {
1565 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1568 track_canvas->get_window()->set_cursor (*grabber_cursor);
1574 case RegionViewName:
1576 /* when the name is not an active item, the entire name highlight is for trimming */
1578 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1579 if (mouse_mode == MouseObject && is_drawable()) {
1580 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1586 case AutomationTrackItem:
1587 if (is_drawable()) {
1588 Gdk::Cursor *cursor;
1589 switch (mouse_mode) {
1591 cursor = selector_cursor;
1594 cursor = zoom_cursor;
1597 cursor = cross_hair_cursor;
1601 track_canvas->get_window()->set_cursor (*cursor);
1603 AutomationTimeAxisView* atv;
1604 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1605 clear_entered_track = false;
1606 set_entered_track (atv);
1612 case RangeMarkerBarItem:
1613 case TransportMarkerBarItem:
1614 case CdMarkerBarItem:
1617 if (is_drawable()) {
1618 track_canvas->get_window()->set_cursor (*timebar_cursor);
1623 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1626 entered_marker = marker;
1627 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1629 case MeterMarkerItem:
1630 case TempoMarkerItem:
1631 if (is_drawable()) {
1632 track_canvas->get_window()->set_cursor (*timebar_cursor);
1636 case FadeInHandleItem:
1637 if (mouse_mode == MouseObject && !internal_editing()) {
1638 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1640 rect->property_fill_color_rgba() = 0;
1641 rect->property_outline_pixels() = 1;
1643 track_canvas->get_window()->set_cursor (*fade_in_cursor);
1647 case FadeOutHandleItem:
1648 if (mouse_mode == MouseObject && !internal_editing()) {
1649 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1651 rect->property_fill_color_rgba() = 0;
1652 rect->property_outline_pixels() = 1;
1654 track_canvas->get_window()->set_cursor (*fade_out_cursor);
1657 case FeatureLineItem:
1659 ArdourCanvas::SimpleLine *line = dynamic_cast<ArdourCanvas::SimpleLine *> (item);
1660 line->property_color_rgba() = 0xFF0000FF;
1664 if (join_object_range_button.get_active()) {
1665 set_canvas_cursor ();
1673 /* second pass to handle entered track status in a comprehensible way.
1676 switch (item_type) {
1678 case AutomationLineItem:
1679 case ControlPointItem:
1680 /* these do not affect the current entered track state */
1681 clear_entered_track = false;
1684 case AutomationTrackItem:
1685 /* handled above already */
1689 set_entered_track (0);
1697 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1707 switch (item_type) {
1708 case ControlPointItem:
1709 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1710 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1711 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1712 cp->set_visible (false);
1716 if (is_drawable()) {
1717 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1720 hide_verbose_canvas_cursor ();
1723 case RegionViewNameHighlight:
1724 case LeftFrameHandle:
1725 case RightFrameHandle:
1726 case StartSelectionTrimItem:
1727 case EndSelectionTrimItem:
1728 case PlayheadCursorItem:
1731 case ImageFrameHandleStartItem:
1732 case ImageFrameHandleEndItem:
1733 case MarkerViewHandleStartItem:
1734 case MarkerViewHandleEndItem:
1737 if (is_drawable()) {
1738 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1743 case AutomationLineItem:
1744 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1746 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1748 line->property_fill_color_rgba() = al->get_line_color();
1750 if (is_drawable()) {
1751 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1755 case RegionViewName:
1756 /* see enter_handler() for notes */
1757 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1758 if (is_drawable() && mouse_mode == MouseObject) {
1759 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1764 case RangeMarkerBarItem:
1765 case TransportMarkerBarItem:
1766 case CdMarkerBarItem:
1770 if (is_drawable()) {
1771 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1776 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1780 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1781 location_flags_changed (loc, this);
1784 case MeterMarkerItem:
1785 case TempoMarkerItem:
1787 if (is_drawable()) {
1788 track_canvas->get_window()->set_cursor (*timebar_cursor);
1793 case FadeInHandleItem:
1794 case FadeOutHandleItem:
1795 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1797 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1799 rect->property_fill_color_rgba() = rv->get_fill_color();
1800 rect->property_outline_pixels() = 0;
1803 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1806 case AutomationTrackItem:
1807 if (is_drawable()) {
1808 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1809 clear_entered_track = true;
1810 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1813 case FeatureLineItem:
1815 ArdourCanvas::SimpleLine *line = dynamic_cast<ArdourCanvas::SimpleLine *> (item);
1816 line->property_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
1828 Editor::left_automation_track ()
1830 if (clear_entered_track) {
1831 set_entered_track (0);
1832 clear_entered_track = false;
1838 Editor::scrub (nframes64_t frame, double current_x)
1842 if (scrubbing_direction == 0) {
1844 _session->request_locate (frame, false);
1845 _session->request_transport_speed (0.1);
1846 scrubbing_direction = 1;
1850 if (last_scrub_x > current_x) {
1852 /* pointer moved to the left */
1854 if (scrubbing_direction > 0) {
1856 /* we reversed direction to go backwards */
1859 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1863 /* still moving to the left (backwards) */
1865 scrub_reversals = 0;
1866 scrub_reverse_distance = 0;
1868 delta = 0.01 * (last_scrub_x - current_x);
1869 _session->request_transport_speed (_session->transport_speed() - delta);
1873 /* pointer moved to the right */
1875 if (scrubbing_direction < 0) {
1876 /* we reversed direction to go forward */
1879 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1882 /* still moving to the right */
1884 scrub_reversals = 0;
1885 scrub_reverse_distance = 0;
1887 delta = 0.01 * (current_x - last_scrub_x);
1888 _session->request_transport_speed (_session->transport_speed() + delta);
1892 /* if there have been more than 2 opposite motion moves detected, or one that moves
1893 back more than 10 pixels, reverse direction
1896 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1898 if (scrubbing_direction > 0) {
1899 /* was forwards, go backwards */
1900 _session->request_transport_speed (-0.1);
1901 scrubbing_direction = -1;
1903 /* was backwards, go forwards */
1904 _session->request_transport_speed (0.1);
1905 scrubbing_direction = 1;
1908 scrub_reverse_distance = 0;
1909 scrub_reversals = 0;
1913 last_scrub_x = current_x;
1917 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1919 _last_motion_y = event->motion.y;
1921 if (event->motion.is_hint) {
1924 /* We call this so that MOTION_NOTIFY events continue to be
1925 delivered to the canvas. We need to do this because we set
1926 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1927 the density of the events, at the expense of a round-trip
1928 to the server. Given that this will mostly occur on cases
1929 where DISPLAY = :0.0, and given the cost of what the motion
1930 event might do, its a good tradeoff.
1933 track_canvas->get_pointer (x, y);
1936 if (current_stepping_trackview) {
1937 /* don't keep the persistent stepped trackview if the mouse moves */
1938 current_stepping_trackview = 0;
1939 step_timeout.disconnect ();
1942 if (_session && _session->actively_recording()) {
1943 /* Sorry. no dragging stuff around while we record */
1947 JoinObjectRangeState const old = _join_object_range_state;
1948 update_join_object_range_location (event->motion.x, event->motion.y);
1949 if (_join_object_range_state != old) {
1950 set_canvas_cursor ();
1953 bool handled = false;
1954 if (_drags->active ()) {
1955 handled = _drags->motion_handler (event, from_autoscroll);
1962 track_canvas_motion (event);
1967 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1969 ControlPoint* control_point;
1971 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1972 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1976 // We shouldn't remove the first or last gain point
1977 if (control_point->line().is_last_point(*control_point) ||
1978 control_point->line().is_first_point(*control_point)) {
1982 control_point->line().remove_point (*control_point);
1986 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1988 ControlPoint* control_point;
1990 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1991 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1995 control_point->line().remove_point (*control_point);
1999 Editor::edit_control_point (ArdourCanvas::Item* item)
2001 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2004 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2008 ControlPointDialog d (p);
2009 d.set_position (Gtk::WIN_POS_MOUSE);
2012 if (d.run () != RESPONSE_ACCEPT) {
2016 p->line().modify_point_y (*p, d.get_y_fraction ());
2020 Editor::edit_note (ArdourCanvas::Item* item)
2022 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2025 EditNoteDialog d (&e->region_view(), e);
2026 d.set_position (Gtk::WIN_POS_MOUSE);
2034 Editor::visible_order_range (int* low, int* high) const
2036 *low = TimeAxisView::max_order ();
2039 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2041 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2043 if (!rtv->hidden()) {
2045 if (*high < rtv->order()) {
2046 *high = rtv->order ();
2049 if (*low > rtv->order()) {
2050 *low = rtv->order ();
2057 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2059 /* Either add to or set the set the region selection, unless
2060 this is an alignment click (control used)
2063 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2064 TimeAxisView* tv = &rv.get_time_axis_view();
2065 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2067 if (rtv && rtv->is_track()) {
2068 speed = rtv->track()->speed();
2071 nframes64_t where = get_preferred_edit_position();
2075 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2077 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
2079 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2081 align_region (rv.region(), End, (nframes64_t) (where * speed));
2085 align_region (rv.region(), Start, (nframes64_t) (where * speed));
2092 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
2095 Timecode::Time timecode;
2098 nframes64_t frame_rate;
2101 if (_session == 0) {
2107 if (Profile->get_sae() || Profile->get_small_screen()) {
2108 m = ARDOUR_UI::instance()->primary_clock.mode();
2110 m = ARDOUR_UI::instance()->secondary_clock.mode();
2114 case AudioClock::BBT:
2115 _session->bbt_time (frame, bbt);
2116 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2119 case AudioClock::Timecode:
2120 _session->timecode_time (frame, timecode);
2121 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2124 case AudioClock::MinSec:
2125 /* XXX this is copied from show_verbose_duration_cursor() */
2126 frame_rate = _session->frame_rate();
2127 hours = frame / (frame_rate * 3600);
2128 frame = frame % (frame_rate * 3600);
2129 mins = frame / (frame_rate * 60);
2130 frame = frame % (frame_rate * 60);
2131 secs = (float) frame / (float) frame_rate;
2132 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2136 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2140 if (xpos >= 0 && ypos >=0) {
2141 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2143 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_position(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2145 show_verbose_canvas_cursor ();
2149 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
2152 Timecode::Time timecode;
2156 nframes64_t distance, frame_rate;
2158 Meter meter_at_start(_session->tempo_map().meter_at(start));
2160 if (_session == 0) {
2166 if (Profile->get_sae() || Profile->get_small_screen()) {
2167 m = ARDOUR_UI::instance()->primary_clock.mode ();
2169 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2173 case AudioClock::BBT:
2174 _session->bbt_time (start, sbbt);
2175 _session->bbt_time (end, ebbt);
2178 /* XXX this computation won't work well if the
2179 user makes a selection that spans any meter changes.
2182 ebbt.bars -= sbbt.bars;
2183 if (ebbt.beats >= sbbt.beats) {
2184 ebbt.beats -= sbbt.beats;
2187 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2189 if (ebbt.ticks >= sbbt.ticks) {
2190 ebbt.ticks -= sbbt.ticks;
2193 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2196 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2199 case AudioClock::Timecode:
2200 _session->timecode_duration (end - start, timecode);
2201 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2204 case AudioClock::MinSec:
2205 /* XXX this stuff should be elsewhere.. */
2206 distance = end - start;
2207 frame_rate = _session->frame_rate();
2208 hours = distance / (frame_rate * 3600);
2209 distance = distance % (frame_rate * 3600);
2210 mins = distance / (frame_rate * 60);
2211 distance = distance % (frame_rate * 60);
2212 secs = (float) distance / (float) frame_rate;
2213 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2217 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2221 if (xpos >= 0 && ypos >=0) {
2222 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2225 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2228 show_verbose_canvas_cursor ();
2232 Editor::collect_new_region_view (RegionView* rv)
2234 latest_regionviews.push_back (rv);
2238 Editor::collect_and_select_new_region_view (RegionView* rv)
2241 latest_regionviews.push_back (rv);
2245 Editor::cancel_selection ()
2247 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2248 (*i)->hide_selection ();
2251 selection->clear ();
2252 clicked_selection = 0;
2257 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction)
2259 boost::shared_ptr<Region> region (rv.region());
2261 if (region->locked()) {
2265 nframes64_t new_bound;
2268 TimeAxisView* tvp = clicked_axisview;
2269 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2271 if (tv && tv->is_track()) {
2272 speed = tv->track()->speed();
2275 if (left_direction) {
2276 if (swap_direction) {
2277 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2279 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2282 if (swap_direction) {
2283 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2285 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2289 region->trim_start ((nframes64_t) (new_bound * speed), this);
2290 rv.region_changed (PropertyChange (ARDOUR::Properties::start));
2294 Editor::single_start_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2296 boost::shared_ptr<Region> region (rv.region());
2298 if (region->locked()) {
2303 TimeAxisView* tvp = clicked_axisview;
2304 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2306 if (tv && tv->is_track()) {
2307 speed = tv->track()->speed();
2310 nframes64_t pre_trim_first_frame = region->first_frame();
2312 region->trim_front ((nframes64_t) (new_bound * speed), this);
2315 //Get the next region on the left of this region and shrink/expand it.
2316 boost::shared_ptr<Playlist> playlist (region->playlist());
2317 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2319 bool regions_touching = false;
2321 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2322 regions_touching = true;
2325 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2326 if (region_left != 0 &&
2327 (region_left->last_frame() > region->first_frame() || regions_touching))
2329 region_left->trim_end(region->first_frame() - 1, this);
2333 rv.region_changed (ARDOUR::bounds_change);
2337 Editor::single_end_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2339 boost::shared_ptr<Region> region (rv.region());
2341 if (region->locked()) {
2346 TimeAxisView* tvp = clicked_axisview;
2347 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2349 if (tv && tv->is_track()) {
2350 speed = tv->track()->speed();
2353 nframes64_t pre_trim_last_frame = region->last_frame();
2355 region->trim_end ((nframes64_t) (new_bound * speed), this);
2358 //Get the next region on the right of this region and shrink/expand it.
2359 boost::shared_ptr<Playlist> playlist (region->playlist());
2360 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2362 bool regions_touching = false;
2364 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)) {
2365 regions_touching = true;
2368 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2369 if (region_right != 0 &&
2370 (region_right->first_frame() < region->last_frame() || regions_touching))
2372 region_right->trim_front(region->last_frame() + 1, this);
2375 rv.region_changed (ARDOUR::bounds_change);
2378 rv.region_changed (PropertyChange (ARDOUR::Properties::length));
2384 Editor::point_trim (GdkEvent* event, nframes64_t new_bound)
2386 RegionView* rv = clicked_regionview;
2388 /* Choose action dependant on which button was pressed */
2389 switch (event->button.button) {
2391 begin_reversible_command (_("Start point trim"));
2393 if (selection->selected (rv)) {
2394 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2395 i != selection->regions.by_layer().end(); ++i)
2398 cerr << "region view contains null region" << endl;
2401 if (!(*i)->region()->locked()) {
2402 (*i)->region()->clear_changes ();
2403 (*i)->region()->trim_front (new_bound, this);
2404 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2409 if (!rv->region()->locked()) {
2410 rv->region()->clear_changes ();
2411 rv->region()->trim_front (new_bound, this);
2412 _session->add_command(new StatefulDiffCommand (rv->region()));
2416 commit_reversible_command();
2420 begin_reversible_command (_("End point trim"));
2422 if (selection->selected (rv)) {
2424 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2426 if (!(*i)->region()->locked()) {
2427 (*i)->region()->clear_changes();
2428 (*i)->region()->trim_end (new_bound, this);
2429 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2435 if (!rv->region()->locked()) {
2436 rv->region()->clear_changes ();
2437 rv->region()->trim_end (new_bound, this);
2438 _session->add_command (new StatefulDiffCommand (rv->region()));
2442 commit_reversible_command();
2451 Editor::thaw_region_after_trim (RegionView& rv)
2453 boost::shared_ptr<Region> region (rv.region());
2455 if (region->locked()) {
2459 region->resume_property_changes ();
2461 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2464 arv->unhide_envelope ();
2469 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2474 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2475 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2479 Location* location = find_location_from_marker (marker, is_start);
2480 location->set_hidden (true, this);
2485 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2487 double x1 = frame_to_pixel (start);
2488 double x2 = frame_to_pixel (end);
2489 double y2 = full_canvas_height - 1.0;
2491 zoom_rect->property_x1() = x1;
2492 zoom_rect->property_y1() = 1.0;
2493 zoom_rect->property_x2() = x2;
2494 zoom_rect->property_y2() = y2;
2499 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2501 using namespace Gtkmm2ext;
2503 ArdourPrompter prompter (false);
2505 prompter.set_prompt (_("Name for region:"));
2506 prompter.set_initial_text (clicked_regionview->region()->name());
2507 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2508 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2509 prompter.show_all ();
2510 switch (prompter.run ()) {
2511 case Gtk::RESPONSE_ACCEPT:
2513 prompter.get_result(str);
2515 clicked_regionview->region()->set_name (str);
2524 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2526 /* no brushing without a useful snap setting */
2528 switch (_snap_mode) {
2530 return; /* can't work because it allows region to be placed anywhere */
2535 switch (_snap_type) {
2543 /* don't brush a copy over the original */
2545 if (pos == rv->region()->position()) {
2549 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2551 if (rtv == 0 || !rtv->is_track()) {
2555 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2556 double speed = rtv->track()->speed();
2558 playlist->clear_changes ();
2559 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
2560 playlist->add_region (new_region, (nframes64_t) (pos * speed));
2561 _session->add_command (new StatefulDiffCommand (playlist));
2563 // playlist is frozen, so we have to update manually XXX this is disgusting
2565 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2569 Editor::track_height_step_timeout ()
2571 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2572 current_stepping_trackview = 0;
2579 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2581 assert (region_view);
2583 _region_motion_group->raise_to_top ();
2585 if (Config->get_edit_mode() == Splice) {
2586 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2588 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2589 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2592 /* sync the canvas to what we think is its current state */
2593 update_canvas_now();
2597 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2599 assert (region_view);
2601 _region_motion_group->raise_to_top ();
2603 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2604 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2608 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2610 assert (region_view);
2612 if (Config->get_edit_mode() == Splice) {
2616 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2617 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2619 begin_reversible_command (_("Drag region brush"));
2622 /** Start a grab where a time range is selected, track(s) are selected, and the
2623 * user clicks and drags a region with a modifier in order to create a new region containing
2624 * the section of the clicked region that lies within the time range.
2627 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2629 if (clicked_regionview == 0) {
2633 /* lets try to create new Region for the selection */
2635 vector<boost::shared_ptr<Region> > new_regions;
2636 create_region_from_selection (new_regions);
2638 if (new_regions.empty()) {
2642 /* XXX fix me one day to use all new regions */
2644 boost::shared_ptr<Region> region (new_regions.front());
2646 /* add it to the current stream/playlist.
2648 tricky: the streamview for the track will add a new regionview. we will
2649 catch the signal it sends when it creates the regionview to
2650 set the regionview we want to then drag.
2653 latest_regionviews.clear();
2654 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2656 /* A selection grab currently creates two undo/redo operations, one for
2657 creating the new region and another for moving it.
2660 begin_reversible_command (_("selection grab"));
2662 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2664 playlist->clear_changes ();
2665 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2666 _session->add_command(new StatefulDiffCommand (playlist));
2668 commit_reversible_command ();
2672 if (latest_regionviews.empty()) {
2673 /* something went wrong */
2677 /* we need to deselect all other regionviews, and select this one
2678 i'm ignoring undo stuff, because the region creation will take care of it
2680 selection->set (latest_regionviews);
2682 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2688 if (_drags->active ()) {
2691 selection->clear ();
2696 Editor::set_internal_edit (bool yn)
2698 _internal_editing = yn;
2701 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2702 mouse_select_button.get_image ()->show ();
2703 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2704 set_canvas_cursor ();
2706 /* deselect everything to avoid confusion when e.g. we can't now cut a previously selected
2707 region because cut means "cut note" rather than "cut region".
2709 selection->clear ();
2713 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2714 mouse_select_button.get_image ()->show ();
2715 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Select/Move Ranges"));
2716 mouse_mode_toggled (mouse_mode); // sets cursor
2720 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2721 * used by the `join object/range' tool mode.
2724 Editor::update_join_object_range_location (double x, double y)
2726 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2727 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2728 that we're over requires searching the playlist.
2731 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2732 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2736 if (mouse_mode == MouseObject) {
2737 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2738 } else if (mouse_mode == MouseRange) {
2739 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2742 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2743 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2747 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2752 rtv->canvas_display()->w2i (cx, cy);
2754 bool const top_half = cy < rtv->current_height () / 2;
2756 _join_object_range_state = top_half ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2762 Editor::effective_mouse_mode () const
2764 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2766 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2774 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2776 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2779 e->region_view().delete_note (e->note ());