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 "pbd/memento_command.h"
31 #include "pbd/basename.h"
32 #include "pbd/stateful_diff_command.h"
34 #include "gtkmm2ext/bindings.h"
35 #include "gtkmm2ext/utils.h"
36 #include "gtkmm2ext/tearoff.h"
38 #include "ardour_ui.h"
40 #include "canvas-note.h"
42 #include "time_axis_view.h"
43 #include "audio_time_axis.h"
44 #include "audio_region_view.h"
45 #include "midi_region_view.h"
47 #include "streamview.h"
48 #include "region_gain_line.h"
49 #include "automation_time_axis.h"
50 #include "control_point.h"
53 #include "selection.h"
56 #include "rgb_macros.h"
57 #include "control_point_dialog.h"
58 #include "editor_drag.h"
59 #include "automation_region_view.h"
60 #include "edit_note_dialog.h"
61 #include "mouse_cursors.h"
62 #include "editor_cursors.h"
63 #include "verbose_cursor.h"
65 #include "ardour/types.h"
66 #include "ardour/profile.h"
67 #include "ardour/route.h"
68 #include "ardour/audio_track.h"
69 #include "ardour/audio_diskstream.h"
70 #include "ardour/midi_diskstream.h"
71 #include "ardour/playlist.h"
72 #include "ardour/audioplaylist.h"
73 #include "ardour/audioregion.h"
74 #include "ardour/midi_region.h"
75 #include "ardour/dB.h"
76 #include "ardour/utils.h"
77 #include "ardour/region_factory.h"
78 #include "ardour/source_factory.h"
79 #include "ardour/session.h"
80 #include "ardour/operations.h"
87 using namespace ARDOUR
;
90 using namespace Editing
;
91 using Gtkmm2ext::Keyboard
;
94 Editor::mouse_frame (framepos_t
& where
, bool& in_track_canvas
) const
98 Gdk::ModifierType mask
;
99 Glib::RefPtr
<Gdk::Window
> canvas_window
= const_cast<Editor
*>(this)->track_canvas
->get_window();
100 Glib::RefPtr
<const Gdk::Window
> pointer_window
;
102 if (!canvas_window
) {
106 pointer_window
= canvas_window
->get_pointer (x
, y
, mask
);
108 if (pointer_window
== track_canvas
->get_bin_window()) {
111 in_track_canvas
= true;
114 in_track_canvas
= false;
119 event
.type
= GDK_BUTTON_RELEASE
;
123 where
= event_frame (&event
, 0, 0);
128 Editor::event_frame (GdkEvent
const * event
, double* pcx
, double* pcy
) const
142 switch (event
->type
) {
143 case GDK_BUTTON_RELEASE
:
144 case GDK_BUTTON_PRESS
:
145 case GDK_2BUTTON_PRESS
:
146 case GDK_3BUTTON_PRESS
:
147 *pcx
= event
->button
.x
;
148 *pcy
= event
->button
.y
;
149 _trackview_group
->w2i(*pcx
, *pcy
);
151 case GDK_MOTION_NOTIFY
:
152 *pcx
= event
->motion
.x
;
153 *pcy
= event
->motion
.y
;
154 _trackview_group
->w2i(*pcx
, *pcy
);
156 case GDK_ENTER_NOTIFY
:
157 case GDK_LEAVE_NOTIFY
:
158 track_canvas
->w2c(event
->crossing
.x
, event
->crossing
.y
, *pcx
, *pcy
);
161 case GDK_KEY_RELEASE
:
162 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
165 warning
<< string_compose (_("Editor::event_frame() used on unhandled event type %1"), event
->type
) << endmsg
;
169 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
170 position is negative (as can be the case with motion events in particular),
171 the frame location is always positive.
174 return pixel_to_frame (*pcx
);
178 Editor::which_grabber_cursor ()
180 Gdk::Cursor
* c
= _cursors
->grabber
;
182 if (_internal_editing
) {
183 switch (mouse_mode
) {
185 c
= _cursors
->midi_pencil
;
189 c
= _cursors
->grabber_note
;
193 c
= _cursors
->midi_resize
;
202 switch (_edit_point
) {
204 c
= _cursors
->grabber_edit_point
;
207 boost::shared_ptr
<Movable
> m
= _movable
.lock();
208 if (m
&& m
->locked()) {
209 c
= _cursors
->speaker
;
219 Editor::set_current_trimmable (boost::shared_ptr
<Trimmable
> t
)
221 boost::shared_ptr
<Trimmable
> st
= _trimmable
.lock();
223 if (!st
|| st
== t
) {
225 set_canvas_cursor ();
230 Editor::set_current_movable (boost::shared_ptr
<Movable
> m
)
232 boost::shared_ptr
<Movable
> sm
= _movable
.lock();
234 if (!sm
|| sm
!= m
) {
236 set_canvas_cursor ();
241 Editor::set_canvas_cursor ()
243 if (_internal_editing
) {
245 switch (mouse_mode
) {
247 current_canvas_cursor
= _cursors
->midi_pencil
;
251 current_canvas_cursor
= which_grabber_cursor();
255 current_canvas_cursor
= _cursors
->midi_resize
;
264 switch (mouse_mode
) {
266 current_canvas_cursor
= _cursors
->selector
;
270 current_canvas_cursor
= which_grabber_cursor();
274 current_canvas_cursor
= _cursors
->cross_hair
;
278 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L
)) {
279 current_canvas_cursor
= _cursors
->zoom_out
;
281 current_canvas_cursor
= _cursors
->zoom_in
;
286 current_canvas_cursor
= _cursors
->time_fx
; // just use playhead
290 current_canvas_cursor
= _cursors
->speaker
;
295 switch (_join_object_range_state
) {
296 case JOIN_OBJECT_RANGE_NONE
:
298 case JOIN_OBJECT_RANGE_OBJECT
:
299 current_canvas_cursor
= which_grabber_cursor ();
301 case JOIN_OBJECT_RANGE_RANGE
:
302 current_canvas_cursor
= _cursors
->selector
;
306 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
307 if (join_object_range_button
.get_active() && last_item_entered
) {
308 if (last_item_entered
->property_parent() && (*last_item_entered
->property_parent()).get_data (X_("timeselection"))) {
309 pair
<TimeAxisView
*, int> tvp
= trackview_by_y_position (_last_motion_y
+ vertical_adjustment
.get_value() - canvas_timebars_vsize
);
310 if (dynamic_cast<AutomationTimeAxisView
*> (tvp
.first
)) {
311 current_canvas_cursor
= _cursors
->up_down
;
316 set_canvas_cursor (current_canvas_cursor
, true);
320 Editor::set_mouse_mode (MouseMode m
, bool force
)
322 if (_drags
->active ()) {
326 if (!force
&& m
== mouse_mode
) {
330 Glib::RefPtr
<Action
> act
;
334 act
= ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
338 act
= ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
342 act
= ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
346 act
= ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
350 act
= ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
354 act
= ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
360 Glib::RefPtr
<ToggleAction
> tact
= Glib::RefPtr
<ToggleAction
>::cast_dynamic (act
);
363 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
364 tact
->set_active (false);
365 tact
->set_active (true);
367 MouseModeChanged (); /* EMIT SIGNAL */
371 Editor::mouse_mode_toggled (MouseMode m
)
377 if (!internal_editing()) {
378 if (mouse_mode
!= MouseRange
&& _join_object_range_state
== JOIN_OBJECT_RANGE_NONE
) {
380 /* in all modes except range and joined object/range, hide the range selection,
381 show the object (region) selection.
384 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
385 (*i
)->hide_selection ();
391 in range or object/range mode, show the range selection.
394 for (TrackSelection::iterator i
= selection
->tracks
.begin(); i
!= selection
->tracks
.end(); ++i
) {
395 (*i
)->show_selection (selection
->time
);
400 set_canvas_cursor ();
402 MouseModeChanged (); /* EMIT SIGNAL */
406 Editor::step_mouse_mode (bool next
)
408 switch (current_mouse_mode()) {
411 if (Profile
->get_sae()) {
412 set_mouse_mode (MouseZoom
);
414 set_mouse_mode (MouseRange
);
417 set_mouse_mode (MouseTimeFX
);
422 if (next
) set_mouse_mode (MouseZoom
);
423 else set_mouse_mode (MouseObject
);
428 if (Profile
->get_sae()) {
429 set_mouse_mode (MouseTimeFX
);
431 set_mouse_mode (MouseGain
);
434 if (Profile
->get_sae()) {
435 set_mouse_mode (MouseObject
);
437 set_mouse_mode (MouseRange
);
443 if (next
) set_mouse_mode (MouseTimeFX
);
444 else set_mouse_mode (MouseZoom
);
449 set_mouse_mode (MouseAudition
);
451 if (Profile
->get_sae()) {
452 set_mouse_mode (MouseZoom
);
454 set_mouse_mode (MouseGain
);
460 if (next
) set_mouse_mode (MouseObject
);
461 else set_mouse_mode (MouseTimeFX
);
467 Editor::button_selection (ArdourCanvas::Item
* /*item*/, GdkEvent
* event
, ItemType item_type
)
469 /* in object/audition/timefx/gain-automation mode,
470 any button press sets the selection if the object
471 can be selected. this is a bit of hack, because
472 we want to avoid this if the mouse operation is a
475 note: not dbl-click or triple-click
477 Also note that there is no region selection in internal edit mode, otherwise
478 for operations operating on the selection (e.g. cut) it is not obvious whether
479 to cut notes or regions.
482 if (((mouse_mode
!= MouseObject
) &&
483 (_join_object_range_state
!= JOIN_OBJECT_RANGE_OBJECT
) &&
484 (mouse_mode
!= MouseAudition
|| item_type
!= RegionItem
) &&
485 (mouse_mode
!= MouseTimeFX
|| item_type
!= RegionItem
) &&
486 (mouse_mode
!= MouseGain
) &&
487 (mouse_mode
!= MouseRange
)) ||
488 ((event
->type
!= GDK_BUTTON_PRESS
&& event
->type
!= GDK_BUTTON_RELEASE
) || event
->button
.button
> 3) ||
489 internal_editing()) {
494 if (event
->type
== GDK_BUTTON_PRESS
|| event
->type
== GDK_BUTTON_RELEASE
) {
496 if ((event
->button
.state
& Keyboard::RelevantModifierKeyMask
) && event
->button
.button
!= 1) {
498 /* almost no selection action on modified button-2 or button-3 events */
500 if (item_type
!= RegionItem
&& event
->button
.button
!= 2) {
506 Selection::Operation op
= ArdourKeyboard::selection_type (event
->button
.state
);
507 bool press
= (event
->type
== GDK_BUTTON_PRESS
);
511 if (mouse_mode
!= MouseRange
|| _join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
) {
512 set_selected_regionview_from_click (press
, op
, true);
513 } else if (event
->type
== GDK_BUTTON_PRESS
) {
514 selection
->clear_tracks ();
515 set_selected_track_as_side_effect (op
, true);
517 if (_join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
&& !selection
->regions
.empty()) {
518 clicked_selection
= select_range_around_region (selection
->regions
.front());
522 case RegionViewNameHighlight
:
524 case LeftFrameHandle
:
525 case RightFrameHandle
:
526 if (mouse_mode
!= MouseRange
|| _join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
) {
527 set_selected_regionview_from_click (press
, op
, true);
528 } else if (event
->type
== GDK_BUTTON_PRESS
) {
529 set_selected_track_as_side_effect (op
);
534 case FadeInHandleItem
:
536 case FadeOutHandleItem
:
538 if (mouse_mode
!= MouseRange
|| _join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
) {
539 set_selected_regionview_from_click (press
, op
, true);
540 } else if (event
->type
== GDK_BUTTON_PRESS
) {
541 set_selected_track_as_side_effect (op
);
545 case ControlPointItem
:
546 set_selected_track_as_side_effect (op
, true);
547 if (mouse_mode
!= MouseRange
|| _join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
) {
548 set_selected_control_point_from_click (op
, false);
553 /* for context click, select track */
554 if (event
->button
.button
== 3) {
555 selection
->clear_tracks ();
556 set_selected_track_as_side_effect (op
, true);
560 case AutomationTrackItem
:
561 set_selected_track_as_side_effect (op
, true);
570 Editor::button_press_handler_1 (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
572 /* single mouse clicks on any of these item types operate
573 independent of mouse mode, mostly because they are
574 not on the main track canvas or because we want
579 case PlayheadCursorItem
:
580 _drags
->set (new CursorDrag (this, item
, true), event
);
584 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::ModifierMask(Keyboard::PrimaryModifier
|Keyboard::TertiaryModifier
))) {
585 hide_marker (item
, event
);
587 _drags
->set (new MarkerDrag (this, item
), event
);
591 case TempoMarkerItem
:
593 new TempoMarkerDrag (
596 Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::CopyModifier
)
602 case MeterMarkerItem
:
604 new MeterMarkerDrag (
607 Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::CopyModifier
)
616 if (!Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
617 _drags
->set (new CursorDrag (this, &playhead_cursor
->canvas_item
, false), event
);
623 case RangeMarkerBarItem
:
624 if (!Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
625 _drags
->set (new CursorDrag (this, &playhead_cursor
->canvas_item
, false), event
);
627 _drags
->set (new RangeMarkerBarDrag (this, item
, RangeMarkerBarDrag::CreateRangeMarker
), event
);
632 case CdMarkerBarItem
:
633 if (!Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
634 _drags
->set (new CursorDrag (this, &playhead_cursor
->canvas_item
, false), event
);
636 _drags
->set (new RangeMarkerBarDrag (this, item
, RangeMarkerBarDrag::CreateCDMarker
), event
);
641 case TransportMarkerBarItem
:
642 if (!Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
643 _drags
->set (new CursorDrag (this, &playhead_cursor
->canvas_item
, false), event
);
645 _drags
->set (new RangeMarkerBarDrag (this, item
, RangeMarkerBarDrag::CreateTransportMarker
), event
);
654 if (_join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
) {
655 /* special case: allow trim of range selections in joined object mode;
656 in theory eff should equal MouseRange in this case, but it doesn't
657 because entering the range selection canvas item results in entered_regionview
658 being set to 0, so update_join_object_range_location acts as if we aren't
661 if (item_type
== StartSelectionTrimItem
) {
662 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::SelectionStartTrim
), event
);
663 } else if (item_type
== EndSelectionTrimItem
) {
664 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::SelectionEndTrim
), event
);
668 Editing::MouseMode eff
= effective_mouse_mode ();
670 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
671 if (item_type
== FadeInHandleItem
|| item_type
== FadeOutHandleItem
) {
678 case StartSelectionTrimItem
:
679 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::SelectionStartTrim
), event
);
682 case EndSelectionTrimItem
:
683 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::SelectionEndTrim
), event
);
687 if (Keyboard::modifier_state_contains
688 (event
->button
.state
, Keyboard::ModifierMask(Keyboard::PrimaryModifier
))) {
689 // contains and not equals because I can't use alt as a modifier alone.
690 start_selection_grab (item
, event
);
691 } else if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::SecondaryModifier
)) {
692 /* grab selection for moving */
693 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::SelectionMove
), event
);
695 double const y
= event
->button
.y
+ vertical_adjustment
.get_value() - canvas_timebars_vsize
;
696 pair
<TimeAxisView
*, int> tvp
= trackview_by_y_position (y
);
698 AutomationTimeAxisView
* atv
= dynamic_cast<AutomationTimeAxisView
*> (tvp
.first
);
699 if (join_object_range_button
.get_active() && atv
) {
700 /* smart "join" mode: drag automation */
701 _drags
->set (new AutomationRangeDrag (this, atv
->base_item(), selection
->time
), event
, _cursors
->up_down
);
703 /* this was debated, but decided the more common action was to
704 make a new selection */
705 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::CreateSelection
), event
);
712 if (internal_editing()) {
713 /* trim notes if we're in internal edit mode and near the ends of the note */
714 ArdourCanvas::CanvasNote
* cn
= dynamic_cast<ArdourCanvas::CanvasNote
*> (item
);
715 if (cn
&& cn
->big_enough_to_trim() && 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
:
883 TimeAxisView
* parent
= clicked_axisview
->get_parent ();
884 if (parent
&& dynamic_cast<MidiTimeAxisView
*> (parent
)) {
885 /* create a MIDI region so that we have somewhere to put automation */
886 _drags
->set (new RegionCreateDrag (this, item
, parent
), event
);
888 /* rubberband drag to select automation points */
889 _drags
->set (new RubberbandSelectDrag (this, item
), event
);
896 if (join_object_range_button
.get_active()) {
897 /* we're in "smart" joined mode, and we've clicked on a Selection */
898 double const y
= event
->button
.y
+ vertical_adjustment
.get_value() - canvas_timebars_vsize
;
899 pair
<TimeAxisView
*, int> tvp
= trackview_by_y_position (y
);
901 /* if we're over an automation track, start a drag of its data */
902 AutomationTimeAxisView
* atv
= dynamic_cast<AutomationTimeAxisView
*> (tvp
.first
);
904 _drags
->set (new AutomationRangeDrag (this, atv
->base_item(), selection
->time
), event
, _cursors
->up_down
);
907 /* if we're over a track and a region, and in the `object' part of a region,
908 put a selection around the region and drag both
910 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*> (tvp
.first
);
911 if (rtv
&& _join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
) {
912 boost::shared_ptr
<Track
> t
= boost::dynamic_pointer_cast
<Track
> (rtv
->route ());
914 boost::shared_ptr
<Playlist
> pl
= t
->playlist ();
917 boost::shared_ptr
<Region
> r
= pl
->top_region_at (event_frame (event
));
919 RegionView
* rv
= rtv
->view()->find_view (r
);
920 clicked_selection
= select_range_around_region (rv
);
921 _drags
->add (new SelectionDrag (this, item
, SelectionDrag::SelectionMove
));
922 list
<RegionView
*> rvs
;
924 _drags
->add (new RegionMoveDrag (this, item
, rv
, rvs
, false, false));
925 _drags
->start_grab (event
);
936 case ImageFrameHandleStartItem
:
937 imageframe_start_handle_op(item
, event
) ;
940 case ImageFrameHandleEndItem
:
941 imageframe_end_handle_op(item
, event
) ;
944 case MarkerViewHandleStartItem
:
945 markerview_item_start_handle_op(item
, event
) ;
948 case MarkerViewHandleEndItem
:
949 markerview_item_end_handle_op(item
, event
) ;
953 start_markerview_grab(item
, event
) ;
956 start_imageframe_grab(item
, event
) ;
974 /* start a grab so that if we finish after moving
975 we can tell what happened.
977 _drags
->set (new RegionGainDrag (this, item
), event
, current_canvas_cursor
);
981 _drags
->set (new LineDrag (this, item
), event
);
984 case ControlPointItem
:
985 _drags
->set (new ControlPointDrag (this, item
), event
);
996 case ControlPointItem
:
997 _drags
->set (new ControlPointDrag (this, item
), event
);
1000 case AutomationLineItem
:
1001 _drags
->set (new LineDrag (this, item
), event
);
1005 // XXX need automation mode to identify which
1007 // start_line_grab_from_regionview (item, event);
1017 if (event
->type
== GDK_BUTTON_PRESS
) {
1018 _drags
->set (new MouseZoomDrag (this, item
), event
);
1025 if (internal_editing() && item_type
== NoteItem
) {
1026 /* drag notes if we're in internal edit mode */
1027 _drags
->set (new NoteResizeDrag (this, item
), event
, current_canvas_cursor
);
1029 } else if ((!internal_editing() || dynamic_cast<AudioRegionView
*> (clicked_regionview
)) && clicked_regionview
) {
1030 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
1031 _drags
->set (new TimeFXDrag (this, item
, clicked_regionview
, selection
->regions
.by_layer()), event
);
1037 _drags
->set (new ScrubDrag (this, item
), event
);
1038 scrub_reversals
= 0;
1039 scrub_reverse_distance
= 0;
1040 last_scrub_x
= event
->button
.x
;
1041 scrubbing_direction
= 0;
1042 set_canvas_cursor (_cursors
->transparent
);
1054 Editor::button_press_handler_2 (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
1056 Editing::MouseMode
const eff
= effective_mouse_mode ();
1059 switch (item_type
) {
1061 if (internal_editing ()) {
1062 /* no region drags in internal edit mode */
1066 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::CopyModifier
)) {
1067 add_region_copy_drag (item
, event
, clicked_regionview
);
1069 add_region_drag (item
, event
, clicked_regionview
);
1071 _drags
->start_grab (event
);
1074 case ControlPointItem
:
1075 _drags
->set (new ControlPointDrag (this, item
), event
);
1083 switch (item_type
) {
1084 case RegionViewNameHighlight
:
1085 _drags
->set (new TrimDrag (this, item
, clicked_regionview
, selection
->regions
.by_layer()), event
);
1089 case LeftFrameHandle
:
1090 case RightFrameHandle
:
1091 if (!internal_editing ()) {
1092 _drags
->set (new TrimDrag (this, item
, clicked_regionview
, selection
->regions
.by_layer()), event
);
1097 case RegionViewName
:
1098 _drags
->set (new TrimDrag (this, clicked_regionview
->get_name_highlight(), clicked_regionview
, selection
->regions
.by_layer()), event
);
1109 /* relax till release */
1115 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
1116 temporal_zoom_to_frame (false, event_frame (event
));
1118 temporal_zoom_to_frame (true, event_frame(event
));
1131 Editor::button_press_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
1133 if (event
->type
!= GDK_BUTTON_PRESS
) {
1137 Glib::RefPtr
<Gdk::Window
> canvas_window
= const_cast<Editor
*>(this)->track_canvas
->get_window();
1139 if (canvas_window
) {
1140 Glib::RefPtr
<const Gdk::Window
> pointer_window
;
1143 Gdk::ModifierType mask
;
1145 pointer_window
= canvas_window
->get_pointer (x
, y
, mask
);
1147 if (pointer_window
== track_canvas
->get_bin_window()) {
1148 track_canvas
->window_to_world (x
, y
, wx
, wy
);
1152 pre_press_cursor
= current_canvas_cursor
;
1154 track_canvas
->grab_focus();
1156 if (_session
&& _session
->actively_recording()) {
1160 button_selection (item
, event
, item_type
);
1162 if (!_drags
->active () &&
1163 (Keyboard::is_delete_event (&event
->button
) ||
1164 Keyboard::is_context_menu_event (&event
->button
) ||
1165 Keyboard::is_edit_event (&event
->button
))) {
1167 /* handled by button release */
1171 switch (event
->button
.button
) {
1173 return button_press_handler_1 (item
, event
, item_type
);
1177 return button_press_handler_2 (item
, event
, item_type
);
1184 return button_press_dispatch (&event
->button
);
1193 Editor::button_press_dispatch (GdkEventButton
* ev
)
1195 /* this function is intended only for buttons 4 and above.
1198 Gtkmm2ext::MouseButton
b (ev
->state
, ev
->button
);
1199 return button_bindings
->activate (b
, Gtkmm2ext::Bindings::Press
);
1203 Editor::button_release_dispatch (GdkEventButton
* ev
)
1205 /* this function is intended only for buttons 4 and above.
1208 Gtkmm2ext::MouseButton
b (ev
->state
, ev
->button
);
1209 return button_bindings
->activate (b
, Gtkmm2ext::Bindings::Release
);
1213 Editor::button_release_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
1215 framepos_t where
= event_frame (event
, 0, 0);
1216 AutomationTimeAxisView
* atv
= 0;
1218 if (pre_press_cursor
) {
1219 set_canvas_cursor (pre_press_cursor
);
1220 pre_press_cursor
= 0;
1223 /* no action if we're recording */
1225 if (_session
&& _session
->actively_recording()) {
1229 /* see if we're finishing a drag */
1231 bool were_dragging
= false;
1232 if (_drags
->active ()) {
1233 bool const r
= _drags
->end_grab (event
);
1235 /* grab dragged, so do nothing else */
1239 were_dragging
= true;
1242 update_region_layering_order_editor ();
1244 /* edit events get handled here */
1246 if (!_drags
->active () && Keyboard::is_edit_event (&event
->button
)) {
1247 switch (item_type
) {
1249 show_region_properties ();
1252 case TempoMarkerItem
:
1253 edit_tempo_marker (item
);
1256 case MeterMarkerItem
:
1257 edit_meter_marker (item
);
1260 case RegionViewName
:
1261 if (clicked_regionview
->name_active()) {
1262 return mouse_rename_region (item
, event
);
1266 case ControlPointItem
:
1267 edit_control_point (item
);
1280 /* context menu events get handled here */
1282 if (Keyboard::is_context_menu_event (&event
->button
)) {
1284 if (!_drags
->active ()) {
1286 /* no matter which button pops up the context menu, tell the menu
1287 widget to use button 1 to drive menu selection.
1290 switch (item_type
) {
1292 case FadeInHandleItem
:
1294 case FadeOutHandleItem
:
1295 popup_fade_context_menu (1, event
->button
.time
, item
, item_type
);
1299 popup_track_context_menu (1, event
->button
.time
, item_type
, false);
1303 case RegionViewNameHighlight
:
1304 case LeftFrameHandle
:
1305 case RightFrameHandle
:
1306 case RegionViewName
:
1307 popup_track_context_menu (1, event
->button
.time
, item_type
, false);
1311 popup_track_context_menu (1, event
->button
.time
, item_type
, true);
1314 case AutomationTrackItem
:
1315 popup_track_context_menu (1, event
->button
.time
, item_type
, false);
1319 case RangeMarkerBarItem
:
1320 case TransportMarkerBarItem
:
1321 case CdMarkerBarItem
:
1324 popup_ruler_menu (where
, item_type
);
1328 marker_context_menu (&event
->button
, item
);
1331 case TempoMarkerItem
:
1332 tempo_or_meter_marker_context_menu (&event
->button
, item
);
1335 case MeterMarkerItem
:
1336 tempo_or_meter_marker_context_menu (&event
->button
, item
);
1339 case CrossfadeViewItem
:
1340 popup_track_context_menu (1, event
->button
.time
, item_type
, false);
1343 case ControlPointItem
:
1344 popup_control_point_context_menu (item
, event
);
1348 case ImageFrameItem
:
1349 popup_imageframe_edit_menu(1, event
->button
.time
, item
, true) ;
1351 case ImageFrameTimeAxisItem
:
1352 popup_imageframe_edit_menu(1, event
->button
.time
, item
, false) ;
1354 case MarkerViewItem
:
1355 popup_marker_time_axis_edit_menu(1, event
->button
.time
, item
, true) ;
1357 case MarkerTimeAxisItem
:
1358 popup_marker_time_axis_edit_menu(1, event
->button
.time
, item
, false) ;
1370 /* delete events get handled here */
1372 Editing::MouseMode
const eff
= effective_mouse_mode ();
1374 if (!_drags
->active () && Keyboard::is_delete_event (&event
->button
)) {
1376 switch (item_type
) {
1377 case TempoMarkerItem
:
1378 remove_tempo_marker (item
);
1381 case MeterMarkerItem
:
1382 remove_meter_marker (item
);
1386 remove_marker (*item
, event
);
1390 if (eff
== MouseObject
) {
1391 remove_clicked_region ();
1395 case ControlPointItem
:
1396 remove_control_point (item
);
1400 remove_midi_note (item
, event
);
1409 switch (event
->button
.button
) {
1412 switch (item_type
) {
1413 /* see comments in button_press_handler */
1414 case PlayheadCursorItem
:
1417 case AutomationLineItem
:
1418 case StartSelectionTrimItem
:
1419 case EndSelectionTrimItem
:
1423 if (!_dragging_playhead
) {
1424 snap_to_with_modifier (where
, event
, 0, true);
1425 mouse_add_new_marker (where
);
1429 case CdMarkerBarItem
:
1430 if (!_dragging_playhead
) {
1431 // if we get here then a dragged range wasn't done
1432 snap_to_with_modifier (where
, event
, 0, true);
1433 mouse_add_new_marker (where
, true);
1438 if (!_dragging_playhead
) {
1439 snap_to_with_modifier (where
, event
);
1440 mouse_add_new_tempo_event (where
);
1445 if (!_dragging_playhead
) {
1446 mouse_add_new_meter_event (pixel_to_frame (event
->button
.x
));
1457 switch (item_type
) {
1458 case AutomationTrackItem
:
1459 atv
= dynamic_cast<AutomationTimeAxisView
*>(clicked_axisview
);
1461 atv
->add_automation_event (item
, event
, where
, event
->button
.y
);
1472 switch (item_type
) {
1475 /* check that we didn't drag before releasing, since
1476 its really annoying to create new control
1477 points when doing this.
1479 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*> (clicked_regionview
);
1480 if (were_dragging
&& arv
) {
1481 arv
->add_gain_point_event (item
, event
);
1487 case AutomationTrackItem
:
1488 dynamic_cast<AutomationTimeAxisView
*>(clicked_axisview
)->
1489 add_automation_event (item
, event
, where
, event
->button
.y
);
1498 set_canvas_cursor (current_canvas_cursor
);
1499 if (scrubbing_direction
== 0) {
1500 /* no drag, just a click */
1501 switch (item_type
) {
1503 play_selected_region ();
1509 /* make sure we stop */
1510 _session
->request_transport_speed (0.0);
1519 /* do any (de)selection operations that should occur on button release */
1520 button_selection (item
, event
, item_type
);
1529 switch (item_type
) {
1531 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
1533 } else if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::ModifierMask (Keyboard::TertiaryModifier
|Keyboard::SecondaryModifier
))) {
1536 // Button2 click is unused
1549 // x_style_paste (where, 1.0);
1570 Editor::enter_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
1577 last_item_entered
= item
;
1579 switch (item_type
) {
1580 case ControlPointItem
:
1581 if (mouse_mode
== MouseGain
|| mouse_mode
== MouseObject
) {
1582 cp
= static_cast<ControlPoint
*>(item
->get_data ("control_point"));
1583 cp
->set_visible (true);
1587 at_y
= cp
->get_y ();
1588 cp
->i2w (at_x
, at_y
);
1592 fraction
= 1.0 - (cp
->get_y() / cp
->line().height());
1594 if (is_drawable() && !_drags
->active ()) {
1595 set_canvas_cursor (_cursors
->fader
);
1598 _verbose_cursor
->set (cp
->line().get_verbose_cursor_string (fraction
), at_x
, at_y
);
1599 _verbose_cursor
->show ();
1604 if (mouse_mode
== MouseGain
) {
1605 ArdourCanvas::Line
*line
= dynamic_cast<ArdourCanvas::Line
*> (item
);
1607 line
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine
.get();
1608 if (is_drawable()) {
1609 set_canvas_cursor (_cursors
->fader
);
1614 case AutomationLineItem
:
1615 if (mouse_mode
== MouseGain
|| mouse_mode
== MouseObject
) {
1617 ArdourCanvas::Line
*line
= dynamic_cast<ArdourCanvas::Line
*> (item
);
1619 line
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine
.get();
1621 if (is_drawable()) {
1622 set_canvas_cursor (_cursors
->fader
);
1627 case RegionViewNameHighlight
:
1628 if (is_drawable() && mouse_mode
== MouseObject
&& entered_regionview
) {
1629 set_canvas_cursor_for_region_view (event
->crossing
.x
, entered_regionview
);
1630 _over_region_trim_target
= true;
1634 case LeftFrameHandle
:
1635 case RightFrameHandle
:
1636 if (is_drawable() && mouse_mode
== MouseObject
&& !internal_editing() && entered_regionview
) {
1637 set_canvas_cursor_for_region_view (event
->crossing
.x
, entered_regionview
);
1641 case StartSelectionTrimItem
:
1642 case EndSelectionTrimItem
:
1645 case ImageFrameHandleStartItem
:
1646 case ImageFrameHandleEndItem
:
1647 case MarkerViewHandleStartItem
:
1648 case MarkerViewHandleEndItem
:
1651 if (is_drawable()) {
1652 set_canvas_cursor (_cursors
->trimmer
);
1656 case PlayheadCursorItem
:
1657 if (is_drawable()) {
1658 switch (_edit_point
) {
1660 set_canvas_cursor (_cursors
->grabber_edit_point
);
1663 set_canvas_cursor (_cursors
->grabber
);
1669 case RegionViewName
:
1671 /* when the name is not an active item, the entire name highlight is for trimming */
1673 if (!reinterpret_cast<RegionView
*> (item
->get_data ("regionview"))->name_active()) {
1674 if (mouse_mode
== MouseObject
&& is_drawable()) {
1675 set_canvas_cursor_for_region_view (event
->crossing
.x
, entered_regionview
);
1676 _over_region_trim_target
= true;
1682 case AutomationTrackItem
:
1683 if (is_drawable()) {
1684 Gdk::Cursor
*cursor
;
1685 switch (mouse_mode
) {
1687 cursor
= _cursors
->selector
;
1690 cursor
= _cursors
->zoom_in
;
1693 cursor
= _cursors
->cross_hair
;
1697 set_canvas_cursor (cursor
);
1699 AutomationTimeAxisView
* atv
;
1700 if ((atv
= static_cast<AutomationTimeAxisView
*>(item
->get_data ("trackview"))) != 0) {
1701 clear_entered_track
= false;
1702 set_entered_track (atv
);
1708 case RangeMarkerBarItem
:
1709 case TransportMarkerBarItem
:
1710 case CdMarkerBarItem
:
1713 if (is_drawable()) {
1714 set_canvas_cursor (_cursors
->timebar
);
1719 if ((marker
= static_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
1722 entered_marker
= marker
;
1723 marker
->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker
.get());
1725 case MeterMarkerItem
:
1726 case TempoMarkerItem
:
1727 if (is_drawable()) {
1728 set_canvas_cursor (_cursors
->timebar
);
1732 case FadeInHandleItem
:
1733 if (mouse_mode
== MouseObject
&& !internal_editing()) {
1734 ArdourCanvas::SimpleRect
*rect
= dynamic_cast<ArdourCanvas::SimpleRect
*> (item
);
1736 rect
->property_fill_color_rgba() = 0xBBBBBBAA;
1738 set_canvas_cursor (_cursors
->fade_in
);
1742 case FadeOutHandleItem
:
1743 if (mouse_mode
== MouseObject
&& !internal_editing()) {
1744 ArdourCanvas::SimpleRect
*rect
= dynamic_cast<ArdourCanvas::SimpleRect
*> (item
);
1746 rect
->property_fill_color_rgba() = 0xBBBBBBAA;
1748 set_canvas_cursor (_cursors
->fade_out
);
1751 case FeatureLineItem
:
1753 ArdourCanvas::Line
*line
= dynamic_cast<ArdourCanvas::Line
*> (item
);
1754 line
->property_fill_color_rgba() = 0xFF0000FF;
1758 if (join_object_range_button
.get_active()) {
1759 set_canvas_cursor ();
1767 /* second pass to handle entered track status in a comprehensible way.
1770 switch (item_type
) {
1772 case AutomationLineItem
:
1773 case ControlPointItem
:
1774 /* these do not affect the current entered track state */
1775 clear_entered_track
= false;
1778 case AutomationTrackItem
:
1779 /* handled above already */
1783 set_entered_track (0);
1791 Editor::leave_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
1801 switch (item_type
) {
1802 case ControlPointItem
:
1803 cp
= reinterpret_cast<ControlPoint
*>(item
->get_data ("control_point"));
1804 if (cp
->line().the_list()->interpolation() != AutomationList::Discrete
) {
1805 if (cp
->line().npoints() > 1 && !cp
->get_selected()) {
1806 cp
->set_visible (false);
1810 if (is_drawable()) {
1811 set_canvas_cursor (current_canvas_cursor
);
1814 _verbose_cursor
->hide ();
1817 case RegionViewNameHighlight
:
1818 case LeftFrameHandle
:
1819 case RightFrameHandle
:
1820 case StartSelectionTrimItem
:
1821 case EndSelectionTrimItem
:
1822 case PlayheadCursorItem
:
1825 case ImageFrameHandleStartItem
:
1826 case ImageFrameHandleEndItem
:
1827 case MarkerViewHandleStartItem
:
1828 case MarkerViewHandleEndItem
:
1831 _over_region_trim_target
= false;
1833 if (is_drawable()) {
1834 set_canvas_cursor (current_canvas_cursor
);
1839 case AutomationLineItem
:
1840 al
= reinterpret_cast<AutomationLine
*> (item
->get_data ("line"));
1842 ArdourCanvas::Line
*line
= dynamic_cast<ArdourCanvas::Line
*> (item
);
1844 line
->property_fill_color_rgba() = al
->get_line_color();
1846 if (is_drawable()) {
1847 set_canvas_cursor (current_canvas_cursor
);
1851 case RegionViewName
:
1852 /* see enter_handler() for notes */
1853 _over_region_trim_target
= false;
1855 if (!reinterpret_cast<RegionView
*> (item
->get_data ("regionview"))->name_active()) {
1856 if (is_drawable() && mouse_mode
== MouseObject
) {
1857 set_canvas_cursor (current_canvas_cursor
);
1862 case RangeMarkerBarItem
:
1863 case TransportMarkerBarItem
:
1864 case CdMarkerBarItem
:
1868 if (is_drawable()) {
1869 set_canvas_cursor (current_canvas_cursor
);
1874 if ((marker
= static_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
1878 if ((loc
= find_location_from_marker (marker
, is_start
)) != 0) {
1879 location_flags_changed (loc
, this);
1882 case MeterMarkerItem
:
1883 case TempoMarkerItem
:
1885 if (is_drawable()) {
1886 set_canvas_cursor (_cursors
->timebar
);
1891 case FadeInHandleItem
:
1892 case FadeOutHandleItem
:
1893 rv
= static_cast<RegionView
*>(item
->get_data ("regionview"));
1895 ArdourCanvas::SimpleRect
*rect
= dynamic_cast<ArdourCanvas::SimpleRect
*> (item
);
1897 rect
->property_fill_color_rgba() = rv
->get_fill_color();
1898 rect
->property_outline_pixels() = 0;
1901 set_canvas_cursor (current_canvas_cursor
);
1904 case AutomationTrackItem
:
1905 if (is_drawable()) {
1906 set_canvas_cursor (current_canvas_cursor
);
1907 clear_entered_track
= true;
1908 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track
));
1911 case FeatureLineItem
:
1913 ArdourCanvas::Line
*line
= dynamic_cast<ArdourCanvas::Line
*> (item
);
1914 line
->property_fill_color_rgba() = (guint
) ARDOUR_UI::config()->canvasvar_ZeroLine
.get();;
1926 Editor::left_automation_track ()
1928 if (clear_entered_track
) {
1929 set_entered_track (0);
1930 clear_entered_track
= false;
1936 Editor::scrub (framepos_t frame
, double current_x
)
1940 if (scrubbing_direction
== 0) {
1942 _session
->request_locate (frame
, false);
1943 _session
->request_transport_speed (0.1);
1944 scrubbing_direction
= 1;
1948 if (last_scrub_x
> current_x
) {
1950 /* pointer moved to the left */
1952 if (scrubbing_direction
> 0) {
1954 /* we reversed direction to go backwards */
1957 scrub_reverse_distance
+= (int) (last_scrub_x
- current_x
);
1961 /* still moving to the left (backwards) */
1963 scrub_reversals
= 0;
1964 scrub_reverse_distance
= 0;
1966 delta
= 0.01 * (last_scrub_x
- current_x
);
1967 _session
->request_transport_speed_nonzero (_session
->transport_speed() - delta
);
1971 /* pointer moved to the right */
1973 if (scrubbing_direction
< 0) {
1974 /* we reversed direction to go forward */
1977 scrub_reverse_distance
+= (int) (current_x
- last_scrub_x
);
1980 /* still moving to the right */
1982 scrub_reversals
= 0;
1983 scrub_reverse_distance
= 0;
1985 delta
= 0.01 * (current_x
- last_scrub_x
);
1986 _session
->request_transport_speed_nonzero (_session
->transport_speed() + delta
);
1990 /* if there have been more than 2 opposite motion moves detected, or one that moves
1991 back more than 10 pixels, reverse direction
1994 if (scrub_reversals
>= 2 || scrub_reverse_distance
> 10) {
1996 if (scrubbing_direction
> 0) {
1997 /* was forwards, go backwards */
1998 _session
->request_transport_speed (-0.1);
1999 scrubbing_direction
= -1;
2001 /* was backwards, go forwards */
2002 _session
->request_transport_speed (0.1);
2003 scrubbing_direction
= 1;
2006 scrub_reverse_distance
= 0;
2007 scrub_reversals
= 0;
2011 last_scrub_x
= current_x
;
2015 Editor::motion_handler (ArdourCanvas::Item
* /*item*/, GdkEvent
* event
, bool from_autoscroll
)
2017 _last_motion_y
= event
->motion
.y
;
2019 if (event
->motion
.is_hint
) {
2022 /* We call this so that MOTION_NOTIFY events continue to be
2023 delivered to the canvas. We need to do this because we set
2024 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2025 the density of the events, at the expense of a round-trip
2026 to the server. Given that this will mostly occur on cases
2027 where DISPLAY = :0.0, and given the cost of what the motion
2028 event might do, its a good tradeoff.
2031 track_canvas
->get_pointer (x
, y
);
2034 if (current_stepping_trackview
) {
2035 /* don't keep the persistent stepped trackview if the mouse moves */
2036 current_stepping_trackview
= 0;
2037 step_timeout
.disconnect ();
2040 if (_session
&& _session
->actively_recording()) {
2041 /* Sorry. no dragging stuff around while we record */
2045 JoinObjectRangeState
const old
= _join_object_range_state
;
2046 update_join_object_range_location (event
->motion
.x
, event
->motion
.y
);
2047 if (_join_object_range_state
!= old
) {
2048 set_canvas_cursor ();
2051 if (_over_region_trim_target
) {
2052 set_canvas_cursor_for_region_view (event
->motion
.x
, entered_regionview
);
2055 bool handled
= false;
2056 if (_drags
->active ()) {
2057 handled
= _drags
->motion_handler (event
, from_autoscroll
);
2064 track_canvas_motion (event
);
2069 Editor::can_remove_control_point (ArdourCanvas::Item
* item
)
2071 ControlPoint
* control_point
;
2073 if ((control_point
= reinterpret_cast<ControlPoint
*> (item
->get_data ("control_point"))) == 0) {
2074 fatal
<< _("programming error: control point canvas item has no control point object pointer!") << endmsg
;
2078 AutomationLine
& line
= control_point
->line ();
2079 if (dynamic_cast<AudioRegionGainLine
*> (&line
)) {
2080 /* we shouldn't remove the first or last gain point in region gain lines */
2081 if (line
.is_last_point(*control_point
) || line
.is_first_point(*control_point
)) {
2090 Editor::remove_control_point (ArdourCanvas::Item
* item
)
2092 if (!can_remove_control_point (item
)) {
2096 ControlPoint
* control_point
;
2098 if ((control_point
= reinterpret_cast<ControlPoint
*> (item
->get_data ("control_point"))) == 0) {
2099 fatal
<< _("programming error: control point canvas item has no control point object pointer!") << endmsg
;
2103 control_point
->line().remove_point (*control_point
);
2107 Editor::edit_control_point (ArdourCanvas::Item
* item
)
2109 ControlPoint
* p
= reinterpret_cast<ControlPoint
*> (item
->get_data ("control_point"));
2112 fatal
<< _("programming error: control point canvas item has no control point object pointer!") << endmsg
;
2116 ControlPointDialog
d (p
);
2117 d
.set_position (Gtk::WIN_POS_MOUSE
);
2120 if (d
.run () != RESPONSE_ACCEPT
) {
2124 p
->line().modify_point_y (*p
, d
.get_y_fraction ());
2128 Editor::edit_note (ArdourCanvas::Item
* item
)
2130 ArdourCanvas::CanvasNoteEvent
* e
= dynamic_cast<ArdourCanvas::CanvasNoteEvent
*> (item
);
2133 EditNoteDialog
d (&e
->region_view(), e
);
2134 d
.set_position (Gtk::WIN_POS_MOUSE
);
2142 Editor::visible_order_range (int* low
, int* high
) const
2144 *low
= TimeAxisView::max_order ();
2147 for (TrackViewList::const_iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
2149 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*> (*i
);
2151 if (!rtv
->hidden()) {
2153 if (*high
< rtv
->order()) {
2154 *high
= rtv
->order ();
2157 if (*low
> rtv
->order()) {
2158 *low
= rtv
->order ();
2165 Editor::region_view_item_click (AudioRegionView
& rv
, GdkEventButton
* event
)
2167 /* Either add to or set the set the region selection, unless
2168 this is an alignment click (control used)
2171 if (Keyboard::modifier_state_contains (event
->state
, Keyboard::PrimaryModifier
)) {
2172 TimeAxisView
* tv
= &rv
.get_time_axis_view();
2173 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*>(tv
);
2175 if (rtv
&& rtv
->is_track()) {
2176 speed
= rtv
->track()->speed();
2179 framepos_t where
= get_preferred_edit_position();
2183 if (Keyboard::modifier_state_equals (event
->state
, Keyboard::ModifierMask (Keyboard::PrimaryModifier
|Keyboard::SecondaryModifier
))) {
2185 align_region (rv
.region(), SyncPoint
, (framepos_t
) (where
* speed
));
2187 } else if (Keyboard::modifier_state_equals (event
->state
, Keyboard::ModifierMask (Keyboard::PrimaryModifier
|Keyboard::TertiaryModifier
))) {
2189 align_region (rv
.region(), End
, (framepos_t
) (where
* speed
));
2193 align_region (rv
.region(), Start
, (framepos_t
) (where
* speed
));
2200 Editor::collect_new_region_view (RegionView
* rv
)
2202 latest_regionviews
.push_back (rv
);
2206 Editor::collect_and_select_new_region_view (RegionView
* rv
)
2209 latest_regionviews
.push_back (rv
);
2213 Editor::cancel_selection ()
2215 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
2216 (*i
)->hide_selection ();
2219 selection
->clear ();
2220 clicked_selection
= 0;
2225 Editor::point_trim (GdkEvent
* event
, framepos_t new_bound
)
2227 RegionView
* rv
= clicked_regionview
;
2229 /* Choose action dependant on which button was pressed */
2230 switch (event
->button
.button
) {
2232 begin_reversible_command (_("start point trim"));
2234 if (selection
->selected (rv
)) {
2235 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin();
2236 i
!= selection
->regions
.by_layer().end(); ++i
)
2239 cerr
<< "region view contains null region" << endl
;
2242 if (!(*i
)->region()->locked()) {
2243 (*i
)->region()->clear_changes ();
2244 (*i
)->region()->trim_front (new_bound
);
2245 _session
->add_command(new StatefulDiffCommand ((*i
)->region()));
2250 if (!rv
->region()->locked()) {
2251 rv
->region()->clear_changes ();
2252 rv
->region()->trim_front (new_bound
);
2253 _session
->add_command(new StatefulDiffCommand (rv
->region()));
2257 commit_reversible_command();
2261 begin_reversible_command (_("End point trim"));
2263 if (selection
->selected (rv
)) {
2265 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
)
2267 if (!(*i
)->region()->locked()) {
2268 (*i
)->region()->clear_changes();
2269 (*i
)->region()->trim_end (new_bound
);
2270 _session
->add_command(new StatefulDiffCommand ((*i
)->region()));
2276 if (!rv
->region()->locked()) {
2277 rv
->region()->clear_changes ();
2278 rv
->region()->trim_end (new_bound
);
2279 _session
->add_command (new StatefulDiffCommand (rv
->region()));
2283 commit_reversible_command();
2292 Editor::hide_marker (ArdourCanvas::Item
* item
, GdkEvent
* /*event*/)
2297 if ((marker
= static_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
2298 fatal
<< _("programming error: marker canvas item has no marker object pointer!") << endmsg
;
2302 Location
* location
= find_location_from_marker (marker
, is_start
);
2303 location
->set_hidden (true, this);
2308 Editor::reposition_zoom_rect (framepos_t start
, framepos_t end
)
2310 double x1
= frame_to_pixel (start
);
2311 double x2
= frame_to_pixel (end
);
2312 double y2
= full_canvas_height
- 1.0;
2314 zoom_rect
->property_x1() = x1
;
2315 zoom_rect
->property_y1() = 1.0;
2316 zoom_rect
->property_x2() = x2
;
2317 zoom_rect
->property_y2() = y2
;
2322 Editor::mouse_rename_region (ArdourCanvas::Item
* /*item*/, GdkEvent
* /*event*/)
2324 using namespace Gtkmm2ext
;
2326 ArdourPrompter
prompter (false);
2328 prompter
.set_prompt (_("Name for region:"));
2329 prompter
.set_initial_text (clicked_regionview
->region()->name());
2330 prompter
.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT
);
2331 prompter
.set_response_sensitive (Gtk::RESPONSE_ACCEPT
, false);
2332 prompter
.show_all ();
2333 switch (prompter
.run ()) {
2334 case Gtk::RESPONSE_ACCEPT
:
2336 prompter
.get_result(str
);
2338 clicked_regionview
->region()->set_name (str
);
2347 Editor::mouse_brush_insert_region (RegionView
* rv
, framepos_t pos
)
2349 /* no brushing without a useful snap setting */
2351 switch (_snap_mode
) {
2353 return; /* can't work because it allows region to be placed anywhere */
2358 switch (_snap_type
) {
2366 /* don't brush a copy over the original */
2368 if (pos
== rv
->region()->position()) {
2372 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*>(&rv
->get_time_axis_view());
2374 if (rtv
== 0 || !rtv
->is_track()) {
2378 boost::shared_ptr
<Playlist
> playlist
= rtv
->playlist();
2379 double speed
= rtv
->track()->speed();
2381 playlist
->clear_changes ();
2382 boost::shared_ptr
<Region
> new_region (RegionFactory::create (rv
->region(), true));
2383 playlist
->add_region (new_region
, (framepos_t
) (pos
* speed
));
2384 _session
->add_command (new StatefulDiffCommand (playlist
));
2386 // playlist is frozen, so we have to update manually XXX this is disgusting
2388 playlist
->RegionAdded (new_region
); /* EMIT SIGNAL */
2392 Editor::track_height_step_timeout ()
2394 if (get_microseconds() - last_track_height_step_timestamp
< 250000) {
2395 current_stepping_trackview
= 0;
2402 Editor::add_region_drag (ArdourCanvas::Item
* item
, GdkEvent
* event
, RegionView
* region_view
)
2404 assert (region_view
);
2406 if (!region_view
->region()->playlist()) {
2410 _region_motion_group
->raise_to_top ();
2412 if (Config
->get_edit_mode() == Splice
) {
2413 _drags
->add (new RegionSpliceDrag (this, item
, region_view
, selection
->regions
.by_layer()));
2415 RegionSelection s
= get_equivalent_regions (selection
->regions
, ARDOUR::Properties::edit
.property_id
);
2416 _drags
->add (new RegionMoveDrag (this, item
, region_view
, s
.by_layer(), false, false));
2419 /* sync the canvas to what we think is its current state */
2420 update_canvas_now();
2424 Editor::add_region_copy_drag (ArdourCanvas::Item
* item
, GdkEvent
* event
, RegionView
* region_view
)
2426 assert (region_view
);
2428 if (!region_view
->region()->playlist()) {
2432 _region_motion_group
->raise_to_top ();
2434 RegionSelection s
= get_equivalent_regions (selection
->regions
, ARDOUR::Properties::edit
.property_id
);
2435 _drags
->add (new RegionMoveDrag (this, item
, region_view
, s
.by_layer(), false, true));
2439 Editor::add_region_brush_drag (ArdourCanvas::Item
* item
, GdkEvent
* event
, RegionView
* region_view
)
2441 assert (region_view
);
2443 if (!region_view
->region()->playlist()) {
2447 if (Config
->get_edit_mode() == Splice
) {
2451 RegionSelection s
= get_equivalent_regions (selection
->regions
, ARDOUR::Properties::edit
.property_id
);
2452 _drags
->add (new RegionMoveDrag (this, item
, region_view
, s
.by_layer(), true, false));
2454 begin_reversible_command (Operations::drag_region_brush
);
2457 /** Start a grab where a time range is selected, track(s) are selected, and the
2458 * user clicks and drags a region with a modifier in order to create a new region containing
2459 * the section of the clicked region that lies within the time range.
2462 Editor::start_selection_grab (ArdourCanvas::Item
* /*item*/, GdkEvent
* event
)
2464 if (clicked_regionview
== 0) {
2468 /* lets try to create new Region for the selection */
2470 vector
<boost::shared_ptr
<Region
> > new_regions
;
2471 create_region_from_selection (new_regions
);
2473 if (new_regions
.empty()) {
2477 /* XXX fix me one day to use all new regions */
2479 boost::shared_ptr
<Region
> region (new_regions
.front());
2481 /* add it to the current stream/playlist.
2483 tricky: the streamview for the track will add a new regionview. we will
2484 catch the signal it sends when it creates the regionview to
2485 set the regionview we want to then drag.
2488 latest_regionviews
.clear();
2489 sigc::connection c
= clicked_routeview
->view()->RegionViewAdded
.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view
));
2491 /* A selection grab currently creates two undo/redo operations, one for
2492 creating the new region and another for moving it.
2495 begin_reversible_command (Operations::selection_grab
);
2497 boost::shared_ptr
<Playlist
> playlist
= clicked_axisview
->playlist();
2499 playlist
->clear_changes ();
2500 clicked_routeview
->playlist()->add_region (region
, selection
->time
[clicked_selection
].start
);
2501 _session
->add_command(new StatefulDiffCommand (playlist
));
2503 commit_reversible_command ();
2507 if (latest_regionviews
.empty()) {
2508 /* something went wrong */
2512 /* we need to deselect all other regionviews, and select this one
2513 i'm ignoring undo stuff, because the region creation will take care of it
2515 selection
->set (latest_regionviews
);
2517 _drags
->set (new RegionMoveDrag (this, latest_regionviews
.front()->get_canvas_group(), latest_regionviews
.front(), latest_regionviews
, false, false), event
);
2523 if (_drags
->active ()) {
2526 selection
->clear ();
2531 Editor::set_internal_edit (bool yn
)
2533 _internal_editing
= yn
;
2536 mouse_select_button
.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2537 mouse_select_button
.get_image ()->show ();
2538 ARDOUR_UI::instance()->set_tip (mouse_select_button
, _("Draw/Edit MIDI Notes"));
2539 mouse_mode_toggled (mouse_mode
);
2541 pre_internal_mouse_mode
= mouse_mode
;
2543 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
2544 (*i
)->enter_internal_edit_mode ();
2549 mouse_select_button
.set_image (*(manage (new Image (::get_icon("tool_range")))));
2550 mouse_select_button
.get_image ()->show ();
2551 ARDOUR_UI::instance()->set_tip (mouse_select_button
, _("Select/Move Ranges"));
2552 mouse_mode_toggled (mouse_mode
); // sets cursor
2554 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
2555 (*i
)->leave_internal_edit_mode ();
2558 if (mouse_mode
== MouseRange
&& pre_internal_mouse_mode
!= MouseRange
) {
2559 /* we were drawing .. flip back to something sensible */
2560 set_mouse_mode (pre_internal_mouse_mode
);
2565 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2566 * used by the `join object/range' tool mode.
2569 Editor::update_join_object_range_location (double x
, double y
)
2571 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2572 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2573 that we're over requires searching the playlist.
2576 if (join_object_range_button
.get_active() == false || (mouse_mode
!= MouseRange
&& mouse_mode
!= MouseObject
)) {
2577 _join_object_range_state
= JOIN_OBJECT_RANGE_NONE
;
2581 if (mouse_mode
== MouseObject
) {
2582 _join_object_range_state
= JOIN_OBJECT_RANGE_OBJECT
;
2583 } else if (mouse_mode
== MouseRange
) {
2584 _join_object_range_state
= JOIN_OBJECT_RANGE_RANGE
;
2587 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2588 pair
<TimeAxisView
*, int> tvp
= trackview_by_y_position (y
+ vertical_adjustment
.get_value() - canvas_timebars_vsize
);
2592 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*> (tvp
.first
);
2597 rtv
->canvas_display()->w2i (cx
, cy
);
2599 double const c
= cy
/ rtv
->view()->child_height();
2601 double const f
= modf (c
, &d
);
2603 _join_object_range_state
= f
< 0.5 ? JOIN_OBJECT_RANGE_RANGE
: JOIN_OBJECT_RANGE_OBJECT
;
2609 Editor::effective_mouse_mode () const
2611 if (_join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
) {
2613 } else if (_join_object_range_state
== JOIN_OBJECT_RANGE_RANGE
) {
2621 Editor::remove_midi_note (ArdourCanvas::Item
* item
, GdkEvent
*)
2623 ArdourCanvas::CanvasNoteEvent
* e
= dynamic_cast<ArdourCanvas::CanvasNoteEvent
*> (item
);
2626 e
->region_view().delete_note (e
->note ());
2630 Editor::set_canvas_cursor_for_region_view (double x
, RegionView
* rv
)
2634 ArdourCanvas::Group
* g
= rv
->get_canvas_group ();
2635 ArdourCanvas::Group
* p
= g
->get_parent_group ();
2637 /* Compute x in region view parent coordinates */
2641 double x1
, x2
, y1
, y2
;
2642 g
->get_bounds (x1
, y1
, x2
, y2
);
2644 /* Halfway across the region */
2645 double const h
= (x1
+ x2
) / 2;
2647 Trimmable::CanTrim ct
= rv
->region()->can_trim ();
2649 if (ct
& Trimmable::FrontTrimEarlier
) {
2650 set_canvas_cursor (_cursors
->left_side_trim
);
2652 set_canvas_cursor (_cursors
->left_side_trim_right_only
);
2655 if (ct
& Trimmable::EndTrimLater
) {
2656 set_canvas_cursor (_cursors
->right_side_trim
);
2658 set_canvas_cursor (_cursors
->right_side_trim_left_only
);
2663 /** Obtain the pointer position in world coordinates */
2665 Editor::get_pointer_position (double& x
, double& y
) const
2668 track_canvas
->get_pointer (px
, py
);
2669 track_canvas
->window_to_world (px
, py
, x
, y
);