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"
59 #include "mouse_cursors.h"
60 #include "editor_cursors.h"
62 #include "ardour/types.h"
63 #include "ardour/profile.h"
64 #include "ardour/route.h"
65 #include "ardour/audio_track.h"
66 #include "ardour/audio_diskstream.h"
67 #include "ardour/midi_diskstream.h"
68 #include "ardour/playlist.h"
69 #include "ardour/audioplaylist.h"
70 #include "ardour/audioregion.h"
71 #include "ardour/midi_region.h"
72 #include "ardour/dB.h"
73 #include "ardour/utils.h"
74 #include "ardour/region_factory.h"
75 #include "ardour/source_factory.h"
76 #include "ardour/session.h"
77 #include "ardour/operations.h"
84 using namespace ARDOUR;
87 using namespace Editing;
88 using Gtkmm2ext::Keyboard;
91 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
95 Gdk::ModifierType mask;
96 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
97 Glib::RefPtr<const Gdk::Window> pointer_window;
103 pointer_window = canvas_window->get_pointer (x, y, mask);
105 if (pointer_window == track_canvas->get_bin_window()) {
108 in_track_canvas = true;
111 in_track_canvas = false;
116 event.type = GDK_BUTTON_RELEASE;
120 where = event_frame (&event, 0, 0);
125 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
139 switch (event->type) {
140 case GDK_BUTTON_RELEASE:
141 case GDK_BUTTON_PRESS:
142 case GDK_2BUTTON_PRESS:
143 case GDK_3BUTTON_PRESS:
144 *pcx = event->button.x;
145 *pcy = event->button.y;
146 _trackview_group->w2i(*pcx, *pcy);
148 case GDK_MOTION_NOTIFY:
149 *pcx = event->motion.x;
150 *pcy = event->motion.y;
151 _trackview_group->w2i(*pcx, *pcy);
153 case GDK_ENTER_NOTIFY:
154 case GDK_LEAVE_NOTIFY:
155 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
158 case GDK_KEY_RELEASE:
159 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
162 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
166 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
167 position is negative (as can be the case with motion events in particular),
168 the frame location is always positive.
171 return pixel_to_frame (*pcx);
175 Editor::which_grabber_cursor ()
177 Gdk::Cursor* c = _cursors->grabber;
179 if (_internal_editing) {
180 switch (mouse_mode) {
182 c = _cursors->midi_pencil;
186 c = _cursors->grabber_note;
190 c = _cursors->midi_resize;
199 switch (_edit_point) {
201 c = _cursors->grabber_edit_point;
204 boost::shared_ptr<Movable> m = _movable.lock();
205 if (m && m->locked()) {
206 c = _cursors->speaker;
216 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
218 boost::shared_ptr<Trimmable> st = _trimmable.lock();
220 if (!st || st == t) {
222 set_canvas_cursor ();
227 Editor::set_current_movable (boost::shared_ptr<Movable> m)
229 boost::shared_ptr<Movable> sm = _movable.lock();
231 if (!sm || sm != m) {
233 set_canvas_cursor ();
238 Editor::set_canvas_cursor ()
240 if (_internal_editing) {
242 switch (mouse_mode) {
244 current_canvas_cursor = _cursors->midi_pencil;
248 current_canvas_cursor = which_grabber_cursor();
252 current_canvas_cursor = _cursors->midi_resize;
261 switch (mouse_mode) {
263 current_canvas_cursor = _cursors->selector;
267 current_canvas_cursor = which_grabber_cursor();
271 current_canvas_cursor = _cursors->cross_hair;
275 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
276 current_canvas_cursor = _cursors->zoom_out;
278 current_canvas_cursor = _cursors->zoom_in;
283 current_canvas_cursor = _cursors->time_fx; // just use playhead
287 current_canvas_cursor = _cursors->speaker;
292 switch (_join_object_range_state) {
293 case JOIN_OBJECT_RANGE_NONE:
295 case JOIN_OBJECT_RANGE_OBJECT:
296 current_canvas_cursor = which_grabber_cursor ();
298 case JOIN_OBJECT_RANGE_RANGE:
299 current_canvas_cursor = _cursors->selector;
303 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
304 if (join_object_range_button.get_active() && last_item_entered) {
305 if (last_item_entered->property_parent() && (*last_item_entered->property_parent()).get_data (X_("timeselection"))) {
306 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value() - canvas_timebars_vsize);
307 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
308 current_canvas_cursor = _cursors->up_down;
313 set_canvas_cursor (current_canvas_cursor, true);
317 Editor::set_mouse_mode (MouseMode m, bool force)
319 if (_drags->active ()) {
323 if (!force && m == mouse_mode) {
327 Glib::RefPtr<Action> act;
331 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
335 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
339 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
343 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
347 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
351 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
357 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
360 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
361 tact->set_active (false);
362 tact->set_active (true);
364 MouseModeChanged (); /* EMIT SIGNAL */
368 Editor::mouse_mode_toggled (MouseMode m)
374 if (!internal_editing()) {
375 if (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
377 /* in all modes except range and joined object/range, hide the range selection,
378 show the object (region) selection.
381 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
382 (*i)->hide_selection ();
388 in range or object/range mode, show the range selection.
391 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
392 (*i)->show_selection (selection->time);
397 set_canvas_cursor ();
399 MouseModeChanged (); /* EMIT SIGNAL */
403 Editor::step_mouse_mode (bool next)
405 switch (current_mouse_mode()) {
408 if (Profile->get_sae()) {
409 set_mouse_mode (MouseZoom);
411 set_mouse_mode (MouseRange);
414 set_mouse_mode (MouseTimeFX);
419 if (next) set_mouse_mode (MouseZoom);
420 else set_mouse_mode (MouseObject);
425 if (Profile->get_sae()) {
426 set_mouse_mode (MouseTimeFX);
428 set_mouse_mode (MouseGain);
431 if (Profile->get_sae()) {
432 set_mouse_mode (MouseObject);
434 set_mouse_mode (MouseRange);
440 if (next) set_mouse_mode (MouseTimeFX);
441 else set_mouse_mode (MouseZoom);
446 set_mouse_mode (MouseAudition);
448 if (Profile->get_sae()) {
449 set_mouse_mode (MouseZoom);
451 set_mouse_mode (MouseGain);
457 if (next) set_mouse_mode (MouseObject);
458 else set_mouse_mode (MouseTimeFX);
464 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
466 /* in object/audition/timefx/gain-automation mode,
467 any button press sets the selection if the object
468 can be selected. this is a bit of hack, because
469 we want to avoid this if the mouse operation is a
472 note: not dbl-click or triple-click
474 Also note that there is no region selection in internal edit mode, otherwise
475 for operations operating on the selection (e.g. cut) it is not obvious whether
476 to cut notes or regions.
479 if (((mouse_mode != MouseObject) &&
480 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
481 (mouse_mode != MouseAudition || item_type != RegionItem) &&
482 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
483 (mouse_mode != MouseGain) &&
484 (mouse_mode != MouseRange)) ||
485 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
486 internal_editing()) {
491 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
493 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
495 /* almost no selection action on modified button-2 or button-3 events */
497 if (item_type != RegionItem && event->button.button != 2) {
503 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
504 bool press = (event->type == GDK_BUTTON_PRESS);
506 // begin_reversible_command (_("select on click"));
510 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
511 set_selected_regionview_from_click (press, op, true);
512 } else if (event->type == GDK_BUTTON_PRESS) {
513 selection->clear_tracks ();
514 set_selected_track_as_side_effect (op, true);
516 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
517 clicked_selection = select_range_around_region (selection->regions.front());
521 case RegionViewNameHighlight:
523 case LeftFrameHandle:
524 case RightFrameHandle:
525 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
526 set_selected_regionview_from_click (press, op, true);
527 } else if (event->type == GDK_BUTTON_PRESS) {
528 set_selected_track_as_side_effect (op);
533 case FadeInHandleItem:
535 case FadeOutHandleItem:
537 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
538 set_selected_regionview_from_click (press, op, true);
539 } else if (event->type == GDK_BUTTON_PRESS) {
540 set_selected_track_as_side_effect (op);
544 case ControlPointItem:
545 set_selected_track_as_side_effect (op, true);
546 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
547 set_selected_control_point_from_click (op, false);
552 /* for context click, select track */
553 if (event->button.button == 3) {
554 selection->clear_tracks ();
555 set_selected_track_as_side_effect (op, true);
559 case AutomationTrackItem:
560 set_selected_track_as_side_effect (op, true);
569 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
571 /* single mouse clicks on any of these item types operate
572 independent of mouse mode, mostly because they are
573 not on the main track canvas or because we want
578 case PlayheadCursorItem:
579 _drags->set (new CursorDrag (this, item, true), event);
583 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
584 hide_marker (item, event);
586 _drags->set (new MarkerDrag (this, item), event);
590 case TempoMarkerItem:
592 new TempoMarkerDrag (
595 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
601 case MeterMarkerItem:
603 new MeterMarkerDrag (
606 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
615 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
616 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
622 case RangeMarkerBarItem:
623 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
624 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
626 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
631 case CdMarkerBarItem:
632 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
633 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
635 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
640 case TransportMarkerBarItem:
641 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
642 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
644 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
653 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
654 /* special case: allow trim of range selections in joined object mode;
655 in theory eff should equal MouseRange in this case, but it doesn't
656 because entering the range selection canvas item results in entered_regionview
657 being set to 0, so update_join_object_range_location acts as if we aren't
660 if (item_type == StartSelectionTrimItem) {
661 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
662 } else if (item_type == EndSelectionTrimItem) {
663 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
667 Editing::MouseMode eff = effective_mouse_mode ();
669 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
670 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
677 case StartSelectionTrimItem:
678 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
681 case EndSelectionTrimItem:
682 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
686 if (Keyboard::modifier_state_contains
687 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
688 // contains and not equals because I can't use alt as a modifier alone.
689 start_selection_grab (item, event);
690 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
691 /* grab selection for moving */
692 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
694 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
695 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
697 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
698 if (join_object_range_button.get_active() && atv) {
699 /* smart "join" mode: drag automation */
700 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, _cursors->up_down);
702 /* this was debated, but decided the more common action was to
703 make a new selection */
704 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
711 if (internal_editing()) {
712 /* trim notes if we're in internal edit mode and near the ends of the note */
713 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
714 cerr << "NoteItem button press, cursor = " << current_canvas_cursor << endl;
715 if (cn->mouse_near_ends()) {
716 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
718 _drags->set (new NoteDrag (this, item), event);
724 if (internal_editing()) {
725 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
726 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
730 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
735 case RegionViewNameHighlight:
736 if (!clicked_regionview->region()->locked()) {
737 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
738 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
743 case LeftFrameHandle:
744 case RightFrameHandle:
745 if (!internal_editing() && !clicked_regionview->region()->locked()) {
746 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
747 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
753 if (!internal_editing()) {
754 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
763 if (internal_editing()) {
764 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
765 if (cn->mouse_near_ends()) {
766 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
768 _drags->set (new NoteDrag (this, item), event);
778 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
779 event->type == GDK_BUTTON_PRESS) {
781 _drags->set (new RubberbandSelectDrag (this, item), event);
783 } else if (event->type == GDK_BUTTON_PRESS) {
786 case FadeInHandleItem:
788 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
789 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
793 case FadeOutHandleItem:
795 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
796 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
800 case FeatureLineItem:
802 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
803 remove_transient(item);
807 _drags->set (new FeatureLineDrag (this, item), event);
813 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
814 /* click on an automation region view; do nothing here and let the ARV's signal handler
820 if (internal_editing ()) {
821 /* no region drags in internal edit mode */
825 /* click on a normal region view */
826 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
827 add_region_copy_drag (item, event, clicked_regionview);
828 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
829 add_region_brush_drag (item, event, clicked_regionview);
831 add_region_drag (item, event, clicked_regionview);
834 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
835 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
838 _drags->start_grab (event);
841 case RegionViewNameHighlight:
842 case LeftFrameHandle:
843 case RightFrameHandle:
844 if (!clicked_regionview->region()->locked()) {
845 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
846 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
853 /* rename happens on edit clicks */
854 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
855 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
860 case ControlPointItem:
861 _drags->set (new ControlPointDrag (this, item), event);
865 case AutomationLineItem:
866 _drags->set (new LineDrag (this, item), event);
871 if (internal_editing()) {
872 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
873 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
877 _drags->set (new RubberbandSelectDrag (this, item), event);
881 case AutomationTrackItem:
882 /* rubberband drag to select automation points */
883 _drags->set (new RubberbandSelectDrag (this, item), event);
888 if (join_object_range_button.get_active()) {
889 /* we're in "smart" joined mode, and we've clicked on a Selection */
890 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
891 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
893 /* if we're over an automation track, start a drag of its data */
894 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
896 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, _cursors->up_down);
899 /* if we're over a track and a region, and in the `object' part of a region,
900 put a selection around the region and drag both
902 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
903 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
904 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
906 boost::shared_ptr<Playlist> pl = t->playlist ();
909 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
911 RegionView* rv = rtv->view()->find_view (r);
912 clicked_selection = select_range_around_region (rv);
913 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
914 list<RegionView*> rvs;
916 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
917 _drags->start_grab (event);
928 case ImageFrameHandleStartItem:
929 imageframe_start_handle_op(item, event) ;
932 case ImageFrameHandleEndItem:
933 imageframe_end_handle_op(item, event) ;
936 case MarkerViewHandleStartItem:
937 markerview_item_start_handle_op(item, event) ;
940 case MarkerViewHandleEndItem:
941 markerview_item_end_handle_op(item, event) ;
945 start_markerview_grab(item, event) ;
948 start_imageframe_grab(item, event) ;
966 /* start a grab so that if we finish after moving
967 we can tell what happened.
969 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
973 _drags->set (new LineDrag (this, item), event);
976 case ControlPointItem:
977 _drags->set (new ControlPointDrag (this, item), event);
988 case ControlPointItem:
989 _drags->set (new ControlPointDrag (this, item), event);
992 case AutomationLineItem:
993 _drags->set (new LineDrag (this, item), event);
997 // XXX need automation mode to identify which
999 // start_line_grab_from_regionview (item, event);
1009 if (event->type == GDK_BUTTON_PRESS) {
1010 _drags->set (new MouseZoomDrag (this, item), event);
1017 if (internal_editing() && item_type == NoteItem) {
1018 /* drag notes if we're in internal edit mode */
1019 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1021 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
1022 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
1023 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1029 _drags->set (new ScrubDrag (this, item), event);
1030 scrub_reversals = 0;
1031 scrub_reverse_distance = 0;
1032 last_scrub_x = event->button.x;
1033 scrubbing_direction = 0;
1034 set_canvas_cursor (_cursors->transparent);
1046 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1048 Editing::MouseMode const eff = effective_mouse_mode ();
1051 switch (item_type) {
1053 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1054 add_region_copy_drag (item, event, clicked_regionview);
1056 add_region_drag (item, event, clicked_regionview);
1058 _drags->start_grab (event);
1061 case ControlPointItem:
1062 _drags->set (new ControlPointDrag (this, item), event);
1070 switch (item_type) {
1071 case RegionViewNameHighlight:
1072 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1076 case LeftFrameHandle:
1077 case RightFrameHandle:
1078 if (!internal_editing ()) {
1079 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1084 case RegionViewName:
1085 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1096 /* relax till release */
1102 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1103 temporal_zoom_to_frame (false, event_frame (event));
1105 temporal_zoom_to_frame (true, event_frame(event));
1118 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1120 if (event->type != GDK_BUTTON_PRESS) {
1124 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1126 if (canvas_window) {
1127 Glib::RefPtr<const Gdk::Window> pointer_window;
1130 Gdk::ModifierType mask;
1132 pointer_window = canvas_window->get_pointer (x, y, mask);
1134 if (pointer_window == track_canvas->get_bin_window()) {
1135 track_canvas->window_to_world (x, y, wx, wy);
1139 pre_press_cursor = current_canvas_cursor;
1141 track_canvas->grab_focus();
1143 if (_session && _session->actively_recording()) {
1147 button_selection (item, event, item_type);
1149 if (!_drags->active () &&
1150 (Keyboard::is_delete_event (&event->button) ||
1151 Keyboard::is_context_menu_event (&event->button) ||
1152 Keyboard::is_edit_event (&event->button))) {
1154 /* handled by button release */
1158 switch (event->button.button) {
1160 return button_press_handler_1 (item, event, item_type);
1164 return button_press_handler_2 (item, event, item_type);
1179 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1181 framepos_t where = event_frame (event, 0, 0);
1182 AutomationTimeAxisView* atv = 0;
1184 if (pre_press_cursor) {
1185 set_canvas_cursor (pre_press_cursor);
1186 pre_press_cursor = 0;
1189 /* no action if we're recording */
1191 if (_session && _session->actively_recording()) {
1195 /* see if we're finishing a drag */
1197 bool were_dragging = false;
1198 if (_drags->active ()) {
1199 bool const r = _drags->end_grab (event);
1201 /* grab dragged, so do nothing else */
1205 were_dragging = true;
1208 update_region_layering_order_editor ();
1210 /* edit events get handled here */
1212 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1213 switch (item_type) {
1215 show_region_properties ();
1218 case TempoMarkerItem:
1219 edit_tempo_marker (item);
1222 case MeterMarkerItem:
1223 edit_meter_marker (item);
1226 case RegionViewName:
1227 if (clicked_regionview->name_active()) {
1228 return mouse_rename_region (item, event);
1232 case ControlPointItem:
1233 edit_control_point (item);
1246 /* context menu events get handled here */
1248 if (Keyboard::is_context_menu_event (&event->button)) {
1250 if (!_drags->active ()) {
1252 /* no matter which button pops up the context menu, tell the menu
1253 widget to use button 1 to drive menu selection.
1256 switch (item_type) {
1258 case FadeInHandleItem:
1260 case FadeOutHandleItem:
1261 popup_fade_context_menu (1, event->button.time, item, item_type);
1265 popup_track_context_menu (1, event->button.time, item_type, false);
1269 case RegionViewNameHighlight:
1270 case LeftFrameHandle:
1271 case RightFrameHandle:
1272 case RegionViewName:
1273 popup_track_context_menu (1, event->button.time, item_type, false);
1277 popup_track_context_menu (1, event->button.time, item_type, true);
1280 case AutomationTrackItem:
1281 popup_track_context_menu (1, event->button.time, item_type, false);
1285 case RangeMarkerBarItem:
1286 case TransportMarkerBarItem:
1287 case CdMarkerBarItem:
1290 popup_ruler_menu (where, item_type);
1294 marker_context_menu (&event->button, item);
1297 case TempoMarkerItem:
1298 tempo_or_meter_marker_context_menu (&event->button, item);
1301 case MeterMarkerItem:
1302 tempo_or_meter_marker_context_menu (&event->button, item);
1305 case CrossfadeViewItem:
1306 popup_track_context_menu (1, event->button.time, item_type, false);
1310 case ImageFrameItem:
1311 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1313 case ImageFrameTimeAxisItem:
1314 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1316 case MarkerViewItem:
1317 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1319 case MarkerTimeAxisItem:
1320 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1332 /* delete events get handled here */
1334 Editing::MouseMode const eff = effective_mouse_mode ();
1336 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1338 switch (item_type) {
1339 case TempoMarkerItem:
1340 remove_tempo_marker (item);
1343 case MeterMarkerItem:
1344 remove_meter_marker (item);
1348 remove_marker (*item, event);
1352 if (eff == MouseObject) {
1353 remove_clicked_region ();
1357 case ControlPointItem:
1358 if (eff == MouseGain) {
1359 remove_gain_control_point (item, event);
1361 remove_control_point (item, event);
1366 remove_midi_note (item, event);
1375 switch (event->button.button) {
1378 switch (item_type) {
1379 /* see comments in button_press_handler */
1380 case PlayheadCursorItem:
1383 case AutomationLineItem:
1384 case StartSelectionTrimItem:
1385 case EndSelectionTrimItem:
1389 if (!_dragging_playhead) {
1390 snap_to_with_modifier (where, event, 0, true);
1391 mouse_add_new_marker (where);
1395 case CdMarkerBarItem:
1396 if (!_dragging_playhead) {
1397 // if we get here then a dragged range wasn't done
1398 snap_to_with_modifier (where, event, 0, true);
1399 mouse_add_new_marker (where, true);
1404 if (!_dragging_playhead) {
1405 snap_to_with_modifier (where, event);
1406 mouse_add_new_tempo_event (where);
1411 if (!_dragging_playhead) {
1412 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1423 switch (item_type) {
1424 case AutomationTrackItem:
1425 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1427 atv->add_automation_event (item, event, where, event->button.y);
1438 switch (item_type) {
1441 /* check that we didn't drag before releasing, since
1442 its really annoying to create new control
1443 points when doing this.
1445 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1446 if (were_dragging && arv) {
1447 arv->add_gain_point_event (item, event);
1453 case AutomationTrackItem:
1454 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1455 add_automation_event (item, event, where, event->button.y);
1464 set_canvas_cursor (current_canvas_cursor);
1465 if (scrubbing_direction == 0) {
1466 /* no drag, just a click */
1467 switch (item_type) {
1469 play_selected_region ();
1475 /* make sure we stop */
1476 _session->request_transport_speed (0.0);
1493 switch (item_type) {
1495 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1497 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1500 // Button2 click is unused
1513 // x_style_paste (where, 1.0);
1533 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1540 last_item_entered = item;
1542 switch (item_type) {
1543 case ControlPointItem:
1544 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1545 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1546 cp->set_visible (true);
1550 at_y = cp->get_y ();
1551 cp->i2w (at_x, at_y);
1555 fraction = 1.0 - (cp->get_y() / cp->line().height());
1557 if (is_drawable() && !_drags->active ()) {
1558 set_canvas_cursor (_cursors->fader);
1561 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1562 show_verbose_canvas_cursor ();
1567 if (mouse_mode == MouseGain) {
1568 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1570 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1571 if (is_drawable()) {
1572 set_canvas_cursor (_cursors->fader);
1577 case AutomationLineItem:
1578 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1580 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1582 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1584 if (is_drawable()) {
1585 set_canvas_cursor (_cursors->fader);
1590 case RegionViewNameHighlight:
1591 if (is_drawable() && mouse_mode == MouseObject && entered_regionview) {
1592 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1593 _over_region_trim_target = true;
1597 case LeftFrameHandle:
1598 case RightFrameHandle:
1599 if (is_drawable() && mouse_mode == MouseObject && !internal_editing() && entered_regionview) {
1600 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1604 case StartSelectionTrimItem:
1605 case EndSelectionTrimItem:
1608 case ImageFrameHandleStartItem:
1609 case ImageFrameHandleEndItem:
1610 case MarkerViewHandleStartItem:
1611 case MarkerViewHandleEndItem:
1614 if (is_drawable()) {
1615 set_canvas_cursor (_cursors->trimmer);
1619 case PlayheadCursorItem:
1620 if (is_drawable()) {
1621 switch (_edit_point) {
1623 set_canvas_cursor (_cursors->grabber_edit_point);
1626 set_canvas_cursor (_cursors->grabber);
1632 case RegionViewName:
1634 /* when the name is not an active item, the entire name highlight is for trimming */
1636 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1637 if (mouse_mode == MouseObject && is_drawable()) {
1638 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1639 _over_region_trim_target = true;
1645 case AutomationTrackItem:
1646 if (is_drawable()) {
1647 Gdk::Cursor *cursor;
1648 switch (mouse_mode) {
1650 cursor = _cursors->selector;
1653 cursor = _cursors->zoom_in;
1656 cursor = _cursors->cross_hair;
1660 set_canvas_cursor (cursor);
1662 AutomationTimeAxisView* atv;
1663 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1664 clear_entered_track = false;
1665 set_entered_track (atv);
1671 case RangeMarkerBarItem:
1672 case TransportMarkerBarItem:
1673 case CdMarkerBarItem:
1676 if (is_drawable()) {
1677 set_canvas_cursor (_cursors->timebar);
1682 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1685 entered_marker = marker;
1686 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1688 case MeterMarkerItem:
1689 case TempoMarkerItem:
1690 if (is_drawable()) {
1691 set_canvas_cursor (_cursors->timebar);
1695 case FadeInHandleItem:
1696 if (mouse_mode == MouseObject && !internal_editing()) {
1697 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1699 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1701 set_canvas_cursor (_cursors->fade_in);
1705 case FadeOutHandleItem:
1706 if (mouse_mode == MouseObject && !internal_editing()) {
1707 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1709 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1711 set_canvas_cursor (_cursors->fade_out);
1714 case FeatureLineItem:
1716 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1717 line->property_fill_color_rgba() = 0xFF0000FF;
1721 if (join_object_range_button.get_active()) {
1722 set_canvas_cursor ();
1730 /* second pass to handle entered track status in a comprehensible way.
1733 switch (item_type) {
1735 case AutomationLineItem:
1736 case ControlPointItem:
1737 /* these do not affect the current entered track state */
1738 clear_entered_track = false;
1741 case AutomationTrackItem:
1742 /* handled above already */
1746 set_entered_track (0);
1754 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1764 switch (item_type) {
1765 case ControlPointItem:
1766 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1767 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1768 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1769 cp->set_visible (false);
1773 if (is_drawable()) {
1774 set_canvas_cursor (current_canvas_cursor);
1777 hide_verbose_canvas_cursor ();
1780 case RegionViewNameHighlight:
1781 case LeftFrameHandle:
1782 case RightFrameHandle:
1783 case StartSelectionTrimItem:
1784 case EndSelectionTrimItem:
1785 case PlayheadCursorItem:
1788 case ImageFrameHandleStartItem:
1789 case ImageFrameHandleEndItem:
1790 case MarkerViewHandleStartItem:
1791 case MarkerViewHandleEndItem:
1794 _over_region_trim_target = false;
1796 if (is_drawable()) {
1797 set_canvas_cursor (current_canvas_cursor);
1802 case AutomationLineItem:
1803 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1805 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1807 line->property_fill_color_rgba() = al->get_line_color();
1809 if (is_drawable()) {
1810 set_canvas_cursor (current_canvas_cursor);
1814 case RegionViewName:
1815 /* see enter_handler() for notes */
1816 _over_region_trim_target = false;
1818 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1819 if (is_drawable() && mouse_mode == MouseObject) {
1820 set_canvas_cursor (current_canvas_cursor);
1825 case RangeMarkerBarItem:
1826 case TransportMarkerBarItem:
1827 case CdMarkerBarItem:
1831 if (is_drawable()) {
1832 set_canvas_cursor (current_canvas_cursor);
1837 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1841 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1842 location_flags_changed (loc, this);
1845 case MeterMarkerItem:
1846 case TempoMarkerItem:
1848 if (is_drawable()) {
1849 set_canvas_cursor (_cursors->timebar);
1854 case FadeInHandleItem:
1855 case FadeOutHandleItem:
1856 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1858 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1860 rect->property_fill_color_rgba() = rv->get_fill_color();
1861 rect->property_outline_pixels() = 0;
1864 set_canvas_cursor (current_canvas_cursor);
1867 case AutomationTrackItem:
1868 if (is_drawable()) {
1869 set_canvas_cursor (current_canvas_cursor);
1870 clear_entered_track = true;
1871 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1874 case FeatureLineItem:
1876 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1877 line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
1889 Editor::left_automation_track ()
1891 if (clear_entered_track) {
1892 set_entered_track (0);
1893 clear_entered_track = false;
1899 Editor::scrub (framepos_t frame, double current_x)
1903 if (scrubbing_direction == 0) {
1905 _session->request_locate (frame, false);
1906 _session->request_transport_speed (0.1);
1907 scrubbing_direction = 1;
1911 if (last_scrub_x > current_x) {
1913 /* pointer moved to the left */
1915 if (scrubbing_direction > 0) {
1917 /* we reversed direction to go backwards */
1920 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1924 /* still moving to the left (backwards) */
1926 scrub_reversals = 0;
1927 scrub_reverse_distance = 0;
1929 delta = 0.01 * (last_scrub_x - current_x);
1930 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1934 /* pointer moved to the right */
1936 if (scrubbing_direction < 0) {
1937 /* we reversed direction to go forward */
1940 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1943 /* still moving to the right */
1945 scrub_reversals = 0;
1946 scrub_reverse_distance = 0;
1948 delta = 0.01 * (current_x - last_scrub_x);
1949 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1953 /* if there have been more than 2 opposite motion moves detected, or one that moves
1954 back more than 10 pixels, reverse direction
1957 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1959 if (scrubbing_direction > 0) {
1960 /* was forwards, go backwards */
1961 _session->request_transport_speed (-0.1);
1962 scrubbing_direction = -1;
1964 /* was backwards, go forwards */
1965 _session->request_transport_speed (0.1);
1966 scrubbing_direction = 1;
1969 scrub_reverse_distance = 0;
1970 scrub_reversals = 0;
1974 last_scrub_x = current_x;
1978 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1980 _last_motion_y = event->motion.y;
1982 if (event->motion.is_hint) {
1985 /* We call this so that MOTION_NOTIFY events continue to be
1986 delivered to the canvas. We need to do this because we set
1987 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1988 the density of the events, at the expense of a round-trip
1989 to the server. Given that this will mostly occur on cases
1990 where DISPLAY = :0.0, and given the cost of what the motion
1991 event might do, its a good tradeoff.
1994 track_canvas->get_pointer (x, y);
1997 if (current_stepping_trackview) {
1998 /* don't keep the persistent stepped trackview if the mouse moves */
1999 current_stepping_trackview = 0;
2000 step_timeout.disconnect ();
2003 if (_session && _session->actively_recording()) {
2004 /* Sorry. no dragging stuff around while we record */
2008 JoinObjectRangeState const old = _join_object_range_state;
2009 update_join_object_range_location (event->motion.x, event->motion.y);
2010 if (_join_object_range_state != old) {
2011 set_canvas_cursor ();
2014 if (_over_region_trim_target) {
2015 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2018 bool handled = false;
2019 if (_drags->active ()) {
2020 handled = _drags->motion_handler (event, from_autoscroll);
2027 track_canvas_motion (event);
2032 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
2034 ControlPoint* control_point;
2036 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2037 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2041 // We shouldn't remove the first or last gain point
2042 if (control_point->line().is_last_point(*control_point) ||
2043 control_point->line().is_first_point(*control_point)) {
2047 control_point->line().remove_point (*control_point);
2051 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2053 ControlPoint* control_point;
2055 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2056 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2060 control_point->line().remove_point (*control_point);
2064 Editor::edit_control_point (ArdourCanvas::Item* item)
2066 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2069 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2073 ControlPointDialog d (p);
2074 d.set_position (Gtk::WIN_POS_MOUSE);
2077 if (d.run () != RESPONSE_ACCEPT) {
2081 p->line().modify_point_y (*p, d.get_y_fraction ());
2085 Editor::edit_note (ArdourCanvas::Item* item)
2087 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2090 EditNoteDialog d (&e->region_view(), e);
2091 d.set_position (Gtk::WIN_POS_MOUSE);
2099 Editor::visible_order_range (int* low, int* high) const
2101 *low = TimeAxisView::max_order ();
2104 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2106 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2108 if (!rtv->hidden()) {
2110 if (*high < rtv->order()) {
2111 *high = rtv->order ();
2114 if (*low > rtv->order()) {
2115 *low = rtv->order ();
2122 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2124 /* Either add to or set the set the region selection, unless
2125 this is an alignment click (control used)
2128 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2129 TimeAxisView* tv = &rv.get_time_axis_view();
2130 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2132 if (rtv && rtv->is_track()) {
2133 speed = rtv->track()->speed();
2136 framepos_t where = get_preferred_edit_position();
2140 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2142 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2144 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2146 align_region (rv.region(), End, (framepos_t) (where * speed));
2150 align_region (rv.region(), Start, (framepos_t) (where * speed));
2157 Editor::show_verbose_time_cursor (framepos_t frame, double offset, double xpos, double ypos)
2160 Timecode::Time timecode;
2161 Timecode::BBT_Time bbt;
2163 framepos_t frame_rate;
2166 if (_session == 0) {
2172 if (Profile->get_sae() || Profile->get_small_screen()) {
2173 m = ARDOUR_UI::instance()->primary_clock.mode();
2175 m = ARDOUR_UI::instance()->secondary_clock.mode();
2179 case AudioClock::BBT:
2180 _session->bbt_time (frame, bbt);
2181 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2184 case AudioClock::Timecode:
2185 _session->timecode_time (frame, timecode);
2186 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2189 case AudioClock::MinSec:
2190 /* XXX this is copied from show_verbose_duration_cursor() */
2191 frame_rate = _session->frame_rate();
2192 hours = frame / (frame_rate * 3600);
2193 frame = frame % (frame_rate * 3600);
2194 mins = frame / (frame_rate * 60);
2195 frame = frame % (frame_rate * 60);
2196 secs = (float) frame / (float) frame_rate;
2197 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2201 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2205 if (xpos >= 0 && ypos >=0) {
2206 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2208 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_position(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2210 show_verbose_canvas_cursor ();
2214 Editor::show_verbose_duration_cursor (framepos_t start, framepos_t end, double offset, double xpos, double ypos)
2217 Timecode::Time timecode;
2218 Timecode::BBT_Time sbbt;
2219 Timecode::BBT_Time ebbt;
2221 framepos_t distance, frame_rate;
2223 Meter meter_at_start(_session->tempo_map().meter_at(start));
2225 if (_session == 0) {
2231 if (Profile->get_sae() || Profile->get_small_screen()) {
2232 m = ARDOUR_UI::instance()->primary_clock.mode ();
2234 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2238 case AudioClock::BBT:
2239 _session->bbt_time (start, sbbt);
2240 _session->bbt_time (end, ebbt);
2243 /* XXX this computation won't work well if the
2244 user makes a selection that spans any meter changes.
2247 ebbt.bars -= sbbt.bars;
2248 if (ebbt.beats >= sbbt.beats) {
2249 ebbt.beats -= sbbt.beats;
2252 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2254 if (ebbt.ticks >= sbbt.ticks) {
2255 ebbt.ticks -= sbbt.ticks;
2258 ebbt.ticks = int(Timecode::BBT_Time::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2261 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2264 case AudioClock::Timecode:
2265 _session->timecode_duration (end - start, timecode);
2266 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2269 case AudioClock::MinSec:
2270 /* XXX this stuff should be elsewhere.. */
2271 distance = end - start;
2272 frame_rate = _session->frame_rate();
2273 hours = distance / (frame_rate * 3600);
2274 distance = distance % (frame_rate * 3600);
2275 mins = distance / (frame_rate * 60);
2276 distance = distance % (frame_rate * 60);
2277 secs = (float) distance / (float) frame_rate;
2278 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2282 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2286 if (xpos >= 0 && ypos >=0) {
2287 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2290 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2293 show_verbose_canvas_cursor ();
2297 Editor::collect_new_region_view (RegionView* rv)
2299 latest_regionviews.push_back (rv);
2303 Editor::collect_and_select_new_region_view (RegionView* rv)
2306 latest_regionviews.push_back (rv);
2310 Editor::cancel_selection ()
2312 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2313 (*i)->hide_selection ();
2316 selection->clear ();
2317 clicked_selection = 0;
2322 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2324 RegionView* rv = clicked_regionview;
2326 /* Choose action dependant on which button was pressed */
2327 switch (event->button.button) {
2329 begin_reversible_command (_("start point trim"));
2331 if (selection->selected (rv)) {
2332 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2333 i != selection->regions.by_layer().end(); ++i)
2336 cerr << "region view contains null region" << endl;
2339 if (!(*i)->region()->locked()) {
2340 (*i)->region()->clear_changes ();
2341 (*i)->region()->trim_front (new_bound, this);
2342 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2347 if (!rv->region()->locked()) {
2348 rv->region()->clear_changes ();
2349 rv->region()->trim_front (new_bound, this);
2350 _session->add_command(new StatefulDiffCommand (rv->region()));
2354 commit_reversible_command();
2358 begin_reversible_command (_("End point trim"));
2360 if (selection->selected (rv)) {
2362 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2364 if (!(*i)->region()->locked()) {
2365 (*i)->region()->clear_changes();
2366 (*i)->region()->trim_end (new_bound, this);
2367 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2373 if (!rv->region()->locked()) {
2374 rv->region()->clear_changes ();
2375 rv->region()->trim_end (new_bound, this);
2376 _session->add_command (new StatefulDiffCommand (rv->region()));
2380 commit_reversible_command();
2389 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2394 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2395 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2399 Location* location = find_location_from_marker (marker, is_start);
2400 location->set_hidden (true, this);
2405 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2407 double x1 = frame_to_pixel (start);
2408 double x2 = frame_to_pixel (end);
2409 double y2 = full_canvas_height - 1.0;
2411 zoom_rect->property_x1() = x1;
2412 zoom_rect->property_y1() = 1.0;
2413 zoom_rect->property_x2() = x2;
2414 zoom_rect->property_y2() = y2;
2419 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2421 using namespace Gtkmm2ext;
2423 ArdourPrompter prompter (false);
2425 prompter.set_prompt (_("Name for region:"));
2426 prompter.set_initial_text (clicked_regionview->region()->name());
2427 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2428 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2429 prompter.show_all ();
2430 switch (prompter.run ()) {
2431 case Gtk::RESPONSE_ACCEPT:
2433 prompter.get_result(str);
2435 clicked_regionview->region()->set_name (str);
2444 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2446 /* no brushing without a useful snap setting */
2448 switch (_snap_mode) {
2450 return; /* can't work because it allows region to be placed anywhere */
2455 switch (_snap_type) {
2463 /* don't brush a copy over the original */
2465 if (pos == rv->region()->position()) {
2469 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2471 if (rtv == 0 || !rtv->is_track()) {
2475 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2476 double speed = rtv->track()->speed();
2478 playlist->clear_changes ();
2479 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2480 playlist->add_region (new_region, (framepos_t) (pos * speed));
2481 _session->add_command (new StatefulDiffCommand (playlist));
2483 // playlist is frozen, so we have to update manually XXX this is disgusting
2485 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2489 Editor::track_height_step_timeout ()
2491 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2492 current_stepping_trackview = 0;
2499 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2501 assert (region_view);
2503 if (!region_view->region()->playlist()) {
2507 _region_motion_group->raise_to_top ();
2509 if (Config->get_edit_mode() == Splice) {
2510 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2512 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2513 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2516 /* sync the canvas to what we think is its current state */
2517 update_canvas_now();
2521 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2523 assert (region_view);
2525 if (!region_view->region()->playlist()) {
2529 _region_motion_group->raise_to_top ();
2531 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2532 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2536 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2538 assert (region_view);
2540 if (!region_view->region()->playlist()) {
2544 if (Config->get_edit_mode() == Splice) {
2548 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2549 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2551 begin_reversible_command (Operations::drag_region_brush);
2554 /** Start a grab where a time range is selected, track(s) are selected, and the
2555 * user clicks and drags a region with a modifier in order to create a new region containing
2556 * the section of the clicked region that lies within the time range.
2559 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2561 if (clicked_regionview == 0) {
2565 /* lets try to create new Region for the selection */
2567 vector<boost::shared_ptr<Region> > new_regions;
2568 create_region_from_selection (new_regions);
2570 if (new_regions.empty()) {
2574 /* XXX fix me one day to use all new regions */
2576 boost::shared_ptr<Region> region (new_regions.front());
2578 /* add it to the current stream/playlist.
2580 tricky: the streamview for the track will add a new regionview. we will
2581 catch the signal it sends when it creates the regionview to
2582 set the regionview we want to then drag.
2585 latest_regionviews.clear();
2586 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2588 /* A selection grab currently creates two undo/redo operations, one for
2589 creating the new region and another for moving it.
2592 begin_reversible_command (Operations::selection_grab);
2594 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2596 playlist->clear_changes ();
2597 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2598 _session->add_command(new StatefulDiffCommand (playlist));
2600 commit_reversible_command ();
2604 if (latest_regionviews.empty()) {
2605 /* something went wrong */
2609 /* we need to deselect all other regionviews, and select this one
2610 i'm ignoring undo stuff, because the region creation will take care of it
2612 selection->set (latest_regionviews);
2614 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2620 if (_drags->active ()) {
2623 selection->clear ();
2628 Editor::set_internal_edit (bool yn)
2630 _internal_editing = yn;
2633 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2634 mouse_select_button.get_image ()->show ();
2635 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2636 mouse_mode_toggled (mouse_mode);
2638 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2639 (*i)->enter_internal_edit_mode ();
2644 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2645 mouse_select_button.get_image ()->show ();
2646 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Select/Move Ranges"));
2647 mouse_mode_toggled (mouse_mode); // sets cursor
2649 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2650 (*i)->leave_internal_edit_mode ();
2655 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2656 * used by the `join object/range' tool mode.
2659 Editor::update_join_object_range_location (double x, double y)
2661 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2662 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2663 that we're over requires searching the playlist.
2666 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2667 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2671 if (mouse_mode == MouseObject) {
2672 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2673 } else if (mouse_mode == MouseRange) {
2674 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2677 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2678 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2682 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2687 rtv->canvas_display()->w2i (cx, cy);
2689 double const c = cy / rtv->view()->child_height();
2691 double const f = modf (c, &d);
2693 _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2699 Editor::effective_mouse_mode () const
2701 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2703 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2711 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2713 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2716 e->region_view().delete_note (e->note ());
2720 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2722 ArdourCanvas::Group* g = rv->get_canvas_group ();
2723 ArdourCanvas::Group* p = g->get_parent_group ();
2725 /* Compute x in region view parent coordinates */
2729 double x1, x2, y1, y2;
2730 g->get_bounds (x1, y1, x2, y2);
2732 /* Halfway across the region */
2733 double const h = (x1 + x2) / 2;
2735 Trimmable::CanTrim ct = rv->region()->can_trim ();
2737 if (ct & Trimmable::FrontTrimEarlier) {
2738 set_canvas_cursor (_cursors->left_side_trim);
2740 set_canvas_cursor (_cursors->left_side_trim_right_only);
2743 if (ct & Trimmable::EndTrimLater) {
2744 set_canvas_cursor (_cursors->right_side_trim);
2746 set_canvas_cursor (_cursors->right_side_trim_left_only);