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"
58 #include "ardour/types.h"
59 #include "ardour/profile.h"
60 #include "ardour/route.h"
61 #include "ardour/audio_track.h"
62 #include "ardour/audio_diskstream.h"
63 #include "ardour/midi_diskstream.h"
64 #include "ardour/playlist.h"
65 #include "ardour/audioplaylist.h"
66 #include "ardour/audioregion.h"
67 #include "ardour/midi_region.h"
68 #include "ardour/dB.h"
69 #include "ardour/utils.h"
70 #include "ardour/region_factory.h"
71 #include "ardour/source_factory.h"
72 #include "ardour/session.h"
79 using namespace ARDOUR;
82 using namespace Editing;
83 using Gtkmm2ext::Keyboard;
86 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
90 Gdk::ModifierType mask;
91 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
92 Glib::RefPtr<const Gdk::Window> pointer_window;
98 pointer_window = canvas_window->get_pointer (x, y, mask);
100 if (pointer_window == track_canvas->get_bin_window()) {
103 in_track_canvas = true;
106 in_track_canvas = false;
111 event.type = GDK_BUTTON_RELEASE;
115 where = event_frame (&event, 0, 0);
120 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
134 switch (event->type) {
135 case GDK_BUTTON_RELEASE:
136 case GDK_BUTTON_PRESS:
137 case GDK_2BUTTON_PRESS:
138 case GDK_3BUTTON_PRESS:
139 *pcx = event->button.x;
140 *pcy = event->button.y;
141 _trackview_group->w2i(*pcx, *pcy);
143 case GDK_MOTION_NOTIFY:
144 *pcx = event->motion.x;
145 *pcy = event->motion.y;
146 _trackview_group->w2i(*pcx, *pcy);
148 case GDK_ENTER_NOTIFY:
149 case GDK_LEAVE_NOTIFY:
150 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
153 case GDK_KEY_RELEASE:
154 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
157 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
161 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
162 position is negative (as can be the case with motion events in particular),
163 the frame location is always positive.
166 return pixel_to_frame (*pcx);
170 Editor::which_grabber_cursor ()
172 Gdk::Cursor* c = grabber_cursor;
174 if (_internal_editing) {
175 switch (mouse_mode) {
177 c = midi_pencil_cursor;
181 c = grabber_note_cursor;
185 c = midi_resize_cursor;
194 switch (_edit_point) {
196 c = grabber_edit_point_cursor;
207 Editor::set_canvas_cursor ()
209 if (_internal_editing) {
211 switch (mouse_mode) {
213 current_canvas_cursor = midi_pencil_cursor;
217 current_canvas_cursor = which_grabber_cursor();
221 current_canvas_cursor = midi_resize_cursor;
230 switch (mouse_mode) {
232 current_canvas_cursor = selector_cursor;
236 current_canvas_cursor = which_grabber_cursor();
240 current_canvas_cursor = cross_hair_cursor;
244 current_canvas_cursor = zoom_cursor;
248 current_canvas_cursor = time_fx_cursor; // just use playhead
252 current_canvas_cursor = speaker_cursor;
257 switch (_join_object_range_state) {
258 case JOIN_OBJECT_RANGE_NONE:
260 case JOIN_OBJECT_RANGE_OBJECT:
261 current_canvas_cursor = which_grabber_cursor ();
263 case JOIN_OBJECT_RANGE_RANGE:
264 current_canvas_cursor = selector_cursor;
269 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
274 Editor::set_mouse_mode (MouseMode m, bool force)
276 if (_drags->active ()) {
280 if (!force && m == mouse_mode) {
284 Glib::RefPtr<Action> act;
288 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
292 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
296 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
300 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
304 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
308 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
314 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
317 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
318 tact->set_active (false);
319 tact->set_active (true);
323 Editor::mouse_mode_toggled (MouseMode m)
329 cerr << "Mouse mode toggled to " << m << endl;
331 if (!internal_editing()) {
332 if (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
334 /* in all modes except range and joined object/range, hide the range selection,
335 show the object (region) selection.
338 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
339 (*i)->set_should_show_selection (true);
341 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
342 (*i)->hide_selection ();
348 in range or object/range mode, show the range selection.
351 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
352 (*i)->show_selection (selection->time);
357 set_canvas_cursor ();
361 Editor::step_mouse_mode (bool next)
363 switch (current_mouse_mode()) {
366 if (Profile->get_sae()) {
367 set_mouse_mode (MouseZoom);
369 set_mouse_mode (MouseRange);
372 set_mouse_mode (MouseTimeFX);
377 if (next) set_mouse_mode (MouseZoom);
378 else set_mouse_mode (MouseObject);
383 if (Profile->get_sae()) {
384 set_mouse_mode (MouseTimeFX);
386 set_mouse_mode (MouseGain);
389 if (Profile->get_sae()) {
390 set_mouse_mode (MouseObject);
392 set_mouse_mode (MouseRange);
398 if (next) set_mouse_mode (MouseTimeFX);
399 else set_mouse_mode (MouseZoom);
404 set_mouse_mode (MouseAudition);
406 if (Profile->get_sae()) {
407 set_mouse_mode (MouseZoom);
409 set_mouse_mode (MouseGain);
415 if (next) set_mouse_mode (MouseObject);
416 else set_mouse_mode (MouseTimeFX);
422 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
424 /* in object/audition/timefx/gain-automation mode,
425 any button press sets the selection if the object
426 can be selected. this is a bit of hack, because
427 we want to avoid this if the mouse operation is a
430 note: not dbl-click or triple-click
432 Also note that there is no region selection in internal edit mode, otherwise
433 for operations operating on the selection (e.g. cut) it is not obvious whether
434 to cut notes or regions.
437 if (((mouse_mode != MouseObject) &&
438 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
439 (mouse_mode != MouseAudition || item_type != RegionItem) &&
440 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
441 (mouse_mode != MouseGain) &&
442 (mouse_mode != MouseRange)) ||
443 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
444 internal_editing()) {
449 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
451 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
453 /* almost no selection action on modified button-2 or button-3 events */
455 if (item_type != RegionItem && event->button.button != 2) {
461 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
462 bool press = (event->type == GDK_BUTTON_PRESS);
464 // begin_reversible_command (_("select on click"));
468 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
469 set_selected_regionview_from_click (press, op, true);
470 } else if (event->type == GDK_BUTTON_PRESS) {
471 selection->clear_tracks ();
472 set_selected_track_as_side_effect (true);
474 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
475 clicked_selection = select_range_around_region (selection->regions.front());
479 case RegionViewNameHighlight:
481 case LeftFrameHandle:
482 case RightFrameHandle:
483 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
484 set_selected_regionview_from_click (press, op, true);
485 } else if (event->type == GDK_BUTTON_PRESS) {
486 set_selected_track_as_side_effect ();
491 case FadeInHandleItem:
493 case FadeOutHandleItem:
495 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
496 set_selected_regionview_from_click (press, op, true);
497 } else if (event->type == GDK_BUTTON_PRESS) {
498 set_selected_track_as_side_effect ();
502 case ControlPointItem:
503 set_selected_track_as_side_effect (true);
504 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
505 set_selected_control_point_from_click (op, false);
510 /* for context click, select track */
511 if (event->button.button == 3) {
512 selection->clear_tracks ();
513 set_selected_track_as_side_effect (true);
517 case AutomationTrackItem:
518 set_selected_track_as_side_effect (true);
527 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
529 /* single mouse clicks on any of these item types operate
530 independent of mouse mode, mostly because they are
531 not on the main track canvas or because we want
536 case PlayheadCursorItem:
537 _drags->set (new CursorDrag (this, item, true), event);
541 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
542 hide_marker (item, event);
544 _drags->set (new MarkerDrag (this, item), event);
548 case TempoMarkerItem:
550 new TempoMarkerDrag (
553 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
559 case MeterMarkerItem:
561 new MeterMarkerDrag (
564 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
573 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
574 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
580 case RangeMarkerBarItem:
581 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
582 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
584 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
589 case CdMarkerBarItem:
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::CreateCDMarker), event);
598 case TransportMarkerBarItem:
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::CreateTransportMarker), event);
611 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
612 /* special case: allow trim of range selections in joined object mode;
613 in theory eff should equal MouseRange in this case, but it doesn't
614 because entering the range selection canvas item results in entered_regionview
615 being set to 0, so update_join_object_range_location acts as if we aren't
618 if (item_type == StartSelectionTrimItem) {
619 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
620 } else if (item_type == EndSelectionTrimItem) {
621 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
625 Editing::MouseMode eff = effective_mouse_mode ();
627 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
628 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
635 case StartSelectionTrimItem:
636 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
639 case EndSelectionTrimItem:
640 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
644 if (Keyboard::modifier_state_contains
645 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
646 // contains and not equals because I can't use alt as a modifier alone.
647 start_selection_grab (item, event);
648 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
649 /* grab selection for moving */
650 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
652 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
653 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
655 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
656 if (join_object_range_button.get_active() && atv) {
657 /* smart "join" mode: drag automation */
658 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
660 /* this was debated, but decided the more common action was to
661 make a new selection */
662 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
669 if (internal_editing()) {
670 /* trim notes if we're in internal edit mode and near the ends of the note */
671 _drags->set (new NoteResizeDrag (this, item), event);
676 if (internal_editing()) {
677 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
680 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
685 case RegionViewNameHighlight:
686 case LeftFrameHandle:
687 case RightFrameHandle:
689 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
690 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
696 if (!internal_editing()) {
697 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
706 if (internal_editing()) {
707 _drags->set (new NoteDrag (this, item), event);
716 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
717 event->type == GDK_BUTTON_PRESS) {
719 _drags->set (new RubberbandSelectDrag (this, item), event);
721 } else if (event->type == GDK_BUTTON_PRESS) {
724 case FadeInHandleItem:
726 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
727 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
731 case FadeOutHandleItem:
733 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
734 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
738 case FeatureLineItem:
740 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
741 remove_transient(item);
745 _drags->set (new FeatureLineDrag (this, item), event);
751 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
752 /* click on an automation region view; do nothing here and let the ARV's signal handler
758 /* click on a normal region view */
759 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
760 add_region_copy_drag (item, event, clicked_regionview);
762 else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
763 add_region_brush_drag (item, event, clicked_regionview);
765 add_region_drag (item, event, clicked_regionview);
768 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
769 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
772 _drags->start_grab (event);
775 case RegionViewNameHighlight:
776 case LeftFrameHandle:
777 case RightFrameHandle:
778 if (!internal_editing ()) {
779 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
780 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
787 /* rename happens on edit clicks */
788 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
789 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
794 case ControlPointItem:
795 _drags->set (new ControlPointDrag (this, item), event);
799 case AutomationLineItem:
800 _drags->set (new LineDrag (this, item), event);
805 if (internal_editing()) {
806 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
809 _drags->set (new RubberbandSelectDrag (this, item), event);
813 case AutomationTrackItem:
814 /* rubberband drag to select automation points */
815 _drags->set (new RubberbandSelectDrag (this, item), event);
820 if (join_object_range_button.get_active()) {
821 /* we're in "smart" joined mode, and we've clicked on a Selection */
822 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
823 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
825 /* if we're over an automation track, start a drag of its data */
826 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
828 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
831 /* if we're over a track and a region, and in the `object' part of a region,
832 put a selection around the region and drag both
834 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
835 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
836 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
838 boost::shared_ptr<Playlist> pl = t->playlist ();
841 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
843 RegionView* rv = rtv->view()->find_view (r);
844 clicked_selection = select_range_around_region (rv);
845 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
846 list<RegionView*> rvs;
848 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
849 _drags->start_grab (event);
860 case ImageFrameHandleStartItem:
861 imageframe_start_handle_op(item, event) ;
864 case ImageFrameHandleEndItem:
865 imageframe_end_handle_op(item, event) ;
868 case MarkerViewHandleStartItem:
869 markerview_item_start_handle_op(item, event) ;
872 case MarkerViewHandleEndItem:
873 markerview_item_end_handle_op(item, event) ;
877 start_markerview_grab(item, event) ;
880 start_imageframe_grab(item, event) ;
898 /* start a grab so that if we finish after moving
899 we can tell what happened.
901 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
905 _drags->set (new LineDrag (this, item), event);
908 case ControlPointItem:
909 _drags->set (new ControlPointDrag (this, item), event);
920 case ControlPointItem:
921 _drags->set (new ControlPointDrag (this, item), event);
924 case AutomationLineItem:
925 _drags->set (new LineDrag (this, item), event);
929 // XXX need automation mode to identify which
931 // start_line_grab_from_regionview (item, event);
941 if (event->type == GDK_BUTTON_PRESS) {
942 _drags->set (new MouseZoomDrag (this, item), event);
949 if (internal_editing() && item_type == NoteItem) {
950 /* drag notes if we're in internal edit mode */
951 _drags->set (new NoteResizeDrag (this, item), event);
953 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
954 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
955 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
961 _drags->set (new ScrubDrag (this, item), event);
963 scrub_reverse_distance = 0;
964 last_scrub_x = event->button.x;
965 scrubbing_direction = 0;
966 track_canvas->get_window()->set_cursor (*transparent_cursor);
978 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
980 Editing::MouseMode const eff = effective_mouse_mode ();
985 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
986 add_region_copy_drag (item, event, clicked_regionview);
988 add_region_drag (item, event, clicked_regionview);
990 _drags->start_grab (event);
993 case ControlPointItem:
994 _drags->set (new ControlPointDrag (this, item), event);
1002 switch (item_type) {
1003 case RegionViewNameHighlight:
1004 case LeftFrameHandle:
1005 case RightFrameHandle:
1006 if (!internal_editing ()) {
1007 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1012 case RegionViewName:
1013 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1024 /* relax till release */
1030 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1031 temporal_zoom_session();
1033 temporal_zoom_to_frame (true, event_frame(event));
1046 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1048 if (event->type != GDK_BUTTON_PRESS) {
1052 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1054 if (canvas_window) {
1055 Glib::RefPtr<const Gdk::Window> pointer_window;
1058 Gdk::ModifierType mask;
1060 pointer_window = canvas_window->get_pointer (x, y, mask);
1062 if (pointer_window == track_canvas->get_bin_window()) {
1063 track_canvas->window_to_world (x, y, wx, wy);
1067 track_canvas->grab_focus();
1069 if (_session && _session->actively_recording()) {
1073 button_selection (item, event, item_type);
1075 if (!_drags->active () &&
1076 (Keyboard::is_delete_event (&event->button) ||
1077 Keyboard::is_context_menu_event (&event->button) ||
1078 Keyboard::is_edit_event (&event->button))) {
1080 /* handled by button release */
1084 switch (event->button.button) {
1086 return button_press_handler_1 (item, event, item_type);
1090 return button_press_handler_2 (item, event, item_type);
1105 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1107 nframes64_t where = event_frame (event, 0, 0);
1108 AutomationTimeAxisView* atv = 0;
1110 /* no action if we're recording */
1112 if (_session && _session->actively_recording()) {
1116 /* see if we're finishing a drag */
1118 bool were_dragging = false;
1119 if (_drags->active ()) {
1120 bool const r = _drags->end_grab (event);
1122 /* grab dragged, so do nothing else */
1126 were_dragging = true;
1129 /* edit events get handled here */
1131 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1132 switch (item_type) {
1137 case TempoMarkerItem:
1138 edit_tempo_marker (item);
1141 case MeterMarkerItem:
1142 edit_meter_marker (item);
1145 case RegionViewName:
1146 if (clicked_regionview->name_active()) {
1147 return mouse_rename_region (item, event);
1151 case ControlPointItem:
1152 edit_control_point (item);
1161 /* context menu events get handled here */
1163 if (Keyboard::is_context_menu_event (&event->button)) {
1165 if (!_drags->active ()) {
1167 /* no matter which button pops up the context menu, tell the menu
1168 widget to use button 1 to drive menu selection.
1171 switch (item_type) {
1173 case FadeInHandleItem:
1175 case FadeOutHandleItem:
1176 popup_fade_context_menu (1, event->button.time, item, item_type);
1180 popup_track_context_menu (1, event->button.time, item_type, false, where);
1184 case RegionViewNameHighlight:
1185 case LeftFrameHandle:
1186 case RightFrameHandle:
1187 case RegionViewName:
1188 popup_track_context_menu (1, event->button.time, item_type, false, where);
1192 popup_track_context_menu (1, event->button.time, item_type, true, where);
1195 case AutomationTrackItem:
1196 popup_track_context_menu (1, event->button.time, item_type, false, where);
1200 case RangeMarkerBarItem:
1201 case TransportMarkerBarItem:
1202 case CdMarkerBarItem:
1205 popup_ruler_menu (where, item_type);
1209 marker_context_menu (&event->button, item);
1212 case TempoMarkerItem:
1213 tempo_or_meter_marker_context_menu (&event->button, item);
1216 case MeterMarkerItem:
1217 tempo_or_meter_marker_context_menu (&event->button, item);
1220 case CrossfadeViewItem:
1221 popup_track_context_menu (1, event->button.time, item_type, false, where);
1225 case ImageFrameItem:
1226 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1228 case ImageFrameTimeAxisItem:
1229 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1231 case MarkerViewItem:
1232 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1234 case MarkerTimeAxisItem:
1235 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1247 /* delete events get handled here */
1249 Editing::MouseMode const eff = effective_mouse_mode ();
1251 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1253 switch (item_type) {
1254 case TempoMarkerItem:
1255 remove_tempo_marker (item);
1258 case MeterMarkerItem:
1259 remove_meter_marker (item);
1263 remove_marker (*item, event);
1267 if (eff == MouseObject) {
1268 remove_clicked_region ();
1272 case ControlPointItem:
1273 if (eff == MouseGain) {
1274 remove_gain_control_point (item, event);
1276 remove_control_point (item, event);
1281 remove_midi_note (item, event);
1290 switch (event->button.button) {
1293 switch (item_type) {
1294 /* see comments in button_press_handler */
1295 case PlayheadCursorItem:
1298 case AutomationLineItem:
1299 case StartSelectionTrimItem:
1300 case EndSelectionTrimItem:
1304 if (!_dragging_playhead) {
1305 snap_to_with_modifier (where, event, 0, true);
1306 mouse_add_new_marker (where);
1310 case CdMarkerBarItem:
1311 if (!_dragging_playhead) {
1312 // if we get here then a dragged range wasn't done
1313 snap_to_with_modifier (where, event, 0, true);
1314 mouse_add_new_marker (where, true);
1319 if (!_dragging_playhead) {
1320 snap_to_with_modifier (where, event);
1321 mouse_add_new_tempo_event (where);
1326 if (!_dragging_playhead) {
1327 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1338 switch (item_type) {
1339 case AutomationTrackItem:
1340 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1342 atv->add_automation_event (item, event, where, event->button.y);
1353 switch (item_type) {
1356 /* check that we didn't drag before releasing, since
1357 its really annoying to create new control
1358 points when doing this.
1360 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1361 if (were_dragging && arv) {
1362 arv->add_gain_point_event (item, event);
1368 case AutomationTrackItem:
1369 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1370 add_automation_event (item, event, where, event->button.y);
1379 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1380 if (scrubbing_direction == 0) {
1381 /* no drag, just a click */
1382 switch (item_type) {
1384 play_selected_region ();
1390 /* make sure we stop */
1391 _session->request_transport_speed (0.0);
1408 switch (item_type) {
1410 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1412 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1415 // Button2 click is unused
1428 // x_style_paste (where, 1.0);
1448 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1455 if (last_item_entered != item) {
1456 last_item_entered = item;
1457 last_item_entered_n = 0;
1460 switch (item_type) {
1461 case ControlPointItem:
1462 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1463 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1464 cp->set_visible (true);
1468 at_y = cp->get_y ();
1469 cp->i2w (at_x, at_y);
1473 fraction = 1.0 - (cp->get_y() / cp->line().height());
1475 if (is_drawable() && !_drags->active ()) {
1476 track_canvas->get_window()->set_cursor (*fader_cursor);
1479 last_item_entered_n++;
1480 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1481 if (last_item_entered_n < 10) {
1482 show_verbose_canvas_cursor ();
1488 if (mouse_mode == MouseGain) {
1489 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1491 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1492 if (is_drawable()) {
1493 track_canvas->get_window()->set_cursor (*fader_cursor);
1498 case AutomationLineItem:
1499 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1501 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1503 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1505 if (is_drawable()) {
1506 track_canvas->get_window()->set_cursor (*fader_cursor);
1511 case RegionViewNameHighlight:
1512 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1513 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1517 case LeftFrameHandle:
1518 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1519 track_canvas->get_window()->set_cursor (*left_side_trim_cursor);
1523 case RightFrameHandle:
1524 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1525 track_canvas->get_window()->set_cursor (*right_side_trim_cursor);
1529 case StartSelectionTrimItem:
1530 case EndSelectionTrimItem:
1533 case ImageFrameHandleStartItem:
1534 case ImageFrameHandleEndItem:
1535 case MarkerViewHandleStartItem:
1536 case MarkerViewHandleEndItem:
1539 if (is_drawable()) {
1540 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1544 case PlayheadCursorItem:
1545 if (is_drawable()) {
1546 switch (_edit_point) {
1548 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1551 track_canvas->get_window()->set_cursor (*grabber_cursor);
1557 case RegionViewName:
1559 /* when the name is not an active item, the entire name highlight is for trimming */
1561 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1562 if (mouse_mode == MouseObject && is_drawable()) {
1563 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1569 case AutomationTrackItem:
1570 if (is_drawable()) {
1571 Gdk::Cursor *cursor;
1572 switch (mouse_mode) {
1574 cursor = selector_cursor;
1577 cursor = zoom_cursor;
1580 cursor = cross_hair_cursor;
1584 track_canvas->get_window()->set_cursor (*cursor);
1586 AutomationTimeAxisView* atv;
1587 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1588 clear_entered_track = false;
1589 set_entered_track (atv);
1595 case RangeMarkerBarItem:
1596 case TransportMarkerBarItem:
1597 case CdMarkerBarItem:
1600 if (is_drawable()) {
1601 track_canvas->get_window()->set_cursor (*timebar_cursor);
1606 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1609 entered_marker = marker;
1610 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1612 case MeterMarkerItem:
1613 case TempoMarkerItem:
1614 if (is_drawable()) {
1615 track_canvas->get_window()->set_cursor (*timebar_cursor);
1619 case FadeInHandleItem:
1620 if (mouse_mode == MouseObject && !internal_editing()) {
1621 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1623 rect->property_fill_color_rgba() = 0;
1624 rect->property_outline_pixels() = 1;
1626 track_canvas->get_window()->set_cursor (*fade_in_cursor);
1630 case FadeOutHandleItem:
1631 if (mouse_mode == MouseObject && !internal_editing()) {
1632 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1634 rect->property_fill_color_rgba() = 0;
1635 rect->property_outline_pixels() = 1;
1637 track_canvas->get_window()->set_cursor (*fade_out_cursor);
1640 case FeatureLineItem:
1642 ArdourCanvas::SimpleLine *line = dynamic_cast<ArdourCanvas::SimpleLine *> (item);
1643 line->property_color_rgba() = 0xFF0000FF;
1650 /* second pass to handle entered track status in a comprehensible way.
1653 switch (item_type) {
1655 case AutomationLineItem:
1656 case ControlPointItem:
1657 /* these do not affect the current entered track state */
1658 clear_entered_track = false;
1661 case AutomationTrackItem:
1662 /* handled above already */
1666 set_entered_track (0);
1674 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1684 switch (item_type) {
1685 case ControlPointItem:
1686 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1687 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1688 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1689 cp->set_visible (false);
1693 if (is_drawable()) {
1694 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1697 hide_verbose_canvas_cursor ();
1700 case RegionViewNameHighlight:
1701 case LeftFrameHandle:
1702 case RightFrameHandle:
1703 case StartSelectionTrimItem:
1704 case EndSelectionTrimItem:
1705 case PlayheadCursorItem:
1708 case ImageFrameHandleStartItem:
1709 case ImageFrameHandleEndItem:
1710 case MarkerViewHandleStartItem:
1711 case MarkerViewHandleEndItem:
1714 if (is_drawable()) {
1715 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1720 case AutomationLineItem:
1721 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1723 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1725 line->property_fill_color_rgba() = al->get_line_color();
1727 if (is_drawable()) {
1728 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1732 case RegionViewName:
1733 /* see enter_handler() for notes */
1734 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1735 if (is_drawable() && mouse_mode == MouseObject) {
1736 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1741 case RangeMarkerBarItem:
1742 case TransportMarkerBarItem:
1743 case CdMarkerBarItem:
1747 if (is_drawable()) {
1748 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1753 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1757 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1758 location_flags_changed (loc, this);
1761 case MeterMarkerItem:
1762 case TempoMarkerItem:
1764 if (is_drawable()) {
1765 track_canvas->get_window()->set_cursor (*timebar_cursor);
1770 case FadeInHandleItem:
1771 case FadeOutHandleItem:
1772 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1774 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1776 rect->property_fill_color_rgba() = rv->get_fill_color();
1777 rect->property_outline_pixels() = 0;
1780 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1783 case AutomationTrackItem:
1784 if (is_drawable()) {
1785 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1786 clear_entered_track = true;
1787 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1790 case FeatureLineItem:
1792 ArdourCanvas::SimpleLine *line = dynamic_cast<ArdourCanvas::SimpleLine *> (item);
1793 line->property_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
1805 Editor::left_automation_track ()
1807 if (clear_entered_track) {
1808 set_entered_track (0);
1809 clear_entered_track = false;
1815 Editor::scrub (nframes64_t frame, double current_x)
1819 if (scrubbing_direction == 0) {
1821 _session->request_locate (frame, false);
1822 _session->request_transport_speed (0.1);
1823 scrubbing_direction = 1;
1827 if (last_scrub_x > current_x) {
1829 /* pointer moved to the left */
1831 if (scrubbing_direction > 0) {
1833 /* we reversed direction to go backwards */
1836 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1840 /* still moving to the left (backwards) */
1842 scrub_reversals = 0;
1843 scrub_reverse_distance = 0;
1845 delta = 0.01 * (last_scrub_x - current_x);
1846 _session->request_transport_speed (_session->transport_speed() - delta);
1850 /* pointer moved to the right */
1852 if (scrubbing_direction < 0) {
1853 /* we reversed direction to go forward */
1856 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1859 /* still moving to the right */
1861 scrub_reversals = 0;
1862 scrub_reverse_distance = 0;
1864 delta = 0.01 * (current_x - last_scrub_x);
1865 _session->request_transport_speed (_session->transport_speed() + delta);
1869 /* if there have been more than 2 opposite motion moves detected, or one that moves
1870 back more than 10 pixels, reverse direction
1873 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1875 if (scrubbing_direction > 0) {
1876 /* was forwards, go backwards */
1877 _session->request_transport_speed (-0.1);
1878 scrubbing_direction = -1;
1880 /* was backwards, go forwards */
1881 _session->request_transport_speed (0.1);
1882 scrubbing_direction = 1;
1885 scrub_reverse_distance = 0;
1886 scrub_reversals = 0;
1890 last_scrub_x = current_x;
1894 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1896 if (event->motion.is_hint) {
1899 /* We call this so that MOTION_NOTIFY events continue to be
1900 delivered to the canvas. We need to do this because we set
1901 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1902 the density of the events, at the expense of a round-trip
1903 to the server. Given that this will mostly occur on cases
1904 where DISPLAY = :0.0, and given the cost of what the motion
1905 event might do, its a good tradeoff.
1908 track_canvas->get_pointer (x, y);
1911 if (current_stepping_trackview) {
1912 /* don't keep the persistent stepped trackview if the mouse moves */
1913 current_stepping_trackview = 0;
1914 step_timeout.disconnect ();
1917 if (_session && _session->actively_recording()) {
1918 /* Sorry. no dragging stuff around while we record */
1922 JoinObjectRangeState const old = _join_object_range_state;
1923 update_join_object_range_location (event->motion.x, event->motion.y);
1924 if (_join_object_range_state != old) {
1925 set_canvas_cursor ();
1928 bool handled = false;
1929 if (_drags->active ()) {
1930 handled = _drags->motion_handler (event, from_autoscroll);
1937 track_canvas_motion (event);
1942 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1944 ControlPoint* control_point;
1946 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1947 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1951 // We shouldn't remove the first or last gain point
1952 if (control_point->line().is_last_point(*control_point) ||
1953 control_point->line().is_first_point(*control_point)) {
1957 control_point->line().remove_point (*control_point);
1961 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1963 ControlPoint* control_point;
1965 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1966 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1970 control_point->line().remove_point (*control_point);
1974 Editor::edit_control_point (ArdourCanvas::Item* item)
1976 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1979 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1983 ControlPointDialog d (p);
1984 d.set_position (Gtk::WIN_POS_MOUSE);
1987 if (d.run () != RESPONSE_ACCEPT) {
1991 p->line().modify_point_y (*p, d.get_y_fraction ());
1996 Editor::visible_order_range (int* low, int* high) const
1998 *low = TimeAxisView::max_order ();
2001 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2003 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2005 if (!rtv->hidden()) {
2007 if (*high < rtv->order()) {
2008 *high = rtv->order ();
2011 if (*low > rtv->order()) {
2012 *low = rtv->order ();
2019 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2021 /* Either add to or set the set the region selection, unless
2022 this is an alignment click (control used)
2025 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2026 TimeAxisView* tv = &rv.get_time_axis_view();
2027 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2029 if (rtv && rtv->is_track()) {
2030 speed = rtv->track()->speed();
2033 nframes64_t where = get_preferred_edit_position();
2037 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2039 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
2041 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2043 align_region (rv.region(), End, (nframes64_t) (where * speed));
2047 align_region (rv.region(), Start, (nframes64_t) (where * speed));
2054 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
2057 Timecode::Time timecode;
2060 nframes64_t frame_rate;
2063 if (_session == 0) {
2069 if (Profile->get_sae() || Profile->get_small_screen()) {
2070 m = ARDOUR_UI::instance()->primary_clock.mode();
2072 m = ARDOUR_UI::instance()->secondary_clock.mode();
2076 case AudioClock::BBT:
2077 _session->bbt_time (frame, bbt);
2078 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2081 case AudioClock::Timecode:
2082 _session->timecode_time (frame, timecode);
2083 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2086 case AudioClock::MinSec:
2087 /* XXX this is copied from show_verbose_duration_cursor() */
2088 frame_rate = _session->frame_rate();
2089 hours = frame / (frame_rate * 3600);
2090 frame = frame % (frame_rate * 3600);
2091 mins = frame / (frame_rate * 60);
2092 frame = frame % (frame_rate * 60);
2093 secs = (float) frame / (float) frame_rate;
2094 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2098 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2102 if (xpos >= 0 && ypos >=0) {
2103 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2105 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_position(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2107 show_verbose_canvas_cursor ();
2111 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
2114 Timecode::Time timecode;
2118 nframes64_t distance, frame_rate;
2120 Meter meter_at_start(_session->tempo_map().meter_at(start));
2122 if (_session == 0) {
2128 if (Profile->get_sae() || Profile->get_small_screen()) {
2129 m = ARDOUR_UI::instance()->primary_clock.mode ();
2131 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2135 case AudioClock::BBT:
2136 _session->bbt_time (start, sbbt);
2137 _session->bbt_time (end, ebbt);
2140 /* XXX this computation won't work well if the
2141 user makes a selection that spans any meter changes.
2144 ebbt.bars -= sbbt.bars;
2145 if (ebbt.beats >= sbbt.beats) {
2146 ebbt.beats -= sbbt.beats;
2149 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2151 if (ebbt.ticks >= sbbt.ticks) {
2152 ebbt.ticks -= sbbt.ticks;
2155 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2158 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2161 case AudioClock::Timecode:
2162 _session->timecode_duration (end - start, timecode);
2163 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2166 case AudioClock::MinSec:
2167 /* XXX this stuff should be elsewhere.. */
2168 distance = end - start;
2169 frame_rate = _session->frame_rate();
2170 hours = distance / (frame_rate * 3600);
2171 distance = distance % (frame_rate * 3600);
2172 mins = distance / (frame_rate * 60);
2173 distance = distance % (frame_rate * 60);
2174 secs = (float) distance / (float) frame_rate;
2175 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2179 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2183 if (xpos >= 0 && ypos >=0) {
2184 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2187 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2190 show_verbose_canvas_cursor ();
2194 Editor::collect_new_region_view (RegionView* rv)
2196 latest_regionviews.push_back (rv);
2200 Editor::collect_and_select_new_region_view (RegionView* rv)
2203 latest_regionviews.push_back (rv);
2207 Editor::cancel_selection ()
2209 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2210 (*i)->hide_selection ();
2213 selection->clear ();
2214 clicked_selection = 0;
2219 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction)
2221 boost::shared_ptr<Region> region (rv.region());
2223 if (region->locked()) {
2227 nframes64_t new_bound;
2230 TimeAxisView* tvp = clicked_axisview;
2231 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2233 if (tv && tv->is_track()) {
2234 speed = tv->track()->speed();
2237 if (left_direction) {
2238 if (swap_direction) {
2239 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2241 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2244 if (swap_direction) {
2245 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2247 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2251 region->trim_start ((nframes64_t) (new_bound * speed), this);
2252 rv.region_changed (PropertyChange (ARDOUR::Properties::start));
2256 Editor::single_start_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2258 boost::shared_ptr<Region> region (rv.region());
2260 if (region->locked()) {
2265 TimeAxisView* tvp = clicked_axisview;
2266 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2268 if (tv && tv->is_track()) {
2269 speed = tv->track()->speed();
2272 nframes64_t pre_trim_first_frame = region->first_frame();
2274 region->trim_front ((nframes64_t) (new_bound * speed), this);
2277 //Get the next region on the left of this region and shrink/expand it.
2278 boost::shared_ptr<Playlist> playlist (region->playlist());
2279 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2281 bool regions_touching = false;
2283 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2284 regions_touching = true;
2287 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2288 if (region_left != 0 &&
2289 (region_left->last_frame() > region->first_frame() || regions_touching))
2291 region_left->trim_end(region->first_frame() - 1, this);
2295 rv.region_changed (ARDOUR::bounds_change);
2299 Editor::single_end_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2301 boost::shared_ptr<Region> region (rv.region());
2303 if (region->locked()) {
2308 TimeAxisView* tvp = clicked_axisview;
2309 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2311 if (tv && tv->is_track()) {
2312 speed = tv->track()->speed();
2315 nframes64_t pre_trim_last_frame = region->last_frame();
2317 region->trim_end ((nframes64_t) (new_bound * speed), this);
2320 //Get the next region on the right of this region and shrink/expand it.
2321 boost::shared_ptr<Playlist> playlist (region->playlist());
2322 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2324 bool regions_touching = false;
2326 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)) {
2327 regions_touching = true;
2330 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2331 if (region_right != 0 &&
2332 (region_right->first_frame() < region->last_frame() || regions_touching))
2334 region_right->trim_front(region->last_frame() + 1, this);
2337 rv.region_changed (ARDOUR::bounds_change);
2340 rv.region_changed (PropertyChange (ARDOUR::Properties::length));
2346 Editor::point_trim (GdkEvent* event, nframes64_t new_bound)
2348 RegionView* rv = clicked_regionview;
2350 /* Choose action dependant on which button was pressed */
2351 switch (event->button.button) {
2353 begin_reversible_command (_("Start point trim"));
2355 if (selection->selected (rv)) {
2356 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2357 i != selection->regions.by_layer().end(); ++i)
2360 cerr << "region view contains null region" << endl;
2363 if (!(*i)->region()->locked()) {
2364 (*i)->region()->clear_history ();
2365 (*i)->region()->trim_front (new_bound, this);
2366 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2371 if (!rv->region()->locked()) {
2372 rv->region()->clear_history ();
2373 rv->region()->trim_front (new_bound, this);
2374 _session->add_command(new StatefulDiffCommand (rv->region()));
2378 commit_reversible_command();
2382 begin_reversible_command (_("End point trim"));
2384 if (selection->selected (rv)) {
2386 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2388 if (!(*i)->region()->locked()) {
2389 (*i)->region()->clear_history();
2390 (*i)->region()->trim_end (new_bound, this);
2391 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2397 if (!rv->region()->locked()) {
2398 rv->region()->clear_history ();
2399 rv->region()->trim_end (new_bound, this);
2400 _session->add_command (new StatefulDiffCommand (rv->region()));
2404 commit_reversible_command();
2413 Editor::thaw_region_after_trim (RegionView& rv)
2415 boost::shared_ptr<Region> region (rv.region());
2417 if (region->locked()) {
2421 region->resume_property_changes ();
2423 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2426 arv->unhide_envelope ();
2431 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2436 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2437 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2441 Location* location = find_location_from_marker (marker, is_start);
2442 location->set_hidden (true, this);
2447 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2449 double x1 = frame_to_pixel (start);
2450 double x2 = frame_to_pixel (end);
2451 double y2 = full_canvas_height - 1.0;
2453 zoom_rect->property_x1() = x1;
2454 zoom_rect->property_y1() = 1.0;
2455 zoom_rect->property_x2() = x2;
2456 zoom_rect->property_y2() = y2;
2461 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2463 using namespace Gtkmm2ext;
2465 ArdourPrompter prompter (false);
2467 prompter.set_prompt (_("Name for region:"));
2468 prompter.set_initial_text (clicked_regionview->region()->name());
2469 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2470 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2471 prompter.show_all ();
2472 switch (prompter.run ()) {
2473 case Gtk::RESPONSE_ACCEPT:
2475 prompter.get_result(str);
2477 clicked_regionview->region()->set_name (str);
2486 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2488 /* no brushing without a useful snap setting */
2490 switch (_snap_mode) {
2492 return; /* can't work because it allows region to be placed anywhere */
2497 switch (_snap_type) {
2505 /* don't brush a copy over the original */
2507 if (pos == rv->region()->position()) {
2511 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2513 if (rtv == 0 || !rtv->is_track()) {
2517 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2518 double speed = rtv->track()->speed();
2520 playlist->clear_history ();
2521 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
2522 playlist->add_region (new_region, (nframes64_t) (pos * speed));
2523 _session->add_command (new StatefulDiffCommand (playlist));
2525 // playlist is frozen, so we have to update manually XXX this is disgusting
2527 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2531 Editor::track_height_step_timeout ()
2533 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2534 current_stepping_trackview = 0;
2541 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2543 assert (region_view);
2545 _region_motion_group->raise_to_top ();
2547 if (Config->get_edit_mode() == Splice) {
2548 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2550 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2551 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2554 /* sync the canvas to what we think is its current state */
2555 update_canvas_now();
2559 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2561 assert (region_view);
2563 _region_motion_group->raise_to_top ();
2565 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2566 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2570 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2572 assert (region_view);
2574 if (Config->get_edit_mode() == Splice) {
2578 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2579 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2581 begin_reversible_command (_("Drag region brush"));
2584 /** Start a grab where a time range is selected, track(s) are selected, and the
2585 * user clicks and drags a region with a modifier in order to create a new region containing
2586 * the section of the clicked region that lies within the time range.
2589 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2591 if (clicked_regionview == 0) {
2595 /* lets try to create new Region for the selection */
2597 vector<boost::shared_ptr<Region> > new_regions;
2598 create_region_from_selection (new_regions);
2600 if (new_regions.empty()) {
2604 /* XXX fix me one day to use all new regions */
2606 boost::shared_ptr<Region> region (new_regions.front());
2608 /* add it to the current stream/playlist.
2610 tricky: the streamview for the track will add a new regionview. we will
2611 catch the signal it sends when it creates the regionview to
2612 set the regionview we want to then drag.
2615 latest_regionviews.clear();
2616 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2618 /* A selection grab currently creates two undo/redo operations, one for
2619 creating the new region and another for moving it.
2622 begin_reversible_command (_("selection grab"));
2624 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2626 playlist->clear_history ();
2627 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2628 _session->add_command(new StatefulDiffCommand (playlist));
2630 commit_reversible_command ();
2634 if (latest_regionviews.empty()) {
2635 /* something went wrong */
2639 /* we need to deselect all other regionviews, and select this one
2640 i'm ignoring undo stuff, because the region creation will take care of it
2642 selection->set (latest_regionviews);
2644 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2650 if (_drags->active ()) {
2653 selection->clear ();
2658 Editor::set_internal_edit (bool yn)
2660 _internal_editing = yn;
2663 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2664 mouse_select_button.get_image ()->show ();
2665 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2666 set_canvas_cursor ();
2668 /* deselect everything to avoid confusion when e.g. we can't now cut a previously selected
2669 region because cut means "cut note" rather than "cut region".
2671 selection->clear ();
2675 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2676 mouse_select_button.get_image ()->show ();
2677 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Select/Move Ranges"));
2678 mouse_mode_toggled (mouse_mode); // sets cursor
2682 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2683 * used by the `join object/range' tool mode.
2686 Editor::update_join_object_range_location (double x, double y)
2688 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2689 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2690 that we're over requires searching the playlist.
2693 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2694 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2698 if (mouse_mode == MouseObject) {
2699 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2700 } else if (mouse_mode == MouseRange) {
2701 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2704 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2705 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2709 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2714 rtv->canvas_display()->w2i (cx, cy);
2716 bool const top_half = cy < rtv->current_height () / 2;
2718 _join_object_range_state = top_half ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2724 Editor::effective_mouse_mode () const
2726 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2728 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2736 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2738 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2741 e->region_view().delete_note (e->note ());