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"
38 #include "canvas-note.h"
40 #include "time_axis_view.h"
41 #include "audio_time_axis.h"
42 #include "audio_region_view.h"
43 #include "midi_region_view.h"
45 #include "streamview.h"
46 #include "region_gain_line.h"
47 #include "automation_time_axis.h"
48 #include "control_point.h"
51 #include "selection.h"
54 #include "rgb_macros.h"
55 #include "control_point_dialog.h"
56 #include "editor_drag.h"
57 #include "automation_region_view.h"
58 #include "edit_note_dialog.h"
60 #include "ardour/types.h"
61 #include "ardour/profile.h"
62 #include "ardour/route.h"
63 #include "ardour/audio_track.h"
64 #include "ardour/audio_diskstream.h"
65 #include "ardour/midi_diskstream.h"
66 #include "ardour/playlist.h"
67 #include "ardour/audioplaylist.h"
68 #include "ardour/audioregion.h"
69 #include "ardour/midi_region.h"
70 #include "ardour/dB.h"
71 #include "ardour/utils.h"
72 #include "ardour/region_factory.h"
73 #include "ardour/source_factory.h"
74 #include "ardour/session.h"
81 using namespace ARDOUR;
84 using namespace Editing;
85 using Gtkmm2ext::Keyboard;
88 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
92 Gdk::ModifierType mask;
93 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
94 Glib::RefPtr<const Gdk::Window> pointer_window;
100 pointer_window = canvas_window->get_pointer (x, y, mask);
102 if (pointer_window == track_canvas->get_bin_window()) {
105 in_track_canvas = true;
108 in_track_canvas = false;
113 event.type = GDK_BUTTON_RELEASE;
117 where = event_frame (&event, 0, 0);
122 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
136 switch (event->type) {
137 case GDK_BUTTON_RELEASE:
138 case GDK_BUTTON_PRESS:
139 case GDK_2BUTTON_PRESS:
140 case GDK_3BUTTON_PRESS:
141 *pcx = event->button.x;
142 *pcy = event->button.y;
143 _trackview_group->w2i(*pcx, *pcy);
145 case GDK_MOTION_NOTIFY:
146 *pcx = event->motion.x;
147 *pcy = event->motion.y;
148 _trackview_group->w2i(*pcx, *pcy);
150 case GDK_ENTER_NOTIFY:
151 case GDK_LEAVE_NOTIFY:
152 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
155 case GDK_KEY_RELEASE:
156 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
159 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
163 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
164 position is negative (as can be the case with motion events in particular),
165 the frame location is always positive.
168 return pixel_to_frame (*pcx);
172 Editor::which_grabber_cursor ()
174 Gdk::Cursor* c = grabber_cursor;
176 if (_internal_editing) {
177 switch (mouse_mode) {
179 c = midi_pencil_cursor;
183 c = grabber_note_cursor;
187 c = midi_resize_cursor;
196 switch (_edit_point) {
198 c = grabber_edit_point_cursor;
201 boost::shared_ptr<Movable> m = _movable.lock();
202 if (m && m->locked()) {
213 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
215 boost::shared_ptr<Trimmable> st = _trimmable.lock();
217 if (!st || st == t) {
219 set_canvas_cursor ();
225 Editor::set_current_movable (boost::shared_ptr<Movable> m)
227 boost::shared_ptr<Movable> sm = _movable.lock();
229 if (!sm || sm != m) {
231 set_canvas_cursor ();
236 Editor::set_canvas_cursor ()
238 if (_internal_editing) {
240 switch (mouse_mode) {
242 current_canvas_cursor = midi_pencil_cursor;
246 current_canvas_cursor = which_grabber_cursor();
250 current_canvas_cursor = midi_resize_cursor;
259 switch (mouse_mode) {
261 current_canvas_cursor = selector_cursor;
265 current_canvas_cursor = which_grabber_cursor();
269 current_canvas_cursor = cross_hair_cursor;
273 current_canvas_cursor = zoom_in_cursor;
277 current_canvas_cursor = time_fx_cursor; // just use playhead
281 current_canvas_cursor = speaker_cursor;
286 switch (_join_object_range_state) {
287 case JOIN_OBJECT_RANGE_NONE:
289 case JOIN_OBJECT_RANGE_OBJECT:
290 current_canvas_cursor = which_grabber_cursor ();
292 case JOIN_OBJECT_RANGE_RANGE:
293 current_canvas_cursor = selector_cursor;
297 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
298 if (join_object_range_button.get_active() && last_item_entered) {
299 if (last_item_entered->property_parent() && (*last_item_entered->property_parent()).get_data (X_("timeselection"))) {
300 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value() - canvas_timebars_vsize);
301 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
302 current_canvas_cursor = up_down_cursor;
307 set_canvas_cursor (current_canvas_cursor, true);
311 Editor::set_mouse_mode (MouseMode m, bool force)
313 if (_drags->active ()) {
317 if (!force && m == mouse_mode) {
321 Glib::RefPtr<Action> act;
325 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
329 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
333 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
337 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
341 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
345 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
351 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
354 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
355 tact->set_active (false);
356 tact->set_active (true);
360 Editor::mouse_mode_toggled (MouseMode m)
366 if (!internal_editing()) {
367 if (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
369 /* in all modes except range and joined object/range, hide the range selection,
370 show the object (region) selection.
373 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
374 (*i)->set_should_show_selection (true);
376 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
377 (*i)->hide_selection ();
383 in range or object/range mode, show the range selection.
386 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
387 (*i)->show_selection (selection->time);
392 set_canvas_cursor ();
396 Editor::step_mouse_mode (bool next)
398 switch (current_mouse_mode()) {
401 if (Profile->get_sae()) {
402 set_mouse_mode (MouseZoom);
404 set_mouse_mode (MouseRange);
407 set_mouse_mode (MouseTimeFX);
412 if (next) set_mouse_mode (MouseZoom);
413 else set_mouse_mode (MouseObject);
418 if (Profile->get_sae()) {
419 set_mouse_mode (MouseTimeFX);
421 set_mouse_mode (MouseGain);
424 if (Profile->get_sae()) {
425 set_mouse_mode (MouseObject);
427 set_mouse_mode (MouseRange);
433 if (next) set_mouse_mode (MouseTimeFX);
434 else set_mouse_mode (MouseZoom);
439 set_mouse_mode (MouseAudition);
441 if (Profile->get_sae()) {
442 set_mouse_mode (MouseZoom);
444 set_mouse_mode (MouseGain);
450 if (next) set_mouse_mode (MouseObject);
451 else set_mouse_mode (MouseTimeFX);
457 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
459 /* in object/audition/timefx/gain-automation mode,
460 any button press sets the selection if the object
461 can be selected. this is a bit of hack, because
462 we want to avoid this if the mouse operation is a
465 note: not dbl-click or triple-click
467 Also note that there is no region selection in internal edit mode, otherwise
468 for operations operating on the selection (e.g. cut) it is not obvious whether
469 to cut notes or regions.
472 if (((mouse_mode != MouseObject) &&
473 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
474 (mouse_mode != MouseAudition || item_type != RegionItem) &&
475 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
476 (mouse_mode != MouseGain) &&
477 (mouse_mode != MouseRange)) ||
478 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
479 internal_editing()) {
484 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
486 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
488 /* almost no selection action on modified button-2 or button-3 events */
490 if (item_type != RegionItem && event->button.button != 2) {
496 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
497 bool press = (event->type == GDK_BUTTON_PRESS);
499 // begin_reversible_command (_("select on click"));
503 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
504 set_selected_regionview_from_click (press, op, true);
505 } else if (event->type == GDK_BUTTON_PRESS) {
506 selection->clear_tracks ();
507 set_selected_track_as_side_effect (op, true);
509 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
510 clicked_selection = select_range_around_region (selection->regions.front());
514 case RegionViewNameHighlight:
516 case LeftFrameHandle:
517 case RightFrameHandle:
518 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
519 set_selected_regionview_from_click (press, op, true);
520 } else if (event->type == GDK_BUTTON_PRESS) {
521 set_selected_track_as_side_effect (op);
526 case FadeInHandleItem:
528 case FadeOutHandleItem:
530 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
531 set_selected_regionview_from_click (press, op, true);
532 } else if (event->type == GDK_BUTTON_PRESS) {
533 set_selected_track_as_side_effect (op);
537 case ControlPointItem:
538 set_selected_track_as_side_effect (op, true);
539 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
540 set_selected_control_point_from_click (op, false);
545 /* for context click, select track */
546 if (event->button.button == 3) {
547 selection->clear_tracks ();
548 set_selected_track_as_side_effect (op, true);
552 case AutomationTrackItem:
553 set_selected_track_as_side_effect (op, true);
562 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
564 /* single mouse clicks on any of these item types operate
565 independent of mouse mode, mostly because they are
566 not on the main track canvas or because we want
571 case PlayheadCursorItem:
572 _drags->set (new CursorDrag (this, item, true), event);
576 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
577 hide_marker (item, event);
579 _drags->set (new MarkerDrag (this, item), event);
583 case TempoMarkerItem:
585 new TempoMarkerDrag (
588 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
594 case MeterMarkerItem:
596 new MeterMarkerDrag (
599 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
608 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
609 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
615 case RangeMarkerBarItem:
616 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
617 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
619 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
624 case CdMarkerBarItem:
625 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
626 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
628 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
633 case TransportMarkerBarItem:
634 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
635 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
637 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
646 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
647 /* special case: allow trim of range selections in joined object mode;
648 in theory eff should equal MouseRange in this case, but it doesn't
649 because entering the range selection canvas item results in entered_regionview
650 being set to 0, so update_join_object_range_location acts as if we aren't
653 if (item_type == StartSelectionTrimItem) {
654 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
655 } else if (item_type == EndSelectionTrimItem) {
656 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
660 Editing::MouseMode eff = effective_mouse_mode ();
662 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
663 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
670 case StartSelectionTrimItem:
671 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
674 case EndSelectionTrimItem:
675 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
679 if (Keyboard::modifier_state_contains
680 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
681 // contains and not equals because I can't use alt as a modifier alone.
682 start_selection_grab (item, event);
683 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
684 /* grab selection for moving */
685 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
687 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
688 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
690 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
691 if (join_object_range_button.get_active() && atv) {
692 /* smart "join" mode: drag automation */
693 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, up_down_cursor);
695 /* this was debated, but decided the more common action was to
696 make a new selection */
697 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
704 if (internal_editing()) {
705 /* trim notes if we're in internal edit mode and near the ends of the note */
706 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
707 cerr << "NoteItem button press, cursor = " << current_canvas_cursor << endl;
708 if (cn->mouse_near_ends()) {
709 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
711 _drags->set (new NoteDrag (this, item), event);
717 if (internal_editing()) {
718 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
719 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
723 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
728 case RegionViewNameHighlight:
729 case LeftFrameHandle:
730 case RightFrameHandle:
731 if (!clicked_regionview->region()->locked()) {
732 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
733 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
739 if (!internal_editing()) {
740 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
749 if (internal_editing()) {
750 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
751 if (cn->mouse_near_ends()) {
752 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
754 _drags->set (new NoteDrag (this, item), event);
764 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
765 event->type == GDK_BUTTON_PRESS) {
767 _drags->set (new RubberbandSelectDrag (this, item), event);
769 } else if (event->type == GDK_BUTTON_PRESS) {
772 case FadeInHandleItem:
774 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
775 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
779 case FadeOutHandleItem:
781 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
782 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
786 case FeatureLineItem:
788 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
789 remove_transient(item);
793 _drags->set (new FeatureLineDrag (this, item), event);
799 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
800 /* click on an automation region view; do nothing here and let the ARV's signal handler
806 if (internal_editing ()) {
807 /* no region drags in internal edit mode */
811 /* click on a normal region view */
812 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
813 add_region_copy_drag (item, event, clicked_regionview);
815 else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
816 add_region_brush_drag (item, event, clicked_regionview);
818 add_region_drag (item, event, clicked_regionview);
821 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
822 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
825 _drags->start_grab (event);
828 case RegionViewNameHighlight:
829 case LeftFrameHandle:
830 case RightFrameHandle:
831 if (!internal_editing () && !clicked_regionview->region()->locked()) {
832 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
833 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
840 /* rename happens on edit clicks */
841 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
842 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
847 case ControlPointItem:
848 _drags->set (new ControlPointDrag (this, item), event);
852 case AutomationLineItem:
853 _drags->set (new LineDrag (this, item), event);
858 if (internal_editing()) {
859 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
860 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
864 _drags->set (new RubberbandSelectDrag (this, item), event);
868 case AutomationTrackItem:
869 /* rubberband drag to select automation points */
870 _drags->set (new RubberbandSelectDrag (this, item), event);
875 if (join_object_range_button.get_active()) {
876 /* we're in "smart" joined mode, and we've clicked on a Selection */
877 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
878 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
880 /* if we're over an automation track, start a drag of its data */
881 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
883 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, up_down_cursor);
886 /* if we're over a track and a region, and in the `object' part of a region,
887 put a selection around the region and drag both
889 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
890 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
891 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
893 boost::shared_ptr<Playlist> pl = t->playlist ();
896 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
898 RegionView* rv = rtv->view()->find_view (r);
899 clicked_selection = select_range_around_region (rv);
900 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
901 list<RegionView*> rvs;
903 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
904 _drags->start_grab (event);
915 case ImageFrameHandleStartItem:
916 imageframe_start_handle_op(item, event) ;
919 case ImageFrameHandleEndItem:
920 imageframe_end_handle_op(item, event) ;
923 case MarkerViewHandleStartItem:
924 markerview_item_start_handle_op(item, event) ;
927 case MarkerViewHandleEndItem:
928 markerview_item_end_handle_op(item, event) ;
932 start_markerview_grab(item, event) ;
935 start_imageframe_grab(item, event) ;
953 /* start a grab so that if we finish after moving
954 we can tell what happened.
956 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
960 _drags->set (new LineDrag (this, item), event);
963 case ControlPointItem:
964 _drags->set (new ControlPointDrag (this, item), event);
975 case ControlPointItem:
976 _drags->set (new ControlPointDrag (this, item), event);
979 case AutomationLineItem:
980 _drags->set (new LineDrag (this, item), event);
984 // XXX need automation mode to identify which
986 // start_line_grab_from_regionview (item, event);
996 if (event->type == GDK_BUTTON_PRESS) {
997 _drags->set (new MouseZoomDrag (this, item), event);
1004 if (internal_editing() && item_type == NoteItem) {
1005 /* drag notes if we're in internal edit mode */
1006 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1008 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
1009 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
1010 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1016 _drags->set (new ScrubDrag (this, item), event);
1017 scrub_reversals = 0;
1018 scrub_reverse_distance = 0;
1019 last_scrub_x = event->button.x;
1020 scrubbing_direction = 0;
1021 set_canvas_cursor (transparent_cursor);
1033 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1035 Editing::MouseMode const eff = effective_mouse_mode ();
1038 switch (item_type) {
1040 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1041 add_region_copy_drag (item, event, clicked_regionview);
1043 add_region_drag (item, event, clicked_regionview);
1045 _drags->start_grab (event);
1048 case ControlPointItem:
1049 _drags->set (new ControlPointDrag (this, item), event);
1057 switch (item_type) {
1058 case RegionViewNameHighlight:
1059 case LeftFrameHandle:
1060 case RightFrameHandle:
1061 if (!internal_editing ()) {
1062 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1067 case RegionViewName:
1068 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1079 /* relax till release */
1085 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1086 temporal_zoom_session();
1088 temporal_zoom_to_frame (true, event_frame(event));
1101 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1103 if (event->type != GDK_BUTTON_PRESS) {
1107 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1109 if (canvas_window) {
1110 Glib::RefPtr<const Gdk::Window> pointer_window;
1113 Gdk::ModifierType mask;
1115 pointer_window = canvas_window->get_pointer (x, y, mask);
1117 if (pointer_window == track_canvas->get_bin_window()) {
1118 track_canvas->window_to_world (x, y, wx, wy);
1122 pre_press_cursor = current_canvas_cursor;
1124 track_canvas->grab_focus();
1126 if (_session && _session->actively_recording()) {
1130 button_selection (item, event, item_type);
1132 if (!_drags->active () &&
1133 (Keyboard::is_delete_event (&event->button) ||
1134 Keyboard::is_context_menu_event (&event->button) ||
1135 Keyboard::is_edit_event (&event->button))) {
1137 /* handled by button release */
1141 switch (event->button.button) {
1143 return button_press_handler_1 (item, event, item_type);
1147 return button_press_handler_2 (item, event, item_type);
1162 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1164 framepos_t where = event_frame (event, 0, 0);
1165 AutomationTimeAxisView* atv = 0;
1167 if (pre_press_cursor) {
1168 set_canvas_cursor (pre_press_cursor);
1169 pre_press_cursor = 0;
1172 /* no action if we're recording */
1174 if (_session && _session->actively_recording()) {
1178 /* see if we're finishing a drag */
1180 bool were_dragging = false;
1181 if (_drags->active ()) {
1182 bool const r = _drags->end_grab (event);
1184 /* grab dragged, so do nothing else */
1188 were_dragging = true;
1191 update_region_layering_order_editor ();
1193 /* edit events get handled here */
1195 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1196 switch (item_type) {
1198 show_region_properties ();
1201 case TempoMarkerItem:
1202 edit_tempo_marker (item);
1205 case MeterMarkerItem:
1206 edit_meter_marker (item);
1209 case RegionViewName:
1210 if (clicked_regionview->name_active()) {
1211 return mouse_rename_region (item, event);
1215 case ControlPointItem:
1216 edit_control_point (item);
1229 /* context menu events get handled here */
1231 if (Keyboard::is_context_menu_event (&event->button)) {
1233 if (!_drags->active ()) {
1235 /* no matter which button pops up the context menu, tell the menu
1236 widget to use button 1 to drive menu selection.
1239 switch (item_type) {
1241 case FadeInHandleItem:
1243 case FadeOutHandleItem:
1244 popup_fade_context_menu (1, event->button.time, item, item_type);
1248 popup_track_context_menu (1, event->button.time, item_type, false, where);
1252 case RegionViewNameHighlight:
1253 case LeftFrameHandle:
1254 case RightFrameHandle:
1255 case RegionViewName:
1256 popup_track_context_menu (1, event->button.time, item_type, false, where);
1260 popup_track_context_menu (1, event->button.time, item_type, true, where);
1263 case AutomationTrackItem:
1264 popup_track_context_menu (1, event->button.time, item_type, false, where);
1268 case RangeMarkerBarItem:
1269 case TransportMarkerBarItem:
1270 case CdMarkerBarItem:
1273 popup_ruler_menu (where, item_type);
1277 marker_context_menu (&event->button, item);
1280 case TempoMarkerItem:
1281 tempo_or_meter_marker_context_menu (&event->button, item);
1284 case MeterMarkerItem:
1285 tempo_or_meter_marker_context_menu (&event->button, item);
1288 case CrossfadeViewItem:
1289 popup_track_context_menu (1, event->button.time, item_type, false, where);
1293 case ImageFrameItem:
1294 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1296 case ImageFrameTimeAxisItem:
1297 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1299 case MarkerViewItem:
1300 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1302 case MarkerTimeAxisItem:
1303 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1315 /* delete events get handled here */
1317 Editing::MouseMode const eff = effective_mouse_mode ();
1319 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1321 switch (item_type) {
1322 case TempoMarkerItem:
1323 remove_tempo_marker (item);
1326 case MeterMarkerItem:
1327 remove_meter_marker (item);
1331 remove_marker (*item, event);
1335 if (eff == MouseObject) {
1336 remove_clicked_region ();
1340 case ControlPointItem:
1341 if (eff == MouseGain) {
1342 remove_gain_control_point (item, event);
1344 remove_control_point (item, event);
1349 remove_midi_note (item, event);
1358 switch (event->button.button) {
1361 switch (item_type) {
1362 /* see comments in button_press_handler */
1363 case PlayheadCursorItem:
1366 case AutomationLineItem:
1367 case StartSelectionTrimItem:
1368 case EndSelectionTrimItem:
1372 if (!_dragging_playhead) {
1373 snap_to_with_modifier (where, event, 0, true);
1374 mouse_add_new_marker (where);
1378 case CdMarkerBarItem:
1379 if (!_dragging_playhead) {
1380 // if we get here then a dragged range wasn't done
1381 snap_to_with_modifier (where, event, 0, true);
1382 mouse_add_new_marker (where, true);
1387 if (!_dragging_playhead) {
1388 snap_to_with_modifier (where, event);
1389 mouse_add_new_tempo_event (where);
1394 if (!_dragging_playhead) {
1395 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1406 switch (item_type) {
1407 case AutomationTrackItem:
1408 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1410 atv->add_automation_event (item, event, where, event->button.y);
1421 switch (item_type) {
1424 /* check that we didn't drag before releasing, since
1425 its really annoying to create new control
1426 points when doing this.
1428 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1429 if (were_dragging && arv) {
1430 arv->add_gain_point_event (item, event);
1436 case AutomationTrackItem:
1437 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1438 add_automation_event (item, event, where, event->button.y);
1447 set_canvas_cursor (current_canvas_cursor);
1448 if (scrubbing_direction == 0) {
1449 /* no drag, just a click */
1450 switch (item_type) {
1452 play_selected_region ();
1458 /* make sure we stop */
1459 _session->request_transport_speed (0.0);
1476 switch (item_type) {
1478 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1480 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1483 // Button2 click is unused
1496 // x_style_paste (where, 1.0);
1516 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1523 last_item_entered = item;
1525 switch (item_type) {
1526 case ControlPointItem:
1527 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1528 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1529 cp->set_visible (true);
1533 at_y = cp->get_y ();
1534 cp->i2w (at_x, at_y);
1538 fraction = 1.0 - (cp->get_y() / cp->line().height());
1540 if (is_drawable() && !_drags->active ()) {
1541 set_canvas_cursor (fader_cursor);
1544 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1545 show_verbose_canvas_cursor ();
1550 if (mouse_mode == MouseGain) {
1551 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1553 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1554 if (is_drawable()) {
1555 set_canvas_cursor (fader_cursor);
1560 case AutomationLineItem:
1561 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1563 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1565 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1567 if (is_drawable()) {
1568 set_canvas_cursor (fader_cursor);
1573 case RegionViewNameHighlight:
1574 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1575 set_canvas_cursor (trimmer_cursor);
1579 case LeftFrameHandle:
1580 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1581 if (entered_regionview) {
1582 Trimmable::CanTrim ct = entered_regionview->region()->can_trim();
1583 if ((ct & Trimmable::EndTrimEarlier) || (ct & Trimmable::EndTrimLater)) {
1584 set_canvas_cursor (left_side_trim_cursor);
1590 case RightFrameHandle:
1591 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1592 if (entered_regionview) {
1593 Trimmable::CanTrim ct = entered_regionview->region()->can_trim();
1594 if ((ct & Trimmable::FrontTrimEarlier) || (ct & Trimmable::FrontTrimLater)) {
1595 set_canvas_cursor (right_side_trim_cursor);
1601 case StartSelectionTrimItem:
1602 case EndSelectionTrimItem:
1605 case ImageFrameHandleStartItem:
1606 case ImageFrameHandleEndItem:
1607 case MarkerViewHandleStartItem:
1608 case MarkerViewHandleEndItem:
1611 if (is_drawable()) {
1612 set_canvas_cursor (trimmer_cursor);
1616 case PlayheadCursorItem:
1617 if (is_drawable()) {
1618 switch (_edit_point) {
1620 set_canvas_cursor (grabber_edit_point_cursor);
1623 set_canvas_cursor (grabber_cursor);
1629 case RegionViewName:
1631 /* when the name is not an active item, the entire name highlight is for trimming */
1633 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1634 if (mouse_mode == MouseObject && is_drawable()) {
1635 set_canvas_cursor (trimmer_cursor);
1641 case AutomationTrackItem:
1642 if (is_drawable()) {
1643 Gdk::Cursor *cursor;
1644 switch (mouse_mode) {
1646 cursor = selector_cursor;
1649 cursor = zoom_in_cursor;
1652 cursor = cross_hair_cursor;
1656 set_canvas_cursor (cursor);
1658 AutomationTimeAxisView* atv;
1659 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1660 clear_entered_track = false;
1661 set_entered_track (atv);
1667 case RangeMarkerBarItem:
1668 case TransportMarkerBarItem:
1669 case CdMarkerBarItem:
1672 if (is_drawable()) {
1673 set_canvas_cursor (timebar_cursor);
1678 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1681 entered_marker = marker;
1682 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1684 case MeterMarkerItem:
1685 case TempoMarkerItem:
1686 if (is_drawable()) {
1687 set_canvas_cursor (timebar_cursor);
1691 case FadeInHandleItem:
1692 if (mouse_mode == MouseObject && !internal_editing()) {
1693 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1695 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1697 set_canvas_cursor (fade_in_cursor);
1701 case FadeOutHandleItem:
1702 if (mouse_mode == MouseObject && !internal_editing()) {
1703 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1705 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1707 set_canvas_cursor (fade_out_cursor);
1710 case FeatureLineItem:
1712 ArdourCanvas::SimpleLine *line = dynamic_cast<ArdourCanvas::SimpleLine *> (item);
1713 line->property_color_rgba() = 0xFF0000FF;
1717 if (join_object_range_button.get_active()) {
1718 set_canvas_cursor ();
1726 /* second pass to handle entered track status in a comprehensible way.
1729 switch (item_type) {
1731 case AutomationLineItem:
1732 case ControlPointItem:
1733 /* these do not affect the current entered track state */
1734 clear_entered_track = false;
1737 case AutomationTrackItem:
1738 /* handled above already */
1742 set_entered_track (0);
1750 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1760 switch (item_type) {
1761 case ControlPointItem:
1762 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1763 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1764 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1765 cp->set_visible (false);
1769 if (is_drawable()) {
1770 set_canvas_cursor (current_canvas_cursor);
1773 hide_verbose_canvas_cursor ();
1776 case RegionViewNameHighlight:
1777 case LeftFrameHandle:
1778 case RightFrameHandle:
1779 case StartSelectionTrimItem:
1780 case EndSelectionTrimItem:
1781 case PlayheadCursorItem:
1784 case ImageFrameHandleStartItem:
1785 case ImageFrameHandleEndItem:
1786 case MarkerViewHandleStartItem:
1787 case MarkerViewHandleEndItem:
1790 if (is_drawable()) {
1791 set_canvas_cursor (current_canvas_cursor);
1796 case AutomationLineItem:
1797 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1799 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1801 line->property_fill_color_rgba() = al->get_line_color();
1803 if (is_drawable()) {
1804 set_canvas_cursor (current_canvas_cursor);
1808 case RegionViewName:
1809 /* see enter_handler() for notes */
1810 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1811 if (is_drawable() && mouse_mode == MouseObject) {
1812 set_canvas_cursor (current_canvas_cursor);
1817 case RangeMarkerBarItem:
1818 case TransportMarkerBarItem:
1819 case CdMarkerBarItem:
1823 if (is_drawable()) {
1824 set_canvas_cursor (current_canvas_cursor);
1829 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1833 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1834 location_flags_changed (loc, this);
1837 case MeterMarkerItem:
1838 case TempoMarkerItem:
1840 if (is_drawable()) {
1841 set_canvas_cursor (timebar_cursor);
1846 case FadeInHandleItem:
1847 case FadeOutHandleItem:
1848 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1850 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1852 rect->property_fill_color_rgba() = rv->get_fill_color();
1853 rect->property_outline_pixels() = 0;
1856 set_canvas_cursor (current_canvas_cursor);
1859 case AutomationTrackItem:
1860 if (is_drawable()) {
1861 set_canvas_cursor (current_canvas_cursor);
1862 clear_entered_track = true;
1863 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1866 case FeatureLineItem:
1868 ArdourCanvas::SimpleLine *line = dynamic_cast<ArdourCanvas::SimpleLine *> (item);
1869 line->property_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
1881 Editor::left_automation_track ()
1883 if (clear_entered_track) {
1884 set_entered_track (0);
1885 clear_entered_track = false;
1891 Editor::scrub (framepos_t frame, double current_x)
1895 if (scrubbing_direction == 0) {
1897 _session->request_locate (frame, false);
1898 _session->request_transport_speed (0.1);
1899 scrubbing_direction = 1;
1903 if (last_scrub_x > current_x) {
1905 /* pointer moved to the left */
1907 if (scrubbing_direction > 0) {
1909 /* we reversed direction to go backwards */
1912 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1916 /* still moving to the left (backwards) */
1918 scrub_reversals = 0;
1919 scrub_reverse_distance = 0;
1921 delta = 0.01 * (last_scrub_x - current_x);
1922 _session->request_transport_speed (_session->transport_speed() - delta);
1926 /* pointer moved to the right */
1928 if (scrubbing_direction < 0) {
1929 /* we reversed direction to go forward */
1932 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1935 /* still moving to the right */
1937 scrub_reversals = 0;
1938 scrub_reverse_distance = 0;
1940 delta = 0.01 * (current_x - last_scrub_x);
1941 _session->request_transport_speed (_session->transport_speed() + delta);
1945 /* if there have been more than 2 opposite motion moves detected, or one that moves
1946 back more than 10 pixels, reverse direction
1949 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1951 if (scrubbing_direction > 0) {
1952 /* was forwards, go backwards */
1953 _session->request_transport_speed (-0.1);
1954 scrubbing_direction = -1;
1956 /* was backwards, go forwards */
1957 _session->request_transport_speed (0.1);
1958 scrubbing_direction = 1;
1961 scrub_reverse_distance = 0;
1962 scrub_reversals = 0;
1966 last_scrub_x = current_x;
1970 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1972 _last_motion_y = event->motion.y;
1974 if (event->motion.is_hint) {
1977 /* We call this so that MOTION_NOTIFY events continue to be
1978 delivered to the canvas. We need to do this because we set
1979 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1980 the density of the events, at the expense of a round-trip
1981 to the server. Given that this will mostly occur on cases
1982 where DISPLAY = :0.0, and given the cost of what the motion
1983 event might do, its a good tradeoff.
1986 track_canvas->get_pointer (x, y);
1989 if (current_stepping_trackview) {
1990 /* don't keep the persistent stepped trackview if the mouse moves */
1991 current_stepping_trackview = 0;
1992 step_timeout.disconnect ();
1995 if (_session && _session->actively_recording()) {
1996 /* Sorry. no dragging stuff around while we record */
2000 JoinObjectRangeState const old = _join_object_range_state;
2001 update_join_object_range_location (event->motion.x, event->motion.y);
2002 if (_join_object_range_state != old) {
2003 set_canvas_cursor ();
2006 bool handled = false;
2007 if (_drags->active ()) {
2008 handled = _drags->motion_handler (event, from_autoscroll);
2015 track_canvas_motion (event);
2020 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
2022 ControlPoint* control_point;
2024 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2025 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2029 // We shouldn't remove the first or last gain point
2030 if (control_point->line().is_last_point(*control_point) ||
2031 control_point->line().is_first_point(*control_point)) {
2035 control_point->line().remove_point (*control_point);
2039 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2041 ControlPoint* control_point;
2043 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2044 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2048 control_point->line().remove_point (*control_point);
2052 Editor::edit_control_point (ArdourCanvas::Item* item)
2054 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2057 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2061 ControlPointDialog d (p);
2062 d.set_position (Gtk::WIN_POS_MOUSE);
2065 if (d.run () != RESPONSE_ACCEPT) {
2069 p->line().modify_point_y (*p, d.get_y_fraction ());
2073 Editor::edit_note (ArdourCanvas::Item* item)
2075 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2078 EditNoteDialog d (&e->region_view(), e);
2079 d.set_position (Gtk::WIN_POS_MOUSE);
2087 Editor::visible_order_range (int* low, int* high) const
2089 *low = TimeAxisView::max_order ();
2092 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2094 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2096 if (!rtv->hidden()) {
2098 if (*high < rtv->order()) {
2099 *high = rtv->order ();
2102 if (*low > rtv->order()) {
2103 *low = rtv->order ();
2110 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2112 /* Either add to or set the set the region selection, unless
2113 this is an alignment click (control used)
2116 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2117 TimeAxisView* tv = &rv.get_time_axis_view();
2118 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2120 if (rtv && rtv->is_track()) {
2121 speed = rtv->track()->speed();
2124 framepos_t where = get_preferred_edit_position();
2128 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2130 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2132 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2134 align_region (rv.region(), End, (framepos_t) (where * speed));
2138 align_region (rv.region(), Start, (framepos_t) (where * speed));
2145 Editor::show_verbose_time_cursor (framepos_t frame, double offset, double xpos, double ypos)
2148 Timecode::Time timecode;
2151 framepos_t frame_rate;
2154 if (_session == 0) {
2160 if (Profile->get_sae() || Profile->get_small_screen()) {
2161 m = ARDOUR_UI::instance()->primary_clock.mode();
2163 m = ARDOUR_UI::instance()->secondary_clock.mode();
2167 case AudioClock::BBT:
2168 _session->bbt_time (frame, bbt);
2169 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2172 case AudioClock::Timecode:
2173 _session->timecode_time (frame, timecode);
2174 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2177 case AudioClock::MinSec:
2178 /* XXX this is copied from show_verbose_duration_cursor() */
2179 frame_rate = _session->frame_rate();
2180 hours = frame / (frame_rate * 3600);
2181 frame = frame % (frame_rate * 3600);
2182 mins = frame / (frame_rate * 60);
2183 frame = frame % (frame_rate * 60);
2184 secs = (float) frame / (float) frame_rate;
2185 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2189 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2193 if (xpos >= 0 && ypos >=0) {
2194 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2196 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_position(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2198 show_verbose_canvas_cursor ();
2202 Editor::show_verbose_duration_cursor (framepos_t start, framepos_t end, double offset, double xpos, double ypos)
2205 Timecode::Time timecode;
2209 framepos_t distance, frame_rate;
2211 Meter meter_at_start(_session->tempo_map().meter_at(start));
2213 if (_session == 0) {
2219 if (Profile->get_sae() || Profile->get_small_screen()) {
2220 m = ARDOUR_UI::instance()->primary_clock.mode ();
2222 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2226 case AudioClock::BBT:
2227 _session->bbt_time (start, sbbt);
2228 _session->bbt_time (end, ebbt);
2231 /* XXX this computation won't work well if the
2232 user makes a selection that spans any meter changes.
2235 ebbt.bars -= sbbt.bars;
2236 if (ebbt.beats >= sbbt.beats) {
2237 ebbt.beats -= sbbt.beats;
2240 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2242 if (ebbt.ticks >= sbbt.ticks) {
2243 ebbt.ticks -= sbbt.ticks;
2246 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2249 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2252 case AudioClock::Timecode:
2253 _session->timecode_duration (end - start, timecode);
2254 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2257 case AudioClock::MinSec:
2258 /* XXX this stuff should be elsewhere.. */
2259 distance = end - start;
2260 frame_rate = _session->frame_rate();
2261 hours = distance / (frame_rate * 3600);
2262 distance = distance % (frame_rate * 3600);
2263 mins = distance / (frame_rate * 60);
2264 distance = distance % (frame_rate * 60);
2265 secs = (float) distance / (float) frame_rate;
2266 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2270 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2274 if (xpos >= 0 && ypos >=0) {
2275 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2278 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2281 show_verbose_canvas_cursor ();
2285 Editor::collect_new_region_view (RegionView* rv)
2287 latest_regionviews.push_back (rv);
2291 Editor::collect_and_select_new_region_view (RegionView* rv)
2294 latest_regionviews.push_back (rv);
2298 Editor::cancel_selection ()
2300 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2301 (*i)->hide_selection ();
2304 selection->clear ();
2305 clicked_selection = 0;
2310 Editor::single_contents_trim (RegionView& rv, framepos_t frame_delta, bool left_direction, bool swap_direction)
2312 boost::shared_ptr<Region> region (rv.region());
2314 if (region->locked()) {
2318 framepos_t new_bound;
2321 TimeAxisView* tvp = clicked_axisview;
2322 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2324 if (tv && tv->is_track()) {
2325 speed = tv->track()->speed();
2328 if (left_direction) {
2329 if (swap_direction) {
2330 new_bound = (framepos_t) (region->position()/speed) + frame_delta;
2332 new_bound = (framepos_t) (region->position()/speed) - frame_delta;
2335 if (swap_direction) {
2336 new_bound = (framepos_t) (region->position()/speed) - frame_delta;
2338 new_bound = (framepos_t) (region->position()/speed) + frame_delta;
2342 region->trim_start ((framepos_t) (new_bound * speed), this);
2343 rv.region_changed (PropertyChange (ARDOUR::Properties::start));
2347 Editor::single_start_trim (RegionView& rv, framepos_t new_bound, bool no_overlap)
2349 boost::shared_ptr<Region> region (rv.region());
2351 if (region->locked()) {
2356 TimeAxisView* tvp = clicked_axisview;
2357 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2359 if (tv && tv->is_track()) {
2360 speed = tv->track()->speed();
2363 framepos_t pre_trim_first_frame = region->first_frame();
2365 region->trim_front ((framepos_t) (new_bound * speed), this);
2368 //Get the next region on the left of this region and shrink/expand it.
2369 boost::shared_ptr<Playlist> playlist (region->playlist());
2370 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2372 bool regions_touching = false;
2374 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2375 regions_touching = true;
2378 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2379 if (region_left != 0 &&
2380 (region_left->last_frame() > region->first_frame() || regions_touching))
2382 region_left->trim_end(region->first_frame() - 1, this);
2386 rv.region_changed (ARDOUR::bounds_change);
2390 Editor::single_end_trim (RegionView& rv, framepos_t new_bound, bool no_overlap)
2392 boost::shared_ptr<Region> region (rv.region());
2394 if (region->locked()) {
2399 TimeAxisView* tvp = clicked_axisview;
2400 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2402 if (tv && tv->is_track()) {
2403 speed = tv->track()->speed();
2406 framepos_t pre_trim_last_frame = region->last_frame();
2408 region->trim_end ((framepos_t) (new_bound * speed), this);
2411 //Get the next region on the right of this region and shrink/expand it.
2412 boost::shared_ptr<Playlist> playlist (region->playlist());
2413 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2415 bool regions_touching = false;
2417 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)) {
2418 regions_touching = true;
2421 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2422 if (region_right != 0 &&
2423 (region_right->first_frame() < region->last_frame() || regions_touching))
2425 region_right->trim_front(region->last_frame() + 1, this);
2428 rv.region_changed (ARDOUR::bounds_change);
2431 rv.region_changed (PropertyChange (ARDOUR::Properties::length));
2437 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2439 RegionView* rv = clicked_regionview;
2441 /* Choose action dependant on which button was pressed */
2442 switch (event->button.button) {
2444 begin_reversible_command (_("start point trim"));
2446 if (selection->selected (rv)) {
2447 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2448 i != selection->regions.by_layer().end(); ++i)
2451 cerr << "region view contains null region" << endl;
2454 if (!(*i)->region()->locked()) {
2455 (*i)->region()->clear_changes ();
2456 (*i)->region()->trim_front (new_bound, this);
2457 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2462 if (!rv->region()->locked()) {
2463 rv->region()->clear_changes ();
2464 rv->region()->trim_front (new_bound, this);
2465 _session->add_command(new StatefulDiffCommand (rv->region()));
2469 commit_reversible_command();
2473 begin_reversible_command (_("End point trim"));
2475 if (selection->selected (rv)) {
2477 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2479 if (!(*i)->region()->locked()) {
2480 (*i)->region()->clear_changes();
2481 (*i)->region()->trim_end (new_bound, this);
2482 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2488 if (!rv->region()->locked()) {
2489 rv->region()->clear_changes ();
2490 rv->region()->trim_end (new_bound, this);
2491 _session->add_command (new StatefulDiffCommand (rv->region()));
2495 commit_reversible_command();
2504 Editor::thaw_region_after_trim (RegionView& rv)
2506 boost::shared_ptr<Region> region (rv.region());
2508 if (region->locked()) {
2512 region->resume_property_changes ();
2514 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2517 arv->unhide_envelope ();
2522 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2527 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2528 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2532 Location* location = find_location_from_marker (marker, is_start);
2533 location->set_hidden (true, this);
2538 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2540 double x1 = frame_to_pixel (start);
2541 double x2 = frame_to_pixel (end);
2542 double y2 = full_canvas_height - 1.0;
2544 zoom_rect->property_x1() = x1;
2545 zoom_rect->property_y1() = 1.0;
2546 zoom_rect->property_x2() = x2;
2547 zoom_rect->property_y2() = y2;
2552 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2554 using namespace Gtkmm2ext;
2556 ArdourPrompter prompter (false);
2558 prompter.set_prompt (_("Name for region:"));
2559 prompter.set_initial_text (clicked_regionview->region()->name());
2560 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2561 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2562 prompter.show_all ();
2563 switch (prompter.run ()) {
2564 case Gtk::RESPONSE_ACCEPT:
2566 prompter.get_result(str);
2568 clicked_regionview->region()->set_name (str);
2577 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2579 /* no brushing without a useful snap setting */
2581 switch (_snap_mode) {
2583 return; /* can't work because it allows region to be placed anywhere */
2588 switch (_snap_type) {
2596 /* don't brush a copy over the original */
2598 if (pos == rv->region()->position()) {
2602 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2604 if (rtv == 0 || !rtv->is_track()) {
2608 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2609 double speed = rtv->track()->speed();
2611 playlist->clear_changes ();
2612 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
2613 playlist->add_region (new_region, (framepos_t) (pos * speed));
2614 _session->add_command (new StatefulDiffCommand (playlist));
2616 // playlist is frozen, so we have to update manually XXX this is disgusting
2618 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2622 Editor::track_height_step_timeout ()
2624 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2625 current_stepping_trackview = 0;
2632 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2634 assert (region_view);
2636 if (!region_view->region()->playlist()) {
2640 _region_motion_group->raise_to_top ();
2642 if (Config->get_edit_mode() == Splice) {
2643 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2645 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2646 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2649 /* sync the canvas to what we think is its current state */
2650 update_canvas_now();
2654 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2656 assert (region_view);
2658 if (!region_view->region()->playlist()) {
2662 _region_motion_group->raise_to_top ();
2664 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2665 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2669 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2671 assert (region_view);
2673 if (!region_view->region()->playlist()) {
2677 if (Config->get_edit_mode() == Splice) {
2681 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2682 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2684 begin_reversible_command (_("Drag region brush"));
2687 /** Start a grab where a time range is selected, track(s) are selected, and the
2688 * user clicks and drags a region with a modifier in order to create a new region containing
2689 * the section of the clicked region that lies within the time range.
2692 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2694 if (clicked_regionview == 0) {
2698 /* lets try to create new Region for the selection */
2700 vector<boost::shared_ptr<Region> > new_regions;
2701 create_region_from_selection (new_regions);
2703 if (new_regions.empty()) {
2707 /* XXX fix me one day to use all new regions */
2709 boost::shared_ptr<Region> region (new_regions.front());
2711 /* add it to the current stream/playlist.
2713 tricky: the streamview for the track will add a new regionview. we will
2714 catch the signal it sends when it creates the regionview to
2715 set the regionview we want to then drag.
2718 latest_regionviews.clear();
2719 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2721 /* A selection grab currently creates two undo/redo operations, one for
2722 creating the new region and another for moving it.
2725 begin_reversible_command (_("selection grab"));
2727 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2729 playlist->clear_changes ();
2730 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2731 _session->add_command(new StatefulDiffCommand (playlist));
2733 commit_reversible_command ();
2737 if (latest_regionviews.empty()) {
2738 /* something went wrong */
2742 /* we need to deselect all other regionviews, and select this one
2743 i'm ignoring undo stuff, because the region creation will take care of it
2745 selection->set (latest_regionviews);
2747 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2753 if (_drags->active ()) {
2756 selection->clear ();
2761 Editor::set_internal_edit (bool yn)
2763 _internal_editing = yn;
2766 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2767 mouse_select_button.get_image ()->show ();
2768 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2769 set_canvas_cursor ();
2771 /* deselect everything to avoid confusion when e.g. we can't now cut a previously selected
2772 region because cut means "cut note" rather than "cut region".
2774 selection->clear ();
2778 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2779 mouse_select_button.get_image ()->show ();
2780 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Select/Move Ranges"));
2781 mouse_mode_toggled (mouse_mode); // sets cursor
2785 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2786 * used by the `join object/range' tool mode.
2789 Editor::update_join_object_range_location (double x, double y)
2791 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2792 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2793 that we're over requires searching the playlist.
2796 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2797 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2801 if (mouse_mode == MouseObject) {
2802 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2803 } else if (mouse_mode == MouseRange) {
2804 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2807 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2808 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2812 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2817 rtv->canvas_display()->w2i (cx, cy);
2819 double const c = cy / rtv->view()->child_height();
2821 double const f = modf (c, &d);
2823 _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2829 Editor::effective_mouse_mode () const
2831 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2833 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2841 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2843 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2846 e->region_view().delete_note (e->note ());