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 (op, 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 (op);
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 (op);
511 case ControlPointItem:
512 set_selected_track_as_side_effect (op, 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 (op, true);
526 case AutomationTrackItem:
527 set_selected_track_as_side_effect (op, 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 framepos_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 update_region_layering_order_editor (where);
1144 /* edit events get handled here */
1146 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1147 switch (item_type) {
1152 case TempoMarkerItem:
1153 edit_tempo_marker (item);
1156 case MeterMarkerItem:
1157 edit_meter_marker (item);
1160 case RegionViewName:
1161 if (clicked_regionview->name_active()) {
1162 return mouse_rename_region (item, event);
1166 case ControlPointItem:
1167 edit_control_point (item);
1180 /* context menu events get handled here */
1182 if (Keyboard::is_context_menu_event (&event->button)) {
1184 if (!_drags->active ()) {
1186 /* no matter which button pops up the context menu, tell the menu
1187 widget to use button 1 to drive menu selection.
1190 switch (item_type) {
1192 case FadeInHandleItem:
1194 case FadeOutHandleItem:
1195 popup_fade_context_menu (1, event->button.time, item, item_type);
1199 popup_track_context_menu (1, event->button.time, item_type, false, where);
1203 case RegionViewNameHighlight:
1204 case LeftFrameHandle:
1205 case RightFrameHandle:
1206 case RegionViewName:
1207 popup_track_context_menu (1, event->button.time, item_type, false, where);
1211 popup_track_context_menu (1, event->button.time, item_type, true, where);
1214 case AutomationTrackItem:
1215 popup_track_context_menu (1, event->button.time, item_type, false, where);
1219 case RangeMarkerBarItem:
1220 case TransportMarkerBarItem:
1221 case CdMarkerBarItem:
1224 popup_ruler_menu (where, item_type);
1228 marker_context_menu (&event->button, item);
1231 case TempoMarkerItem:
1232 tempo_or_meter_marker_context_menu (&event->button, item);
1235 case MeterMarkerItem:
1236 tempo_or_meter_marker_context_menu (&event->button, item);
1239 case CrossfadeViewItem:
1240 popup_track_context_menu (1, event->button.time, item_type, false, where);
1244 case ImageFrameItem:
1245 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1247 case ImageFrameTimeAxisItem:
1248 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1250 case MarkerViewItem:
1251 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1253 case MarkerTimeAxisItem:
1254 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1266 /* delete events get handled here */
1268 Editing::MouseMode const eff = effective_mouse_mode ();
1270 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1272 switch (item_type) {
1273 case TempoMarkerItem:
1274 remove_tempo_marker (item);
1277 case MeterMarkerItem:
1278 remove_meter_marker (item);
1282 remove_marker (*item, event);
1286 if (eff == MouseObject) {
1287 remove_clicked_region ();
1291 case ControlPointItem:
1292 if (eff == MouseGain) {
1293 remove_gain_control_point (item, event);
1295 remove_control_point (item, event);
1300 remove_midi_note (item, event);
1309 switch (event->button.button) {
1312 switch (item_type) {
1313 /* see comments in button_press_handler */
1314 case PlayheadCursorItem:
1317 case AutomationLineItem:
1318 case StartSelectionTrimItem:
1319 case EndSelectionTrimItem:
1323 if (!_dragging_playhead) {
1324 snap_to_with_modifier (where, event, 0, true);
1325 mouse_add_new_marker (where);
1329 case CdMarkerBarItem:
1330 if (!_dragging_playhead) {
1331 // if we get here then a dragged range wasn't done
1332 snap_to_with_modifier (where, event, 0, true);
1333 mouse_add_new_marker (where, true);
1338 if (!_dragging_playhead) {
1339 snap_to_with_modifier (where, event);
1340 mouse_add_new_tempo_event (where);
1345 if (!_dragging_playhead) {
1346 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1357 switch (item_type) {
1358 case AutomationTrackItem:
1359 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1361 atv->add_automation_event (item, event, where, event->button.y);
1372 switch (item_type) {
1375 /* check that we didn't drag before releasing, since
1376 its really annoying to create new control
1377 points when doing this.
1379 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1380 if (were_dragging && arv) {
1381 arv->add_gain_point_event (item, event);
1387 case AutomationTrackItem:
1388 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1389 add_automation_event (item, event, where, event->button.y);
1398 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1399 if (scrubbing_direction == 0) {
1400 /* no drag, just a click */
1401 switch (item_type) {
1403 play_selected_region ();
1409 /* make sure we stop */
1410 _session->request_transport_speed (0.0);
1427 switch (item_type) {
1429 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1431 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1434 // Button2 click is unused
1447 // x_style_paste (where, 1.0);
1467 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1474 last_item_entered = item;
1476 switch (item_type) {
1477 case ControlPointItem:
1478 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1479 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1480 cp->set_visible (true);
1484 at_y = cp->get_y ();
1485 cp->i2w (at_x, at_y);
1489 fraction = 1.0 - (cp->get_y() / cp->line().height());
1491 if (is_drawable() && !_drags->active ()) {
1492 track_canvas->get_window()->set_cursor (*fader_cursor);
1495 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1496 show_verbose_canvas_cursor ();
1501 if (mouse_mode == MouseGain) {
1502 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1504 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1505 if (is_drawable()) {
1506 track_canvas->get_window()->set_cursor (*fader_cursor);
1511 case AutomationLineItem:
1512 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1514 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1516 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1518 if (is_drawable()) {
1519 track_canvas->get_window()->set_cursor (*fader_cursor);
1524 case RegionViewNameHighlight:
1525 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1526 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1530 case LeftFrameHandle:
1531 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1532 track_canvas->get_window()->set_cursor (*left_side_trim_cursor);
1536 case RightFrameHandle:
1537 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1538 track_canvas->get_window()->set_cursor (*right_side_trim_cursor);
1542 case StartSelectionTrimItem:
1543 case EndSelectionTrimItem:
1546 case ImageFrameHandleStartItem:
1547 case ImageFrameHandleEndItem:
1548 case MarkerViewHandleStartItem:
1549 case MarkerViewHandleEndItem:
1552 if (is_drawable()) {
1553 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1557 case PlayheadCursorItem:
1558 if (is_drawable()) {
1559 switch (_edit_point) {
1561 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1564 track_canvas->get_window()->set_cursor (*grabber_cursor);
1570 case RegionViewName:
1572 /* when the name is not an active item, the entire name highlight is for trimming */
1574 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1575 if (mouse_mode == MouseObject && is_drawable()) {
1576 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1582 case AutomationTrackItem:
1583 if (is_drawable()) {
1584 Gdk::Cursor *cursor;
1585 switch (mouse_mode) {
1587 cursor = selector_cursor;
1590 cursor = zoom_cursor;
1593 cursor = cross_hair_cursor;
1597 track_canvas->get_window()->set_cursor (*cursor);
1599 AutomationTimeAxisView* atv;
1600 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1601 clear_entered_track = false;
1602 set_entered_track (atv);
1608 case RangeMarkerBarItem:
1609 case TransportMarkerBarItem:
1610 case CdMarkerBarItem:
1613 if (is_drawable()) {
1614 track_canvas->get_window()->set_cursor (*timebar_cursor);
1619 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1622 entered_marker = marker;
1623 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1625 case MeterMarkerItem:
1626 case TempoMarkerItem:
1627 if (is_drawable()) {
1628 track_canvas->get_window()->set_cursor (*timebar_cursor);
1632 case FadeInHandleItem:
1633 if (mouse_mode == MouseObject && !internal_editing()) {
1634 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1636 rect->property_fill_color_rgba() = 0;
1637 rect->property_outline_pixels() = 1;
1639 track_canvas->get_window()->set_cursor (*fade_in_cursor);
1643 case FadeOutHandleItem:
1644 if (mouse_mode == MouseObject && !internal_editing()) {
1645 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1647 rect->property_fill_color_rgba() = 0;
1648 rect->property_outline_pixels() = 1;
1650 track_canvas->get_window()->set_cursor (*fade_out_cursor);
1653 case FeatureLineItem:
1655 ArdourCanvas::SimpleLine *line = dynamic_cast<ArdourCanvas::SimpleLine *> (item);
1656 line->property_color_rgba() = 0xFF0000FF;
1660 if (join_object_range_button.get_active()) {
1661 set_canvas_cursor ();
1669 /* second pass to handle entered track status in a comprehensible way.
1672 switch (item_type) {
1674 case AutomationLineItem:
1675 case ControlPointItem:
1676 /* these do not affect the current entered track state */
1677 clear_entered_track = false;
1680 case AutomationTrackItem:
1681 /* handled above already */
1685 set_entered_track (0);
1693 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1703 switch (item_type) {
1704 case ControlPointItem:
1705 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1706 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1707 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1708 cp->set_visible (false);
1712 if (is_drawable()) {
1713 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1716 hide_verbose_canvas_cursor ();
1719 case RegionViewNameHighlight:
1720 case LeftFrameHandle:
1721 case RightFrameHandle:
1722 case StartSelectionTrimItem:
1723 case EndSelectionTrimItem:
1724 case PlayheadCursorItem:
1727 case ImageFrameHandleStartItem:
1728 case ImageFrameHandleEndItem:
1729 case MarkerViewHandleStartItem:
1730 case MarkerViewHandleEndItem:
1733 if (is_drawable()) {
1734 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1739 case AutomationLineItem:
1740 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1742 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1744 line->property_fill_color_rgba() = al->get_line_color();
1746 if (is_drawable()) {
1747 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1751 case RegionViewName:
1752 /* see enter_handler() for notes */
1753 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1754 if (is_drawable() && mouse_mode == MouseObject) {
1755 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1760 case RangeMarkerBarItem:
1761 case TransportMarkerBarItem:
1762 case CdMarkerBarItem:
1766 if (is_drawable()) {
1767 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1772 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1776 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1777 location_flags_changed (loc, this);
1780 case MeterMarkerItem:
1781 case TempoMarkerItem:
1783 if (is_drawable()) {
1784 track_canvas->get_window()->set_cursor (*timebar_cursor);
1789 case FadeInHandleItem:
1790 case FadeOutHandleItem:
1791 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1793 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1795 rect->property_fill_color_rgba() = rv->get_fill_color();
1796 rect->property_outline_pixels() = 0;
1799 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1802 case AutomationTrackItem:
1803 if (is_drawable()) {
1804 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1805 clear_entered_track = true;
1806 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1809 case FeatureLineItem:
1811 ArdourCanvas::SimpleLine *line = dynamic_cast<ArdourCanvas::SimpleLine *> (item);
1812 line->property_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
1824 Editor::left_automation_track ()
1826 if (clear_entered_track) {
1827 set_entered_track (0);
1828 clear_entered_track = false;
1834 Editor::scrub (nframes64_t frame, double current_x)
1838 if (scrubbing_direction == 0) {
1840 _session->request_locate (frame, false);
1841 _session->request_transport_speed (0.1);
1842 scrubbing_direction = 1;
1846 if (last_scrub_x > current_x) {
1848 /* pointer moved to the left */
1850 if (scrubbing_direction > 0) {
1852 /* we reversed direction to go backwards */
1855 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1859 /* still moving to the left (backwards) */
1861 scrub_reversals = 0;
1862 scrub_reverse_distance = 0;
1864 delta = 0.01 * (last_scrub_x - current_x);
1865 _session->request_transport_speed (_session->transport_speed() - delta);
1869 /* pointer moved to the right */
1871 if (scrubbing_direction < 0) {
1872 /* we reversed direction to go forward */
1875 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1878 /* still moving to the right */
1880 scrub_reversals = 0;
1881 scrub_reverse_distance = 0;
1883 delta = 0.01 * (current_x - last_scrub_x);
1884 _session->request_transport_speed (_session->transport_speed() + delta);
1888 /* if there have been more than 2 opposite motion moves detected, or one that moves
1889 back more than 10 pixels, reverse direction
1892 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1894 if (scrubbing_direction > 0) {
1895 /* was forwards, go backwards */
1896 _session->request_transport_speed (-0.1);
1897 scrubbing_direction = -1;
1899 /* was backwards, go forwards */
1900 _session->request_transport_speed (0.1);
1901 scrubbing_direction = 1;
1904 scrub_reverse_distance = 0;
1905 scrub_reversals = 0;
1909 last_scrub_x = current_x;
1913 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1915 _last_motion_y = event->motion.y;
1917 if (event->motion.is_hint) {
1920 /* We call this so that MOTION_NOTIFY events continue to be
1921 delivered to the canvas. We need to do this because we set
1922 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1923 the density of the events, at the expense of a round-trip
1924 to the server. Given that this will mostly occur on cases
1925 where DISPLAY = :0.0, and given the cost of what the motion
1926 event might do, its a good tradeoff.
1929 track_canvas->get_pointer (x, y);
1932 if (current_stepping_trackview) {
1933 /* don't keep the persistent stepped trackview if the mouse moves */
1934 current_stepping_trackview = 0;
1935 step_timeout.disconnect ();
1938 if (_session && _session->actively_recording()) {
1939 /* Sorry. no dragging stuff around while we record */
1943 JoinObjectRangeState const old = _join_object_range_state;
1944 update_join_object_range_location (event->motion.x, event->motion.y);
1945 if (_join_object_range_state != old) {
1946 set_canvas_cursor ();
1949 bool handled = false;
1950 if (_drags->active ()) {
1951 handled = _drags->motion_handler (event, from_autoscroll);
1958 track_canvas_motion (event);
1963 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1965 ControlPoint* control_point;
1967 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1968 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1972 // We shouldn't remove the first or last gain point
1973 if (control_point->line().is_last_point(*control_point) ||
1974 control_point->line().is_first_point(*control_point)) {
1978 control_point->line().remove_point (*control_point);
1982 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1984 ControlPoint* control_point;
1986 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1987 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1991 control_point->line().remove_point (*control_point);
1995 Editor::edit_control_point (ArdourCanvas::Item* item)
1997 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2000 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2004 ControlPointDialog d (p);
2005 d.set_position (Gtk::WIN_POS_MOUSE);
2008 if (d.run () != RESPONSE_ACCEPT) {
2012 p->line().modify_point_y (*p, d.get_y_fraction ());
2016 Editor::edit_note (ArdourCanvas::Item* item)
2018 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2021 EditNoteDialog d (&e->region_view(), e);
2022 d.set_position (Gtk::WIN_POS_MOUSE);
2030 Editor::visible_order_range (int* low, int* high) const
2032 *low = TimeAxisView::max_order ();
2035 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2037 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2039 if (!rtv->hidden()) {
2041 if (*high < rtv->order()) {
2042 *high = rtv->order ();
2045 if (*low > rtv->order()) {
2046 *low = rtv->order ();
2053 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2055 /* Either add to or set the set the region selection, unless
2056 this is an alignment click (control used)
2059 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2060 TimeAxisView* tv = &rv.get_time_axis_view();
2061 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2063 if (rtv && rtv->is_track()) {
2064 speed = rtv->track()->speed();
2067 nframes64_t where = get_preferred_edit_position();
2071 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2073 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
2075 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2077 align_region (rv.region(), End, (nframes64_t) (where * speed));
2081 align_region (rv.region(), Start, (nframes64_t) (where * speed));
2088 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
2091 Timecode::Time timecode;
2094 nframes64_t frame_rate;
2097 if (_session == 0) {
2103 if (Profile->get_sae() || Profile->get_small_screen()) {
2104 m = ARDOUR_UI::instance()->primary_clock.mode();
2106 m = ARDOUR_UI::instance()->secondary_clock.mode();
2110 case AudioClock::BBT:
2111 _session->bbt_time (frame, bbt);
2112 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2115 case AudioClock::Timecode:
2116 _session->timecode_time (frame, timecode);
2117 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2120 case AudioClock::MinSec:
2121 /* XXX this is copied from show_verbose_duration_cursor() */
2122 frame_rate = _session->frame_rate();
2123 hours = frame / (frame_rate * 3600);
2124 frame = frame % (frame_rate * 3600);
2125 mins = frame / (frame_rate * 60);
2126 frame = frame % (frame_rate * 60);
2127 secs = (float) frame / (float) frame_rate;
2128 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2132 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2136 if (xpos >= 0 && ypos >=0) {
2137 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2139 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_position(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2141 show_verbose_canvas_cursor ();
2145 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
2148 Timecode::Time timecode;
2152 nframes64_t distance, frame_rate;
2154 Meter meter_at_start(_session->tempo_map().meter_at(start));
2156 if (_session == 0) {
2162 if (Profile->get_sae() || Profile->get_small_screen()) {
2163 m = ARDOUR_UI::instance()->primary_clock.mode ();
2165 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2169 case AudioClock::BBT:
2170 _session->bbt_time (start, sbbt);
2171 _session->bbt_time (end, ebbt);
2174 /* XXX this computation won't work well if the
2175 user makes a selection that spans any meter changes.
2178 ebbt.bars -= sbbt.bars;
2179 if (ebbt.beats >= sbbt.beats) {
2180 ebbt.beats -= sbbt.beats;
2183 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2185 if (ebbt.ticks >= sbbt.ticks) {
2186 ebbt.ticks -= sbbt.ticks;
2189 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2192 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2195 case AudioClock::Timecode:
2196 _session->timecode_duration (end - start, timecode);
2197 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2200 case AudioClock::MinSec:
2201 /* XXX this stuff should be elsewhere.. */
2202 distance = end - start;
2203 frame_rate = _session->frame_rate();
2204 hours = distance / (frame_rate * 3600);
2205 distance = distance % (frame_rate * 3600);
2206 mins = distance / (frame_rate * 60);
2207 distance = distance % (frame_rate * 60);
2208 secs = (float) distance / (float) frame_rate;
2209 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2213 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2217 if (xpos >= 0 && ypos >=0) {
2218 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2221 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2224 show_verbose_canvas_cursor ();
2228 Editor::collect_new_region_view (RegionView* rv)
2230 latest_regionviews.push_back (rv);
2234 Editor::collect_and_select_new_region_view (RegionView* rv)
2237 latest_regionviews.push_back (rv);
2241 Editor::cancel_selection ()
2243 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2244 (*i)->hide_selection ();
2247 selection->clear ();
2248 clicked_selection = 0;
2253 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction)
2255 boost::shared_ptr<Region> region (rv.region());
2257 if (region->locked()) {
2261 nframes64_t new_bound;
2264 TimeAxisView* tvp = clicked_axisview;
2265 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2267 if (tv && tv->is_track()) {
2268 speed = tv->track()->speed();
2271 if (left_direction) {
2272 if (swap_direction) {
2273 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2275 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2278 if (swap_direction) {
2279 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2281 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2285 region->trim_start ((nframes64_t) (new_bound * speed), this);
2286 rv.region_changed (PropertyChange (ARDOUR::Properties::start));
2290 Editor::single_start_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2292 boost::shared_ptr<Region> region (rv.region());
2294 if (region->locked()) {
2299 TimeAxisView* tvp = clicked_axisview;
2300 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2302 if (tv && tv->is_track()) {
2303 speed = tv->track()->speed();
2306 nframes64_t pre_trim_first_frame = region->first_frame();
2308 region->trim_front ((nframes64_t) (new_bound * speed), this);
2311 //Get the next region on the left of this region and shrink/expand it.
2312 boost::shared_ptr<Playlist> playlist (region->playlist());
2313 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2315 bool regions_touching = false;
2317 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2318 regions_touching = true;
2321 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2322 if (region_left != 0 &&
2323 (region_left->last_frame() > region->first_frame() || regions_touching))
2325 region_left->trim_end(region->first_frame() - 1, this);
2329 rv.region_changed (ARDOUR::bounds_change);
2333 Editor::single_end_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2335 boost::shared_ptr<Region> region (rv.region());
2337 if (region->locked()) {
2342 TimeAxisView* tvp = clicked_axisview;
2343 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2345 if (tv && tv->is_track()) {
2346 speed = tv->track()->speed();
2349 nframes64_t pre_trim_last_frame = region->last_frame();
2351 region->trim_end ((nframes64_t) (new_bound * speed), this);
2354 //Get the next region on the right of this region and shrink/expand it.
2355 boost::shared_ptr<Playlist> playlist (region->playlist());
2356 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2358 bool regions_touching = false;
2360 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)) {
2361 regions_touching = true;
2364 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2365 if (region_right != 0 &&
2366 (region_right->first_frame() < region->last_frame() || regions_touching))
2368 region_right->trim_front(region->last_frame() + 1, this);
2371 rv.region_changed (ARDOUR::bounds_change);
2374 rv.region_changed (PropertyChange (ARDOUR::Properties::length));
2380 Editor::point_trim (GdkEvent* event, nframes64_t new_bound)
2382 RegionView* rv = clicked_regionview;
2384 /* Choose action dependant on which button was pressed */
2385 switch (event->button.button) {
2387 begin_reversible_command (_("Start point trim"));
2389 if (selection->selected (rv)) {
2390 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2391 i != selection->regions.by_layer().end(); ++i)
2394 cerr << "region view contains null region" << endl;
2397 if (!(*i)->region()->locked()) {
2398 (*i)->region()->clear_changes ();
2399 (*i)->region()->trim_front (new_bound, this);
2400 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2405 if (!rv->region()->locked()) {
2406 rv->region()->clear_changes ();
2407 rv->region()->trim_front (new_bound, this);
2408 _session->add_command(new StatefulDiffCommand (rv->region()));
2412 commit_reversible_command();
2416 begin_reversible_command (_("End point trim"));
2418 if (selection->selected (rv)) {
2420 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2422 if (!(*i)->region()->locked()) {
2423 (*i)->region()->clear_changes();
2424 (*i)->region()->trim_end (new_bound, this);
2425 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2431 if (!rv->region()->locked()) {
2432 rv->region()->clear_changes ();
2433 rv->region()->trim_end (new_bound, this);
2434 _session->add_command (new StatefulDiffCommand (rv->region()));
2438 commit_reversible_command();
2447 Editor::thaw_region_after_trim (RegionView& rv)
2449 boost::shared_ptr<Region> region (rv.region());
2451 if (region->locked()) {
2455 region->resume_property_changes ();
2457 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2460 arv->unhide_envelope ();
2465 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2470 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2471 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2475 Location* location = find_location_from_marker (marker, is_start);
2476 location->set_hidden (true, this);
2481 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2483 double x1 = frame_to_pixel (start);
2484 double x2 = frame_to_pixel (end);
2485 double y2 = full_canvas_height - 1.0;
2487 zoom_rect->property_x1() = x1;
2488 zoom_rect->property_y1() = 1.0;
2489 zoom_rect->property_x2() = x2;
2490 zoom_rect->property_y2() = y2;
2495 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2497 using namespace Gtkmm2ext;
2499 ArdourPrompter prompter (false);
2501 prompter.set_prompt (_("Name for region:"));
2502 prompter.set_initial_text (clicked_regionview->region()->name());
2503 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2504 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2505 prompter.show_all ();
2506 switch (prompter.run ()) {
2507 case Gtk::RESPONSE_ACCEPT:
2509 prompter.get_result(str);
2511 clicked_regionview->region()->set_name (str);
2520 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2522 /* no brushing without a useful snap setting */
2524 switch (_snap_mode) {
2526 return; /* can't work because it allows region to be placed anywhere */
2531 switch (_snap_type) {
2539 /* don't brush a copy over the original */
2541 if (pos == rv->region()->position()) {
2545 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2547 if (rtv == 0 || !rtv->is_track()) {
2551 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2552 double speed = rtv->track()->speed();
2554 playlist->clear_changes ();
2555 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
2556 playlist->add_region (new_region, (nframes64_t) (pos * speed));
2557 _session->add_command (new StatefulDiffCommand (playlist));
2559 // playlist is frozen, so we have to update manually XXX this is disgusting
2561 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2565 Editor::track_height_step_timeout ()
2567 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2568 current_stepping_trackview = 0;
2575 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2577 assert (region_view);
2579 _region_motion_group->raise_to_top ();
2581 if (Config->get_edit_mode() == Splice) {
2582 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2584 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2585 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2588 /* sync the canvas to what we think is its current state */
2589 update_canvas_now();
2593 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2595 assert (region_view);
2597 _region_motion_group->raise_to_top ();
2599 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2600 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2604 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2606 assert (region_view);
2608 if (Config->get_edit_mode() == Splice) {
2612 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2613 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2615 begin_reversible_command (_("Drag region brush"));
2618 /** Start a grab where a time range is selected, track(s) are selected, and the
2619 * user clicks and drags a region with a modifier in order to create a new region containing
2620 * the section of the clicked region that lies within the time range.
2623 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2625 if (clicked_regionview == 0) {
2629 /* lets try to create new Region for the selection */
2631 vector<boost::shared_ptr<Region> > new_regions;
2632 create_region_from_selection (new_regions);
2634 if (new_regions.empty()) {
2638 /* XXX fix me one day to use all new regions */
2640 boost::shared_ptr<Region> region (new_regions.front());
2642 /* add it to the current stream/playlist.
2644 tricky: the streamview for the track will add a new regionview. we will
2645 catch the signal it sends when it creates the regionview to
2646 set the regionview we want to then drag.
2649 latest_regionviews.clear();
2650 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2652 /* A selection grab currently creates two undo/redo operations, one for
2653 creating the new region and another for moving it.
2656 begin_reversible_command (_("selection grab"));
2658 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2660 playlist->clear_changes ();
2661 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2662 _session->add_command(new StatefulDiffCommand (playlist));
2664 commit_reversible_command ();
2668 if (latest_regionviews.empty()) {
2669 /* something went wrong */
2673 /* we need to deselect all other regionviews, and select this one
2674 i'm ignoring undo stuff, because the region creation will take care of it
2676 selection->set (latest_regionviews);
2678 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2684 if (_drags->active ()) {
2687 selection->clear ();
2692 Editor::set_internal_edit (bool yn)
2694 _internal_editing = yn;
2697 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2698 mouse_select_button.get_image ()->show ();
2699 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2700 set_canvas_cursor ();
2702 /* deselect everything to avoid confusion when e.g. we can't now cut a previously selected
2703 region because cut means "cut note" rather than "cut region".
2705 selection->clear ();
2709 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2710 mouse_select_button.get_image ()->show ();
2711 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Select/Move Ranges"));
2712 mouse_mode_toggled (mouse_mode); // sets cursor
2716 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2717 * used by the `join object/range' tool mode.
2720 Editor::update_join_object_range_location (double x, double y)
2722 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2723 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2724 that we're over requires searching the playlist.
2727 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2728 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2732 if (mouse_mode == MouseObject) {
2733 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2734 } else if (mouse_mode == MouseRange) {
2735 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2738 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2739 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2743 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2748 rtv->canvas_display()->w2i (cx, cy);
2750 bool const top_half = cy < rtv->current_height () / 2;
2752 _join_object_range_state = top_half ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2758 Editor::effective_mouse_mode () const
2760 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2762 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2770 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2772 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2775 e->region_view().delete_note (e->note ());