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 /* first, 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 button_selection (item, event, item_type);
1131 /* edit events get handled here */
1133 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1134 switch (item_type) {
1139 case TempoMarkerItem:
1140 edit_tempo_marker (item);
1143 case MeterMarkerItem:
1144 edit_meter_marker (item);
1147 case RegionViewName:
1148 if (clicked_regionview->name_active()) {
1149 return mouse_rename_region (item, event);
1153 case ControlPointItem:
1154 edit_control_point (item);
1163 /* context menu events get handled here */
1165 if (Keyboard::is_context_menu_event (&event->button)) {
1167 if (!_drags->active ()) {
1169 /* no matter which button pops up the context menu, tell the menu
1170 widget to use button 1 to drive menu selection.
1173 switch (item_type) {
1175 case FadeInHandleItem:
1177 case FadeOutHandleItem:
1178 popup_fade_context_menu (1, event->button.time, item, item_type);
1182 popup_track_context_menu (1, event->button.time, item_type, false, where);
1186 case RegionViewNameHighlight:
1187 case LeftFrameHandle:
1188 case RightFrameHandle:
1189 case RegionViewName:
1190 popup_track_context_menu (1, event->button.time, item_type, false, where);
1194 popup_track_context_menu (1, event->button.time, item_type, true, where);
1197 case AutomationTrackItem:
1198 popup_track_context_menu (1, event->button.time, item_type, false, where);
1202 case RangeMarkerBarItem:
1203 case TransportMarkerBarItem:
1204 case CdMarkerBarItem:
1207 popup_ruler_menu (where, item_type);
1211 marker_context_menu (&event->button, item);
1214 case TempoMarkerItem:
1215 tempo_or_meter_marker_context_menu (&event->button, item);
1218 case MeterMarkerItem:
1219 tempo_or_meter_marker_context_menu (&event->button, item);
1222 case CrossfadeViewItem:
1223 popup_track_context_menu (1, event->button.time, item_type, false, where);
1227 case ImageFrameItem:
1228 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1230 case ImageFrameTimeAxisItem:
1231 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1233 case MarkerViewItem:
1234 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1236 case MarkerTimeAxisItem:
1237 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1249 /* delete events get handled here */
1251 Editing::MouseMode const eff = effective_mouse_mode ();
1253 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1255 switch (item_type) {
1256 case TempoMarkerItem:
1257 remove_tempo_marker (item);
1260 case MeterMarkerItem:
1261 remove_meter_marker (item);
1265 remove_marker (*item, event);
1269 if (eff == MouseObject) {
1270 remove_clicked_region ();
1274 case ControlPointItem:
1275 if (eff == MouseGain) {
1276 remove_gain_control_point (item, event);
1278 remove_control_point (item, event);
1283 remove_midi_note (item, event);
1292 switch (event->button.button) {
1295 switch (item_type) {
1296 /* see comments in button_press_handler */
1297 case PlayheadCursorItem:
1300 case AutomationLineItem:
1301 case StartSelectionTrimItem:
1302 case EndSelectionTrimItem:
1306 if (!_dragging_playhead) {
1307 snap_to_with_modifier (where, event, 0, true);
1308 mouse_add_new_marker (where);
1312 case CdMarkerBarItem:
1313 if (!_dragging_playhead) {
1314 // if we get here then a dragged range wasn't done
1315 snap_to_with_modifier (where, event, 0, true);
1316 mouse_add_new_marker (where, true);
1321 if (!_dragging_playhead) {
1322 snap_to_with_modifier (where, event);
1323 mouse_add_new_tempo_event (where);
1328 if (!_dragging_playhead) {
1329 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1340 switch (item_type) {
1341 case AutomationTrackItem:
1342 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1344 atv->add_automation_event (item, event, where, event->button.y);
1355 switch (item_type) {
1358 /* check that we didn't drag before releasing, since
1359 its really annoying to create new control
1360 points when doing this.
1362 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1363 if (were_dragging && arv) {
1364 arv->add_gain_point_event (item, event);
1370 case AutomationTrackItem:
1371 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1372 add_automation_event (item, event, where, event->button.y);
1381 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1382 if (scrubbing_direction == 0) {
1383 /* no drag, just a click */
1384 switch (item_type) {
1386 play_selected_region ();
1392 /* make sure we stop */
1393 _session->request_transport_speed (0.0);
1410 switch (item_type) {
1412 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1414 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1417 // Button2 click is unused
1430 // x_style_paste (where, 1.0);
1450 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1457 if (last_item_entered != item) {
1458 last_item_entered = item;
1459 last_item_entered_n = 0;
1462 switch (item_type) {
1463 case ControlPointItem:
1464 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1465 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1466 cp->set_visible (true);
1470 at_y = cp->get_y ();
1471 cp->i2w (at_x, at_y);
1475 fraction = 1.0 - (cp->get_y() / cp->line().height());
1477 if (is_drawable() && !_drags->active ()) {
1478 track_canvas->get_window()->set_cursor (*fader_cursor);
1481 last_item_entered_n++;
1482 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1483 if (last_item_entered_n < 10) {
1484 show_verbose_canvas_cursor ();
1490 if (mouse_mode == MouseGain) {
1491 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1493 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1494 if (is_drawable()) {
1495 track_canvas->get_window()->set_cursor (*fader_cursor);
1500 case AutomationLineItem:
1501 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1503 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1505 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1507 if (is_drawable()) {
1508 track_canvas->get_window()->set_cursor (*fader_cursor);
1513 case RegionViewNameHighlight:
1514 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1515 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1519 case LeftFrameHandle:
1520 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1521 track_canvas->get_window()->set_cursor (*left_side_trim_cursor);
1525 case RightFrameHandle:
1526 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1527 track_canvas->get_window()->set_cursor (*right_side_trim_cursor);
1531 case StartSelectionTrimItem:
1532 case EndSelectionTrimItem:
1535 case ImageFrameHandleStartItem:
1536 case ImageFrameHandleEndItem:
1537 case MarkerViewHandleStartItem:
1538 case MarkerViewHandleEndItem:
1541 if (is_drawable()) {
1542 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1546 case PlayheadCursorItem:
1547 if (is_drawable()) {
1548 switch (_edit_point) {
1550 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1553 track_canvas->get_window()->set_cursor (*grabber_cursor);
1559 case RegionViewName:
1561 /* when the name is not an active item, the entire name highlight is for trimming */
1563 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1564 if (mouse_mode == MouseObject && is_drawable()) {
1565 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1571 case AutomationTrackItem:
1572 if (is_drawable()) {
1573 Gdk::Cursor *cursor;
1574 switch (mouse_mode) {
1576 cursor = selector_cursor;
1579 cursor = zoom_cursor;
1582 cursor = cross_hair_cursor;
1586 track_canvas->get_window()->set_cursor (*cursor);
1588 AutomationTimeAxisView* atv;
1589 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1590 clear_entered_track = false;
1591 set_entered_track (atv);
1597 case RangeMarkerBarItem:
1598 case TransportMarkerBarItem:
1599 case CdMarkerBarItem:
1602 if (is_drawable()) {
1603 track_canvas->get_window()->set_cursor (*timebar_cursor);
1608 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1611 entered_marker = marker;
1612 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1614 case MeterMarkerItem:
1615 case TempoMarkerItem:
1616 if (is_drawable()) {
1617 track_canvas->get_window()->set_cursor (*timebar_cursor);
1621 case FadeInHandleItem:
1622 if (mouse_mode == MouseObject && !internal_editing()) {
1623 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1625 rect->property_fill_color_rgba() = 0;
1626 rect->property_outline_pixels() = 1;
1628 track_canvas->get_window()->set_cursor (*fade_in_cursor);
1632 case FadeOutHandleItem:
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_out_cursor);
1642 case FeatureLineItem:
1644 ArdourCanvas::SimpleLine *line = dynamic_cast<ArdourCanvas::SimpleLine *> (item);
1645 line->property_color_rgba() = 0xFF0000FF;
1652 /* second pass to handle entered track status in a comprehensible way.
1655 switch (item_type) {
1657 case AutomationLineItem:
1658 case ControlPointItem:
1659 /* these do not affect the current entered track state */
1660 clear_entered_track = false;
1663 case AutomationTrackItem:
1664 /* handled above already */
1668 set_entered_track (0);
1676 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1686 switch (item_type) {
1687 case ControlPointItem:
1688 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1689 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1690 if (cp->line().npoints() > 1 && !cp->selected()) {
1691 cp->set_visible (false);
1695 if (is_drawable()) {
1696 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1699 hide_verbose_canvas_cursor ();
1702 case RegionViewNameHighlight:
1703 case LeftFrameHandle:
1704 case RightFrameHandle:
1705 case StartSelectionTrimItem:
1706 case EndSelectionTrimItem:
1707 case PlayheadCursorItem:
1710 case ImageFrameHandleStartItem:
1711 case ImageFrameHandleEndItem:
1712 case MarkerViewHandleStartItem:
1713 case MarkerViewHandleEndItem:
1716 if (is_drawable()) {
1717 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1722 case AutomationLineItem:
1723 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1725 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1727 line->property_fill_color_rgba() = al->get_line_color();
1729 if (is_drawable()) {
1730 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1734 case RegionViewName:
1735 /* see enter_handler() for notes */
1736 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1737 if (is_drawable() && mouse_mode == MouseObject) {
1738 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1743 case RangeMarkerBarItem:
1744 case TransportMarkerBarItem:
1745 case CdMarkerBarItem:
1749 if (is_drawable()) {
1750 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1755 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1759 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1760 location_flags_changed (loc, this);
1763 case MeterMarkerItem:
1764 case TempoMarkerItem:
1766 if (is_drawable()) {
1767 track_canvas->get_window()->set_cursor (*timebar_cursor);
1772 case FadeInHandleItem:
1773 case FadeOutHandleItem:
1774 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1776 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1778 rect->property_fill_color_rgba() = rv->get_fill_color();
1779 rect->property_outline_pixels() = 0;
1782 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1785 case AutomationTrackItem:
1786 if (is_drawable()) {
1787 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1788 clear_entered_track = true;
1789 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1792 case FeatureLineItem:
1794 ArdourCanvas::SimpleLine *line = dynamic_cast<ArdourCanvas::SimpleLine *> (item);
1795 line->property_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
1807 Editor::left_automation_track ()
1809 if (clear_entered_track) {
1810 set_entered_track (0);
1811 clear_entered_track = false;
1817 Editor::scrub (nframes64_t frame, double current_x)
1821 if (scrubbing_direction == 0) {
1823 _session->request_locate (frame, false);
1824 _session->request_transport_speed (0.1);
1825 scrubbing_direction = 1;
1829 if (last_scrub_x > current_x) {
1831 /* pointer moved to the left */
1833 if (scrubbing_direction > 0) {
1835 /* we reversed direction to go backwards */
1838 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1842 /* still moving to the left (backwards) */
1844 scrub_reversals = 0;
1845 scrub_reverse_distance = 0;
1847 delta = 0.01 * (last_scrub_x - current_x);
1848 _session->request_transport_speed (_session->transport_speed() - delta);
1852 /* pointer moved to the right */
1854 if (scrubbing_direction < 0) {
1855 /* we reversed direction to go forward */
1858 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1861 /* still moving to the right */
1863 scrub_reversals = 0;
1864 scrub_reverse_distance = 0;
1866 delta = 0.01 * (current_x - last_scrub_x);
1867 _session->request_transport_speed (_session->transport_speed() + delta);
1871 /* if there have been more than 2 opposite motion moves detected, or one that moves
1872 back more than 10 pixels, reverse direction
1875 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1877 if (scrubbing_direction > 0) {
1878 /* was forwards, go backwards */
1879 _session->request_transport_speed (-0.1);
1880 scrubbing_direction = -1;
1882 /* was backwards, go forwards */
1883 _session->request_transport_speed (0.1);
1884 scrubbing_direction = 1;
1887 scrub_reverse_distance = 0;
1888 scrub_reversals = 0;
1892 last_scrub_x = current_x;
1896 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1898 if (event->motion.is_hint) {
1901 /* We call this so that MOTION_NOTIFY events continue to be
1902 delivered to the canvas. We need to do this because we set
1903 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1904 the density of the events, at the expense of a round-trip
1905 to the server. Given that this will mostly occur on cases
1906 where DISPLAY = :0.0, and given the cost of what the motion
1907 event might do, its a good tradeoff.
1910 track_canvas->get_pointer (x, y);
1913 if (current_stepping_trackview) {
1914 /* don't keep the persistent stepped trackview if the mouse moves */
1915 current_stepping_trackview = 0;
1916 step_timeout.disconnect ();
1919 if (_session && _session->actively_recording()) {
1920 /* Sorry. no dragging stuff around while we record */
1924 JoinObjectRangeState const old = _join_object_range_state;
1925 update_join_object_range_location (event->motion.x, event->motion.y);
1926 if (_join_object_range_state != old) {
1927 set_canvas_cursor ();
1930 bool handled = false;
1931 if (_drags->active ()) {
1932 handled = _drags->motion_handler (event, from_autoscroll);
1939 track_canvas_motion (event);
1944 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1946 ControlPoint* control_point;
1948 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1949 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1953 // We shouldn't remove the first or last gain point
1954 if (control_point->line().is_last_point(*control_point) ||
1955 control_point->line().is_first_point(*control_point)) {
1959 control_point->line().remove_point (*control_point);
1963 Editor::remove_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 control_point->line().remove_point (*control_point);
1976 Editor::edit_control_point (ArdourCanvas::Item* item)
1978 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1981 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1985 ControlPointDialog d (p);
1986 d.set_position (Gtk::WIN_POS_MOUSE);
1989 if (d.run () != RESPONSE_ACCEPT) {
1993 p->line().modify_point_y (*p, d.get_y_fraction ());
1998 Editor::visible_order_range (int* low, int* high) const
2000 *low = TimeAxisView::max_order ();
2003 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2005 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2007 if (!rtv->hidden()) {
2009 if (*high < rtv->order()) {
2010 *high = rtv->order ();
2013 if (*low > rtv->order()) {
2014 *low = rtv->order ();
2021 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2023 /* Either add to or set the set the region selection, unless
2024 this is an alignment click (control used)
2027 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2028 TimeAxisView* tv = &rv.get_time_axis_view();
2029 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2031 if (rtv && rtv->is_track()) {
2032 speed = rtv->track()->speed();
2035 nframes64_t where = get_preferred_edit_position();
2039 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2041 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
2043 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2045 align_region (rv.region(), End, (nframes64_t) (where * speed));
2049 align_region (rv.region(), Start, (nframes64_t) (where * speed));
2056 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
2059 Timecode::Time timecode;
2062 nframes64_t frame_rate;
2065 if (_session == 0) {
2071 if (Profile->get_sae() || Profile->get_small_screen()) {
2072 m = ARDOUR_UI::instance()->primary_clock.mode();
2074 m = ARDOUR_UI::instance()->secondary_clock.mode();
2078 case AudioClock::BBT:
2079 _session->bbt_time (frame, bbt);
2080 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2083 case AudioClock::Timecode:
2084 _session->timecode_time (frame, timecode);
2085 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2088 case AudioClock::MinSec:
2089 /* XXX this is copied from show_verbose_duration_cursor() */
2090 frame_rate = _session->frame_rate();
2091 hours = frame / (frame_rate * 3600);
2092 frame = frame % (frame_rate * 3600);
2093 mins = frame / (frame_rate * 60);
2094 frame = frame % (frame_rate * 60);
2095 secs = (float) frame / (float) frame_rate;
2096 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2100 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2104 if (xpos >= 0 && ypos >=0) {
2105 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2107 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_position(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2109 show_verbose_canvas_cursor ();
2113 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
2116 Timecode::Time timecode;
2120 nframes64_t distance, frame_rate;
2122 Meter meter_at_start(_session->tempo_map().meter_at(start));
2124 if (_session == 0) {
2130 if (Profile->get_sae() || Profile->get_small_screen()) {
2131 m = ARDOUR_UI::instance()->primary_clock.mode ();
2133 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2137 case AudioClock::BBT:
2138 _session->bbt_time (start, sbbt);
2139 _session->bbt_time (end, ebbt);
2142 /* XXX this computation won't work well if the
2143 user makes a selection that spans any meter changes.
2146 ebbt.bars -= sbbt.bars;
2147 if (ebbt.beats >= sbbt.beats) {
2148 ebbt.beats -= sbbt.beats;
2151 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2153 if (ebbt.ticks >= sbbt.ticks) {
2154 ebbt.ticks -= sbbt.ticks;
2157 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2160 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2163 case AudioClock::Timecode:
2164 _session->timecode_duration (end - start, timecode);
2165 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2168 case AudioClock::MinSec:
2169 /* XXX this stuff should be elsewhere.. */
2170 distance = end - start;
2171 frame_rate = _session->frame_rate();
2172 hours = distance / (frame_rate * 3600);
2173 distance = distance % (frame_rate * 3600);
2174 mins = distance / (frame_rate * 60);
2175 distance = distance % (frame_rate * 60);
2176 secs = (float) distance / (float) frame_rate;
2177 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2181 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2185 if (xpos >= 0 && ypos >=0) {
2186 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2189 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2192 show_verbose_canvas_cursor ();
2196 Editor::collect_new_region_view (RegionView* rv)
2198 latest_regionviews.push_back (rv);
2202 Editor::collect_and_select_new_region_view (RegionView* rv)
2205 latest_regionviews.push_back (rv);
2209 Editor::cancel_selection ()
2211 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2212 (*i)->hide_selection ();
2215 selection->clear ();
2216 clicked_selection = 0;
2221 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction)
2223 boost::shared_ptr<Region> region (rv.region());
2225 if (region->locked()) {
2229 nframes64_t new_bound;
2232 TimeAxisView* tvp = clicked_axisview;
2233 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2235 if (tv && tv->is_track()) {
2236 speed = tv->track()->speed();
2239 if (left_direction) {
2240 if (swap_direction) {
2241 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2243 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2246 if (swap_direction) {
2247 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2249 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2253 region->trim_start ((nframes64_t) (new_bound * speed), this);
2254 rv.region_changed (PropertyChange (ARDOUR::Properties::start));
2258 Editor::single_start_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2260 boost::shared_ptr<Region> region (rv.region());
2262 if (region->locked()) {
2267 TimeAxisView* tvp = clicked_axisview;
2268 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2270 if (tv && tv->is_track()) {
2271 speed = tv->track()->speed();
2274 nframes64_t pre_trim_first_frame = region->first_frame();
2276 region->trim_front ((nframes64_t) (new_bound * speed), this);
2279 //Get the next region on the left of this region and shrink/expand it.
2280 boost::shared_ptr<Playlist> playlist (region->playlist());
2281 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2283 bool regions_touching = false;
2285 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2286 regions_touching = true;
2289 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2290 if (region_left != 0 &&
2291 (region_left->last_frame() > region->first_frame() || regions_touching))
2293 region_left->trim_end(region->first_frame() - 1, this);
2297 rv.region_changed (ARDOUR::bounds_change);
2301 Editor::single_end_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2303 boost::shared_ptr<Region> region (rv.region());
2305 if (region->locked()) {
2310 TimeAxisView* tvp = clicked_axisview;
2311 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2313 if (tv && tv->is_track()) {
2314 speed = tv->track()->speed();
2317 nframes64_t pre_trim_last_frame = region->last_frame();
2319 region->trim_end ((nframes64_t) (new_bound * speed), this);
2322 //Get the next region on the right of this region and shrink/expand it.
2323 boost::shared_ptr<Playlist> playlist (region->playlist());
2324 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2326 bool regions_touching = false;
2328 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)) {
2329 regions_touching = true;
2332 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2333 if (region_right != 0 &&
2334 (region_right->first_frame() < region->last_frame() || regions_touching))
2336 region_right->trim_front(region->last_frame() + 1, this);
2339 rv.region_changed (ARDOUR::bounds_change);
2342 rv.region_changed (PropertyChange (ARDOUR::Properties::length));
2348 Editor::point_trim (GdkEvent* event, nframes64_t new_bound)
2350 RegionView* rv = clicked_regionview;
2352 /* Choose action dependant on which button was pressed */
2353 switch (event->button.button) {
2355 begin_reversible_command (_("Start point trim"));
2357 if (selection->selected (rv)) {
2358 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2359 i != selection->regions.by_layer().end(); ++i)
2362 cerr << "region view contains null region" << endl;
2365 if (!(*i)->region()->locked()) {
2366 (*i)->region()->clear_history ();
2367 (*i)->region()->trim_front (new_bound, this);
2368 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2373 if (!rv->region()->locked()) {
2374 rv->region()->clear_history ();
2375 rv->region()->trim_front (new_bound, this);
2376 _session->add_command(new StatefulDiffCommand (rv->region()));
2380 commit_reversible_command();
2384 begin_reversible_command (_("End point trim"));
2386 if (selection->selected (rv)) {
2388 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2390 if (!(*i)->region()->locked()) {
2391 (*i)->region()->clear_history();
2392 (*i)->region()->trim_end (new_bound, this);
2393 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2399 if (!rv->region()->locked()) {
2400 rv->region()->clear_history ();
2401 rv->region()->trim_end (new_bound, this);
2402 _session->add_command (new StatefulDiffCommand (rv->region()));
2406 commit_reversible_command();
2415 Editor::thaw_region_after_trim (RegionView& rv)
2417 boost::shared_ptr<Region> region (rv.region());
2419 if (region->locked()) {
2423 region->resume_property_changes ();
2425 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2428 arv->unhide_envelope ();
2433 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2438 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2439 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2443 Location* location = find_location_from_marker (marker, is_start);
2444 location->set_hidden (true, this);
2449 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2451 double x1 = frame_to_pixel (start);
2452 double x2 = frame_to_pixel (end);
2453 double y2 = full_canvas_height - 1.0;
2455 zoom_rect->property_x1() = x1;
2456 zoom_rect->property_y1() = 1.0;
2457 zoom_rect->property_x2() = x2;
2458 zoom_rect->property_y2() = y2;
2463 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2465 using namespace Gtkmm2ext;
2467 ArdourPrompter prompter (false);
2469 prompter.set_prompt (_("Name for region:"));
2470 prompter.set_initial_text (clicked_regionview->region()->name());
2471 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2472 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2473 prompter.show_all ();
2474 switch (prompter.run ()) {
2475 case Gtk::RESPONSE_ACCEPT:
2477 prompter.get_result(str);
2479 clicked_regionview->region()->set_name (str);
2488 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2490 /* no brushing without a useful snap setting */
2492 switch (_snap_mode) {
2494 return; /* can't work because it allows region to be placed anywhere */
2499 switch (_snap_type) {
2507 /* don't brush a copy over the original */
2509 if (pos == rv->region()->position()) {
2513 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2515 if (rtv == 0 || !rtv->is_track()) {
2519 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2520 double speed = rtv->track()->speed();
2522 playlist->clear_history ();
2523 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
2524 playlist->add_region (new_region, (nframes64_t) (pos * speed));
2525 _session->add_command (new StatefulDiffCommand (playlist));
2527 // playlist is frozen, so we have to update manually XXX this is disgusting
2529 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2533 Editor::track_height_step_timeout ()
2535 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2536 current_stepping_trackview = 0;
2543 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2545 assert (region_view);
2547 _region_motion_group->raise_to_top ();
2549 if (Config->get_edit_mode() == Splice) {
2550 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2552 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2553 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2556 /* sync the canvas to what we think is its current state */
2557 update_canvas_now();
2561 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2563 assert (region_view);
2565 _region_motion_group->raise_to_top ();
2567 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2568 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2572 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2574 assert (region_view);
2576 if (Config->get_edit_mode() == Splice) {
2580 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2581 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2583 begin_reversible_command (_("Drag region brush"));
2586 /** Start a grab where a time range is selected, track(s) are selected, and the
2587 * user clicks and drags a region with a modifier in order to create a new region containing
2588 * the section of the clicked region that lies within the time range.
2591 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2593 if (clicked_regionview == 0) {
2597 /* lets try to create new Region for the selection */
2599 vector<boost::shared_ptr<Region> > new_regions;
2600 create_region_from_selection (new_regions);
2602 if (new_regions.empty()) {
2606 /* XXX fix me one day to use all new regions */
2608 boost::shared_ptr<Region> region (new_regions.front());
2610 /* add it to the current stream/playlist.
2612 tricky: the streamview for the track will add a new regionview. we will
2613 catch the signal it sends when it creates the regionview to
2614 set the regionview we want to then drag.
2617 latest_regionviews.clear();
2618 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2620 /* A selection grab currently creates two undo/redo operations, one for
2621 creating the new region and another for moving it.
2624 begin_reversible_command (_("selection grab"));
2626 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2628 playlist->clear_history ();
2629 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2630 _session->add_command(new StatefulDiffCommand (playlist));
2632 commit_reversible_command ();
2636 if (latest_regionviews.empty()) {
2637 /* something went wrong */
2641 /* we need to deselect all other regionviews, and select this one
2642 i'm ignoring undo stuff, because the region creation will take care of it
2644 selection->set (latest_regionviews);
2646 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2652 if (_drags->active ()) {
2655 selection->clear ();
2660 Editor::set_internal_edit (bool yn)
2662 _internal_editing = yn;
2665 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2666 mouse_select_button.get_image ()->show ();
2667 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2668 set_canvas_cursor ();
2670 /* deselect everything to avoid confusion when e.g. we can't now cut a previously selected
2671 region because cut means "cut note" rather than "cut region".
2673 selection->clear ();
2677 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2678 mouse_select_button.get_image ()->show ();
2679 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Select/Move Ranges"));
2680 mouse_mode_toggled (mouse_mode); // sets cursor
2684 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2685 * used by the `join object/range' tool mode.
2688 Editor::update_join_object_range_location (double x, double y)
2690 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2691 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2692 that we're over requires searching the playlist.
2695 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2696 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2700 if (mouse_mode == MouseObject) {
2701 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2702 } else if (mouse_mode == MouseRange) {
2703 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2706 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2707 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2711 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2716 rtv->canvas_display()->w2i (cx, cy);
2718 bool const top_half = cy < rtv->current_height () / 2;
2720 _join_object_range_state = top_half ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2726 Editor::effective_mouse_mode () const
2728 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2730 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2738 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2740 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2743 e->region_view().delete_note (e->note ());