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 (RegionSelection::iterator i
= selection
->regions
.begin(); i
!= selection
->regions
.end(); ++i
) {
382 (*i
)->set_should_show_selection (true);
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
);
509 // begin_reversible_command (_("select on click"));
513 if (mouse_mode
!= MouseRange
|| _join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
) {
514 set_selected_regionview_from_click (press
, op
, true);
515 } else if (event
->type
== GDK_BUTTON_PRESS
) {
516 selection
->clear_tracks ();
517 set_selected_track_as_side_effect (op
, true);
519 if (_join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
&& !selection
->regions
.empty()) {
520 clicked_selection
= select_range_around_region (selection
->regions
.front());
524 case RegionViewNameHighlight
:
526 case LeftFrameHandle
:
527 case RightFrameHandle
:
528 if (mouse_mode
!= MouseRange
|| _join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
) {
529 set_selected_regionview_from_click (press
, op
, true);
530 } else if (event
->type
== GDK_BUTTON_PRESS
) {
531 set_selected_track_as_side_effect (op
);
536 case FadeInHandleItem
:
538 case FadeOutHandleItem
:
540 if (mouse_mode
!= MouseRange
|| _join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
) {
541 set_selected_regionview_from_click (press
, op
, true);
542 } else if (event
->type
== GDK_BUTTON_PRESS
) {
543 set_selected_track_as_side_effect (op
);
547 case ControlPointItem
:
548 set_selected_track_as_side_effect (op
, true);
549 if (mouse_mode
!= MouseRange
|| _join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
) {
550 set_selected_control_point_from_click (op
, false);
555 /* for context click, select track */
556 if (event
->button
.button
== 3) {
557 selection
->clear_tracks ();
558 set_selected_track_as_side_effect (op
, true);
562 case AutomationTrackItem
:
563 set_selected_track_as_side_effect (op
, true);
572 Editor::button_press_handler_1 (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
574 /* single mouse clicks on any of these item types operate
575 independent of mouse mode, mostly because they are
576 not on the main track canvas or because we want
581 case PlayheadCursorItem
:
582 _drags
->set (new CursorDrag (this, item
, true), event
);
586 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::ModifierMask(Keyboard::PrimaryModifier
|Keyboard::TertiaryModifier
))) {
587 hide_marker (item
, event
);
589 _drags
->set (new MarkerDrag (this, item
), event
);
593 case TempoMarkerItem
:
595 new TempoMarkerDrag (
598 Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::CopyModifier
)
604 case MeterMarkerItem
:
606 new MeterMarkerDrag (
609 Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::CopyModifier
)
618 if (!Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
619 _drags
->set (new CursorDrag (this, &playhead_cursor
->canvas_item
, false), event
);
625 case RangeMarkerBarItem
:
626 if (!Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
627 _drags
->set (new CursorDrag (this, &playhead_cursor
->canvas_item
, false), event
);
629 _drags
->set (new RangeMarkerBarDrag (this, item
, RangeMarkerBarDrag::CreateRangeMarker
), event
);
634 case CdMarkerBarItem
:
635 if (!Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
636 _drags
->set (new CursorDrag (this, &playhead_cursor
->canvas_item
, false), event
);
638 _drags
->set (new RangeMarkerBarDrag (this, item
, RangeMarkerBarDrag::CreateCDMarker
), event
);
643 case TransportMarkerBarItem
:
644 if (!Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
645 _drags
->set (new CursorDrag (this, &playhead_cursor
->canvas_item
, false), event
);
647 _drags
->set (new RangeMarkerBarDrag (this, item
, RangeMarkerBarDrag::CreateTransportMarker
), event
);
656 if (_join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
) {
657 /* special case: allow trim of range selections in joined object mode;
658 in theory eff should equal MouseRange in this case, but it doesn't
659 because entering the range selection canvas item results in entered_regionview
660 being set to 0, so update_join_object_range_location acts as if we aren't
663 if (item_type
== StartSelectionTrimItem
) {
664 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::SelectionStartTrim
), event
);
665 } else if (item_type
== EndSelectionTrimItem
) {
666 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::SelectionEndTrim
), event
);
670 Editing::MouseMode eff
= effective_mouse_mode ();
672 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
673 if (item_type
== FadeInHandleItem
|| item_type
== FadeOutHandleItem
) {
680 case StartSelectionTrimItem
:
681 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::SelectionStartTrim
), event
);
684 case EndSelectionTrimItem
:
685 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::SelectionEndTrim
), event
);
689 if (Keyboard::modifier_state_contains
690 (event
->button
.state
, Keyboard::ModifierMask(Keyboard::PrimaryModifier
))) {
691 // contains and not equals because I can't use alt as a modifier alone.
692 start_selection_grab (item
, event
);
693 } else if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::SecondaryModifier
)) {
694 /* grab selection for moving */
695 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::SelectionMove
), event
);
697 double const y
= event
->button
.y
+ vertical_adjustment
.get_value() - canvas_timebars_vsize
;
698 pair
<TimeAxisView
*, int> tvp
= trackview_by_y_position (y
);
700 AutomationTimeAxisView
* atv
= dynamic_cast<AutomationTimeAxisView
*> (tvp
.first
);
701 if (join_object_range_button
.get_active() && atv
) {
702 /* smart "join" mode: drag automation */
703 _drags
->set (new AutomationRangeDrag (this, atv
->base_item(), selection
->time
), event
, _cursors
->up_down
);
705 /* this was debated, but decided the more common action was to
706 make a new selection */
707 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::CreateSelection
), event
);
714 if (internal_editing()) {
715 /* trim notes if we're in internal edit mode and near the ends of the note */
716 ArdourCanvas::CanvasNote
* cn
= dynamic_cast<ArdourCanvas::CanvasNote
*> (item
);
717 cerr
<< "NoteItem button press, cursor = " << current_canvas_cursor
<< endl
;
718 if (cn
->mouse_near_ends()) {
719 _drags
->set (new NoteResizeDrag (this, item
), event
, current_canvas_cursor
);
721 _drags
->set (new NoteDrag (this, item
), event
);
727 if (internal_editing()) {
728 if (dynamic_cast<MidiTimeAxisView
*> (clicked_axisview
)) {
729 _drags
->set (new RegionCreateDrag (this, item
, clicked_axisview
), event
);
733 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::CreateSelection
), event
);
738 case RegionViewNameHighlight
:
739 case LeftFrameHandle
:
740 case RightFrameHandle
:
741 if (!clicked_regionview
->region()->locked()) {
742 RegionSelection s
= get_equivalent_regions (selection
->regions
, Properties::edit
.property_id
);
743 _drags
->set (new TrimDrag (this, item
, clicked_regionview
, s
.by_layer()), event
);
749 if (!internal_editing()) {
750 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::CreateSelection
), event
);
759 if (internal_editing()) {
760 ArdourCanvas::CanvasNoteEvent
* cn
= dynamic_cast<ArdourCanvas::CanvasNoteEvent
*> (item
);
761 if (cn
->mouse_near_ends()) {
762 _drags
->set (new NoteResizeDrag (this, item
), event
, current_canvas_cursor
);
764 _drags
->set (new NoteDrag (this, item
), event
);
774 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::ModifierMask(Keyboard::PrimaryModifier
|Keyboard::SecondaryModifier
)) &&
775 event
->type
== GDK_BUTTON_PRESS
) {
777 _drags
->set (new RubberbandSelectDrag (this, item
), event
);
779 } else if (event
->type
== GDK_BUTTON_PRESS
) {
782 case FadeInHandleItem
:
784 RegionSelection s
= get_equivalent_regions (selection
->regions
, Properties::edit
.property_id
);
785 _drags
->set (new FadeInDrag (this, item
, reinterpret_cast<RegionView
*> (item
->get_data("regionview")), s
), event
, _cursors
->fade_in
);
789 case FadeOutHandleItem
:
791 RegionSelection s
= get_equivalent_regions (selection
->regions
, Properties::edit
.property_id
);
792 _drags
->set (new FadeOutDrag (this, item
, reinterpret_cast<RegionView
*> (item
->get_data("regionview")), s
), event
, _cursors
->fade_out
);
796 case FeatureLineItem
:
798 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::TertiaryModifier
)) {
799 remove_transient(item
);
803 _drags
->set (new FeatureLineDrag (this, item
), event
);
809 if (dynamic_cast<AutomationRegionView
*> (clicked_regionview
)) {
810 /* click on an automation region view; do nothing here and let the ARV's signal handler
816 if (internal_editing ()) {
817 /* no region drags in internal edit mode */
821 /* click on a normal region view */
822 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::CopyModifier
)) {
823 add_region_copy_drag (item
, event
, clicked_regionview
);
825 else if (Keyboard::the_keyboard().key_is_down (GDK_b
)) {
826 add_region_brush_drag (item
, event
, clicked_regionview
);
828 add_region_drag (item
, event
, clicked_regionview
);
831 if (_join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
&& !selection
->regions
.empty()) {
832 _drags
->add (new SelectionDrag (this, clicked_axisview
->get_selection_rect (clicked_selection
)->rect
, SelectionDrag::SelectionMove
));
835 _drags
->start_grab (event
);
838 case RegionViewNameHighlight
:
839 case LeftFrameHandle
:
840 case RightFrameHandle
:
841 if (!internal_editing () && !clicked_regionview
->region()->locked()) {
842 RegionSelection s
= get_equivalent_regions (selection
->regions
, Properties::edit
.property_id
);
843 _drags
->set (new TrimDrag (this, item
, clicked_regionview
, s
.by_layer()), event
);
850 /* rename happens on edit clicks */
851 RegionSelection s
= get_equivalent_regions (selection
->regions
, Properties::edit
.property_id
);
852 _drags
->set (new TrimDrag (this, clicked_regionview
->get_name_highlight(), clicked_regionview
, s
.by_layer()), event
);
857 case ControlPointItem
:
858 _drags
->set (new ControlPointDrag (this, item
), event
);
862 case AutomationLineItem
:
863 _drags
->set (new LineDrag (this, item
), event
);
868 if (internal_editing()) {
869 if (dynamic_cast<MidiTimeAxisView
*> (clicked_axisview
)) {
870 _drags
->set (new RegionCreateDrag (this, item
, clicked_axisview
), event
);
874 _drags
->set (new RubberbandSelectDrag (this, item
), event
);
878 case AutomationTrackItem
:
879 /* rubberband drag to select automation points */
880 _drags
->set (new RubberbandSelectDrag (this, item
), event
);
885 if (join_object_range_button
.get_active()) {
886 /* we're in "smart" joined mode, and we've clicked on a Selection */
887 double const y
= event
->button
.y
+ vertical_adjustment
.get_value() - canvas_timebars_vsize
;
888 pair
<TimeAxisView
*, int> tvp
= trackview_by_y_position (y
);
890 /* if we're over an automation track, start a drag of its data */
891 AutomationTimeAxisView
* atv
= dynamic_cast<AutomationTimeAxisView
*> (tvp
.first
);
893 _drags
->set (new AutomationRangeDrag (this, atv
->base_item(), selection
->time
), event
, _cursors
->up_down
);
896 /* if we're over a track and a region, and in the `object' part of a region,
897 put a selection around the region and drag both
899 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*> (tvp
.first
);
900 if (rtv
&& _join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
) {
901 boost::shared_ptr
<Track
> t
= boost::dynamic_pointer_cast
<Track
> (rtv
->route ());
903 boost::shared_ptr
<Playlist
> pl
= t
->playlist ();
906 boost::shared_ptr
<Region
> r
= pl
->top_region_at (event_frame (event
));
908 RegionView
* rv
= rtv
->view()->find_view (r
);
909 clicked_selection
= select_range_around_region (rv
);
910 _drags
->add (new SelectionDrag (this, item
, SelectionDrag::SelectionMove
));
911 list
<RegionView
*> rvs
;
913 _drags
->add (new RegionMoveDrag (this, item
, rv
, rvs
, false, false));
914 _drags
->start_grab (event
);
925 case ImageFrameHandleStartItem
:
926 imageframe_start_handle_op(item
, event
) ;
929 case ImageFrameHandleEndItem
:
930 imageframe_end_handle_op(item
, event
) ;
933 case MarkerViewHandleStartItem
:
934 markerview_item_start_handle_op(item
, event
) ;
937 case MarkerViewHandleEndItem
:
938 markerview_item_end_handle_op(item
, event
) ;
942 start_markerview_grab(item
, event
) ;
945 start_imageframe_grab(item
, event
) ;
963 /* start a grab so that if we finish after moving
964 we can tell what happened.
966 _drags
->set (new RegionGainDrag (this, item
), event
, current_canvas_cursor
);
970 _drags
->set (new LineDrag (this, item
), event
);
973 case ControlPointItem
:
974 _drags
->set (new ControlPointDrag (this, item
), event
);
985 case ControlPointItem
:
986 _drags
->set (new ControlPointDrag (this, item
), event
);
989 case AutomationLineItem
:
990 _drags
->set (new LineDrag (this, item
), event
);
994 // XXX need automation mode to identify which
996 // start_line_grab_from_regionview (item, event);
1006 if (event
->type
== GDK_BUTTON_PRESS
) {
1007 _drags
->set (new MouseZoomDrag (this, item
), event
);
1014 if (internal_editing() && item_type
== NoteItem
) {
1015 /* drag notes if we're in internal edit mode */
1016 _drags
->set (new NoteResizeDrag (this, item
), event
, current_canvas_cursor
);
1018 } else if ((!internal_editing() || dynamic_cast<AudioRegionView
*> (clicked_regionview
)) && clicked_regionview
) {
1019 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
1020 _drags
->set (new TimeFXDrag (this, item
, clicked_regionview
, selection
->regions
.by_layer()), event
);
1026 _drags
->set (new ScrubDrag (this, item
), event
);
1027 scrub_reversals
= 0;
1028 scrub_reverse_distance
= 0;
1029 last_scrub_x
= event
->button
.x
;
1030 scrubbing_direction
= 0;
1031 set_canvas_cursor (_cursors
->transparent
);
1043 Editor::button_press_handler_2 (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
1045 Editing::MouseMode
const eff
= effective_mouse_mode ();
1048 switch (item_type
) {
1050 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::CopyModifier
)) {
1051 add_region_copy_drag (item
, event
, clicked_regionview
);
1053 add_region_drag (item
, event
, clicked_regionview
);
1055 _drags
->start_grab (event
);
1058 case ControlPointItem
:
1059 _drags
->set (new ControlPointDrag (this, item
), event
);
1067 switch (item_type
) {
1068 case RegionViewNameHighlight
:
1069 case LeftFrameHandle
:
1070 case RightFrameHandle
:
1071 if (!internal_editing ()) {
1072 _drags
->set (new TrimDrag (this, item
, clicked_regionview
, selection
->regions
.by_layer()), event
);
1077 case RegionViewName
:
1078 _drags
->set (new TrimDrag (this, clicked_regionview
->get_name_highlight(), clicked_regionview
, selection
->regions
.by_layer()), event
);
1089 /* relax till release */
1095 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
1096 temporal_zoom_session();
1098 temporal_zoom_to_frame (true, event_frame(event
));
1111 Editor::button_press_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
1113 if (event
->type
!= GDK_BUTTON_PRESS
) {
1117 Glib::RefPtr
<Gdk::Window
> canvas_window
= const_cast<Editor
*>(this)->track_canvas
->get_window();
1119 if (canvas_window
) {
1120 Glib::RefPtr
<const Gdk::Window
> pointer_window
;
1123 Gdk::ModifierType mask
;
1125 pointer_window
= canvas_window
->get_pointer (x
, y
, mask
);
1127 if (pointer_window
== track_canvas
->get_bin_window()) {
1128 track_canvas
->window_to_world (x
, y
, wx
, wy
);
1132 pre_press_cursor
= current_canvas_cursor
;
1134 track_canvas
->grab_focus();
1136 if (_session
&& _session
->actively_recording()) {
1140 button_selection (item
, event
, item_type
);
1142 if (!_drags
->active () &&
1143 (Keyboard::is_delete_event (&event
->button
) ||
1144 Keyboard::is_context_menu_event (&event
->button
) ||
1145 Keyboard::is_edit_event (&event
->button
))) {
1147 /* handled by button release */
1151 switch (event
->button
.button
) {
1153 return button_press_handler_1 (item
, event
, item_type
);
1157 return button_press_handler_2 (item
, event
, item_type
);
1172 Editor::button_release_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
1174 framepos_t where
= event_frame (event
, 0, 0);
1175 AutomationTimeAxisView
* atv
= 0;
1177 if (pre_press_cursor
) {
1178 set_canvas_cursor (pre_press_cursor
);
1179 pre_press_cursor
= 0;
1182 /* no action if we're recording */
1184 if (_session
&& _session
->actively_recording()) {
1188 /* see if we're finishing a drag */
1190 bool were_dragging
= false;
1191 if (_drags
->active ()) {
1192 bool const r
= _drags
->end_grab (event
);
1194 /* grab dragged, so do nothing else */
1198 were_dragging
= true;
1201 update_region_layering_order_editor ();
1203 /* edit events get handled here */
1205 if (!_drags
->active () && Keyboard::is_edit_event (&event
->button
)) {
1206 switch (item_type
) {
1208 show_region_properties ();
1211 case TempoMarkerItem
:
1212 edit_tempo_marker (item
);
1215 case MeterMarkerItem
:
1216 edit_meter_marker (item
);
1219 case RegionViewName
:
1220 if (clicked_regionview
->name_active()) {
1221 return mouse_rename_region (item
, event
);
1225 case ControlPointItem
:
1226 edit_control_point (item
);
1239 /* context menu events get handled here */
1241 if (Keyboard::is_context_menu_event (&event
->button
)) {
1243 if (!_drags
->active ()) {
1245 /* no matter which button pops up the context menu, tell the menu
1246 widget to use button 1 to drive menu selection.
1249 switch (item_type
) {
1251 case FadeInHandleItem
:
1253 case FadeOutHandleItem
:
1254 popup_fade_context_menu (1, event
->button
.time
, item
, item_type
);
1258 popup_track_context_menu (1, event
->button
.time
, item_type
, false);
1262 case RegionViewNameHighlight
:
1263 case LeftFrameHandle
:
1264 case RightFrameHandle
:
1265 case RegionViewName
:
1266 popup_track_context_menu (1, event
->button
.time
, item_type
, false);
1270 popup_track_context_menu (1, event
->button
.time
, item_type
, true);
1273 case AutomationTrackItem
:
1274 popup_track_context_menu (1, event
->button
.time
, item_type
, false);
1278 case RangeMarkerBarItem
:
1279 case TransportMarkerBarItem
:
1280 case CdMarkerBarItem
:
1283 popup_ruler_menu (where
, item_type
);
1287 marker_context_menu (&event
->button
, item
);
1290 case TempoMarkerItem
:
1291 tempo_or_meter_marker_context_menu (&event
->button
, item
);
1294 case MeterMarkerItem
:
1295 tempo_or_meter_marker_context_menu (&event
->button
, item
);
1298 case CrossfadeViewItem
:
1299 popup_track_context_menu (1, event
->button
.time
, item_type
, false);
1303 case ImageFrameItem
:
1304 popup_imageframe_edit_menu(1, event
->button
.time
, item
, true) ;
1306 case ImageFrameTimeAxisItem
:
1307 popup_imageframe_edit_menu(1, event
->button
.time
, item
, false) ;
1309 case MarkerViewItem
:
1310 popup_marker_time_axis_edit_menu(1, event
->button
.time
, item
, true) ;
1312 case MarkerTimeAxisItem
:
1313 popup_marker_time_axis_edit_menu(1, event
->button
.time
, item
, false) ;
1325 /* delete events get handled here */
1327 Editing::MouseMode
const eff
= effective_mouse_mode ();
1329 if (!_drags
->active () && Keyboard::is_delete_event (&event
->button
)) {
1331 switch (item_type
) {
1332 case TempoMarkerItem
:
1333 remove_tempo_marker (item
);
1336 case MeterMarkerItem
:
1337 remove_meter_marker (item
);
1341 remove_marker (*item
, event
);
1345 if (eff
== MouseObject
) {
1346 remove_clicked_region ();
1350 case ControlPointItem
:
1351 if (eff
== MouseGain
) {
1352 remove_gain_control_point (item
, event
);
1354 remove_control_point (item
, event
);
1359 remove_midi_note (item
, event
);
1368 switch (event
->button
.button
) {
1371 switch (item_type
) {
1372 /* see comments in button_press_handler */
1373 case PlayheadCursorItem
:
1376 case AutomationLineItem
:
1377 case StartSelectionTrimItem
:
1378 case EndSelectionTrimItem
:
1382 if (!_dragging_playhead
) {
1383 snap_to_with_modifier (where
, event
, 0, true);
1384 mouse_add_new_marker (where
);
1388 case CdMarkerBarItem
:
1389 if (!_dragging_playhead
) {
1390 // if we get here then a dragged range wasn't done
1391 snap_to_with_modifier (where
, event
, 0, true);
1392 mouse_add_new_marker (where
, true);
1397 if (!_dragging_playhead
) {
1398 snap_to_with_modifier (where
, event
);
1399 mouse_add_new_tempo_event (where
);
1404 if (!_dragging_playhead
) {
1405 mouse_add_new_meter_event (pixel_to_frame (event
->button
.x
));
1416 switch (item_type
) {
1417 case AutomationTrackItem
:
1418 atv
= dynamic_cast<AutomationTimeAxisView
*>(clicked_axisview
);
1420 atv
->add_automation_event (item
, event
, where
, event
->button
.y
);
1431 switch (item_type
) {
1434 /* check that we didn't drag before releasing, since
1435 its really annoying to create new control
1436 points when doing this.
1438 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*> (clicked_regionview
);
1439 if (were_dragging
&& arv
) {
1440 arv
->add_gain_point_event (item
, event
);
1446 case AutomationTrackItem
:
1447 dynamic_cast<AutomationTimeAxisView
*>(clicked_axisview
)->
1448 add_automation_event (item
, event
, where
, event
->button
.y
);
1457 set_canvas_cursor (current_canvas_cursor
);
1458 if (scrubbing_direction
== 0) {
1459 /* no drag, just a click */
1460 switch (item_type
) {
1462 play_selected_region ();
1468 /* make sure we stop */
1469 _session
->request_transport_speed (0.0);
1486 switch (item_type
) {
1488 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
1490 } else if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::ModifierMask (Keyboard::TertiaryModifier
|Keyboard::SecondaryModifier
))) {
1493 // Button2 click is unused
1506 // x_style_paste (where, 1.0);
1526 Editor::enter_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
1533 last_item_entered
= item
;
1535 switch (item_type
) {
1536 case ControlPointItem
:
1537 if (mouse_mode
== MouseGain
|| mouse_mode
== MouseObject
) {
1538 cp
= static_cast<ControlPoint
*>(item
->get_data ("control_point"));
1539 cp
->set_visible (true);
1543 at_y
= cp
->get_y ();
1544 cp
->i2w (at_x
, at_y
);
1548 fraction
= 1.0 - (cp
->get_y() / cp
->line().height());
1550 if (is_drawable() && !_drags
->active ()) {
1551 set_canvas_cursor (_cursors
->fader
);
1554 set_verbose_canvas_cursor (cp
->line().get_verbose_cursor_string (fraction
), at_x
, at_y
);
1555 show_verbose_canvas_cursor ();
1560 if (mouse_mode
== MouseGain
) {
1561 ArdourCanvas::Line
*line
= dynamic_cast<ArdourCanvas::Line
*> (item
);
1563 line
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine
.get();
1564 if (is_drawable()) {
1565 set_canvas_cursor (_cursors
->fader
);
1570 case AutomationLineItem
:
1571 if (mouse_mode
== MouseGain
|| mouse_mode
== MouseObject
) {
1573 ArdourCanvas::Line
*line
= dynamic_cast<ArdourCanvas::Line
*> (item
);
1575 line
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine
.get();
1577 if (is_drawable()) {
1578 set_canvas_cursor (_cursors
->fader
);
1583 case RegionViewNameHighlight
:
1584 if (is_drawable() && mouse_mode
== MouseObject
&& !internal_editing() && entered_regionview
) {
1585 set_canvas_cursor_for_region_view (event
->crossing
.x
, entered_regionview
);
1586 _over_region_trim_target
= true;
1590 case LeftFrameHandle
:
1591 case RightFrameHandle
:
1592 if (is_drawable() && mouse_mode
== MouseObject
&& !internal_editing() && entered_regionview
) {
1593 set_canvas_cursor_for_region_view (event
->crossing
.x
, entered_regionview
);
1597 case StartSelectionTrimItem
:
1598 case EndSelectionTrimItem
:
1601 case ImageFrameHandleStartItem
:
1602 case ImageFrameHandleEndItem
:
1603 case MarkerViewHandleStartItem
:
1604 case MarkerViewHandleEndItem
:
1607 if (is_drawable()) {
1608 set_canvas_cursor (_cursors
->trimmer
);
1612 case PlayheadCursorItem
:
1613 if (is_drawable()) {
1614 switch (_edit_point
) {
1616 set_canvas_cursor (_cursors
->grabber_edit_point
);
1619 set_canvas_cursor (_cursors
->grabber
);
1625 case RegionViewName
:
1627 /* when the name is not an active item, the entire name highlight is for trimming */
1629 if (!reinterpret_cast<RegionView
*> (item
->get_data ("regionview"))->name_active()) {
1630 if (mouse_mode
== MouseObject
&& is_drawable()) {
1631 set_canvas_cursor_for_region_view (event
->crossing
.x
, entered_regionview
);
1632 _over_region_trim_target
= true;
1638 case AutomationTrackItem
:
1639 if (is_drawable()) {
1640 Gdk::Cursor
*cursor
;
1641 switch (mouse_mode
) {
1643 cursor
= _cursors
->selector
;
1646 cursor
= _cursors
->zoom_in
;
1649 cursor
= _cursors
->cross_hair
;
1653 set_canvas_cursor (cursor
);
1655 AutomationTimeAxisView
* atv
;
1656 if ((atv
= static_cast<AutomationTimeAxisView
*>(item
->get_data ("trackview"))) != 0) {
1657 clear_entered_track
= false;
1658 set_entered_track (atv
);
1664 case RangeMarkerBarItem
:
1665 case TransportMarkerBarItem
:
1666 case CdMarkerBarItem
:
1669 if (is_drawable()) {
1670 set_canvas_cursor (_cursors
->timebar
);
1675 if ((marker
= static_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
1678 entered_marker
= marker
;
1679 marker
->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker
.get());
1681 case MeterMarkerItem
:
1682 case TempoMarkerItem
:
1683 if (is_drawable()) {
1684 set_canvas_cursor (_cursors
->timebar
);
1688 case FadeInHandleItem
:
1689 if (mouse_mode
== MouseObject
&& !internal_editing()) {
1690 ArdourCanvas::SimpleRect
*rect
= dynamic_cast<ArdourCanvas::SimpleRect
*> (item
);
1692 rect
->property_fill_color_rgba() = 0xBBBBBBAA;
1694 set_canvas_cursor (_cursors
->fade_in
);
1698 case FadeOutHandleItem
:
1699 if (mouse_mode
== MouseObject
&& !internal_editing()) {
1700 ArdourCanvas::SimpleRect
*rect
= dynamic_cast<ArdourCanvas::SimpleRect
*> (item
);
1702 rect
->property_fill_color_rgba() = 0xBBBBBBAA;
1704 set_canvas_cursor (_cursors
->fade_out
);
1707 case FeatureLineItem
:
1709 ArdourCanvas::SimpleLine
*line
= dynamic_cast<ArdourCanvas::SimpleLine
*> (item
);
1710 line
->property_color_rgba() = 0xFF0000FF;
1714 if (join_object_range_button
.get_active()) {
1715 set_canvas_cursor ();
1723 /* second pass to handle entered track status in a comprehensible way.
1726 switch (item_type
) {
1728 case AutomationLineItem
:
1729 case ControlPointItem
:
1730 /* these do not affect the current entered track state */
1731 clear_entered_track
= false;
1734 case AutomationTrackItem
:
1735 /* handled above already */
1739 set_entered_track (0);
1747 Editor::leave_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
1757 switch (item_type
) {
1758 case ControlPointItem
:
1759 cp
= reinterpret_cast<ControlPoint
*>(item
->get_data ("control_point"));
1760 if (cp
->line().the_list()->interpolation() != AutomationList::Discrete
) {
1761 if (cp
->line().npoints() > 1 && !cp
->get_selected()) {
1762 cp
->set_visible (false);
1766 if (is_drawable()) {
1767 set_canvas_cursor (current_canvas_cursor
);
1770 hide_verbose_canvas_cursor ();
1773 case RegionViewNameHighlight
:
1774 case LeftFrameHandle
:
1775 case RightFrameHandle
:
1776 case StartSelectionTrimItem
:
1777 case EndSelectionTrimItem
:
1778 case PlayheadCursorItem
:
1781 case ImageFrameHandleStartItem
:
1782 case ImageFrameHandleEndItem
:
1783 case MarkerViewHandleStartItem
:
1784 case MarkerViewHandleEndItem
:
1787 _over_region_trim_target
= false;
1789 if (is_drawable()) {
1790 set_canvas_cursor (current_canvas_cursor
);
1795 case AutomationLineItem
:
1796 al
= reinterpret_cast<AutomationLine
*> (item
->get_data ("line"));
1798 ArdourCanvas::Line
*line
= dynamic_cast<ArdourCanvas::Line
*> (item
);
1800 line
->property_fill_color_rgba() = al
->get_line_color();
1802 if (is_drawable()) {
1803 set_canvas_cursor (current_canvas_cursor
);
1807 case RegionViewName
:
1808 /* see enter_handler() for notes */
1809 _over_region_trim_target
= false;
1811 if (!reinterpret_cast<RegionView
*> (item
->get_data ("regionview"))->name_active()) {
1812 if (is_drawable() && mouse_mode
== MouseObject
) {
1813 set_canvas_cursor (current_canvas_cursor
);
1818 case RangeMarkerBarItem
:
1819 case TransportMarkerBarItem
:
1820 case CdMarkerBarItem
:
1824 if (is_drawable()) {
1825 set_canvas_cursor (current_canvas_cursor
);
1830 if ((marker
= static_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
1834 if ((loc
= find_location_from_marker (marker
, is_start
)) != 0) {
1835 location_flags_changed (loc
, this);
1838 case MeterMarkerItem
:
1839 case TempoMarkerItem
:
1841 if (is_drawable()) {
1842 set_canvas_cursor (_cursors
->timebar
);
1847 case FadeInHandleItem
:
1848 case FadeOutHandleItem
:
1849 rv
= static_cast<RegionView
*>(item
->get_data ("regionview"));
1851 ArdourCanvas::SimpleRect
*rect
= dynamic_cast<ArdourCanvas::SimpleRect
*> (item
);
1853 rect
->property_fill_color_rgba() = rv
->get_fill_color();
1854 rect
->property_outline_pixels() = 0;
1857 set_canvas_cursor (current_canvas_cursor
);
1860 case AutomationTrackItem
:
1861 if (is_drawable()) {
1862 set_canvas_cursor (current_canvas_cursor
);
1863 clear_entered_track
= true;
1864 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track
));
1867 case FeatureLineItem
:
1869 ArdourCanvas::SimpleLine
*line
= dynamic_cast<ArdourCanvas::SimpleLine
*> (item
);
1870 line
->property_color_rgba() = (guint
) ARDOUR_UI::config()->canvasvar_ZeroLine
.get();;
1882 Editor::left_automation_track ()
1884 if (clear_entered_track
) {
1885 set_entered_track (0);
1886 clear_entered_track
= false;
1892 Editor::scrub (framepos_t frame
, double current_x
)
1896 if (scrubbing_direction
== 0) {
1898 _session
->request_locate (frame
, false);
1899 _session
->request_transport_speed (0.1);
1900 scrubbing_direction
= 1;
1904 if (last_scrub_x
> current_x
) {
1906 /* pointer moved to the left */
1908 if (scrubbing_direction
> 0) {
1910 /* we reversed direction to go backwards */
1913 scrub_reverse_distance
+= (int) (last_scrub_x
- current_x
);
1917 /* still moving to the left (backwards) */
1919 scrub_reversals
= 0;
1920 scrub_reverse_distance
= 0;
1922 delta
= 0.01 * (last_scrub_x
- current_x
);
1923 _session
->request_transport_speed_nonzero (_session
->transport_speed() - delta
);
1927 /* pointer moved to the right */
1929 if (scrubbing_direction
< 0) {
1930 /* we reversed direction to go forward */
1933 scrub_reverse_distance
+= (int) (current_x
- last_scrub_x
);
1936 /* still moving to the right */
1938 scrub_reversals
= 0;
1939 scrub_reverse_distance
= 0;
1941 delta
= 0.01 * (current_x
- last_scrub_x
);
1942 _session
->request_transport_speed_nonzero (_session
->transport_speed() + delta
);
1946 /* if there have been more than 2 opposite motion moves detected, or one that moves
1947 back more than 10 pixels, reverse direction
1950 if (scrub_reversals
>= 2 || scrub_reverse_distance
> 10) {
1952 if (scrubbing_direction
> 0) {
1953 /* was forwards, go backwards */
1954 _session
->request_transport_speed (-0.1);
1955 scrubbing_direction
= -1;
1957 /* was backwards, go forwards */
1958 _session
->request_transport_speed (0.1);
1959 scrubbing_direction
= 1;
1962 scrub_reverse_distance
= 0;
1963 scrub_reversals
= 0;
1967 last_scrub_x
= current_x
;
1971 Editor::motion_handler (ArdourCanvas::Item
* /*item*/, GdkEvent
* event
, bool from_autoscroll
)
1973 _last_motion_y
= event
->motion
.y
;
1975 if (event
->motion
.is_hint
) {
1978 /* We call this so that MOTION_NOTIFY events continue to be
1979 delivered to the canvas. We need to do this because we set
1980 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1981 the density of the events, at the expense of a round-trip
1982 to the server. Given that this will mostly occur on cases
1983 where DISPLAY = :0.0, and given the cost of what the motion
1984 event might do, its a good tradeoff.
1987 track_canvas
->get_pointer (x
, y
);
1990 if (current_stepping_trackview
) {
1991 /* don't keep the persistent stepped trackview if the mouse moves */
1992 current_stepping_trackview
= 0;
1993 step_timeout
.disconnect ();
1996 if (_session
&& _session
->actively_recording()) {
1997 /* Sorry. no dragging stuff around while we record */
2001 JoinObjectRangeState
const old
= _join_object_range_state
;
2002 update_join_object_range_location (event
->motion
.x
, event
->motion
.y
);
2003 if (_join_object_range_state
!= old
) {
2004 set_canvas_cursor ();
2007 if (_over_region_trim_target
) {
2008 set_canvas_cursor_for_region_view (event
->motion
.x
, entered_regionview
);
2011 bool handled
= false;
2012 if (_drags
->active ()) {
2013 handled
= _drags
->motion_handler (event
, from_autoscroll
);
2020 track_canvas_motion (event
);
2025 Editor::remove_gain_control_point (ArdourCanvas::Item
*item
, GdkEvent
* /*event*/)
2027 ControlPoint
* control_point
;
2029 if ((control_point
= reinterpret_cast<ControlPoint
*> (item
->get_data ("control_point"))) == 0) {
2030 fatal
<< _("programming error: control point canvas item has no control point object pointer!") << endmsg
;
2034 // We shouldn't remove the first or last gain point
2035 if (control_point
->line().is_last_point(*control_point
) ||
2036 control_point
->line().is_first_point(*control_point
)) {
2040 control_point
->line().remove_point (*control_point
);
2044 Editor::remove_control_point (ArdourCanvas::Item
* item
, GdkEvent
* /*event*/)
2046 ControlPoint
* control_point
;
2048 if ((control_point
= reinterpret_cast<ControlPoint
*> (item
->get_data ("control_point"))) == 0) {
2049 fatal
<< _("programming error: control point canvas item has no control point object pointer!") << endmsg
;
2053 control_point
->line().remove_point (*control_point
);
2057 Editor::edit_control_point (ArdourCanvas::Item
* item
)
2059 ControlPoint
* p
= reinterpret_cast<ControlPoint
*> (item
->get_data ("control_point"));
2062 fatal
<< _("programming error: control point canvas item has no control point object pointer!") << endmsg
;
2066 ControlPointDialog
d (p
);
2067 d
.set_position (Gtk::WIN_POS_MOUSE
);
2070 if (d
.run () != RESPONSE_ACCEPT
) {
2074 p
->line().modify_point_y (*p
, d
.get_y_fraction ());
2078 Editor::edit_note (ArdourCanvas::Item
* item
)
2080 ArdourCanvas::CanvasNoteEvent
* e
= dynamic_cast<ArdourCanvas::CanvasNoteEvent
*> (item
);
2083 EditNoteDialog
d (&e
->region_view(), e
);
2084 d
.set_position (Gtk::WIN_POS_MOUSE
);
2092 Editor::visible_order_range (int* low
, int* high
) const
2094 *low
= TimeAxisView::max_order ();
2097 for (TrackViewList::const_iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
2099 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*> (*i
);
2101 if (!rtv
->hidden()) {
2103 if (*high
< rtv
->order()) {
2104 *high
= rtv
->order ();
2107 if (*low
> rtv
->order()) {
2108 *low
= rtv
->order ();
2115 Editor::region_view_item_click (AudioRegionView
& rv
, GdkEventButton
* event
)
2117 /* Either add to or set the set the region selection, unless
2118 this is an alignment click (control used)
2121 if (Keyboard::modifier_state_contains (event
->state
, Keyboard::PrimaryModifier
)) {
2122 TimeAxisView
* tv
= &rv
.get_time_axis_view();
2123 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*>(tv
);
2125 if (rtv
&& rtv
->is_track()) {
2126 speed
= rtv
->track()->speed();
2129 framepos_t where
= get_preferred_edit_position();
2133 if (Keyboard::modifier_state_equals (event
->state
, Keyboard::ModifierMask (Keyboard::PrimaryModifier
|Keyboard::SecondaryModifier
))) {
2135 align_region (rv
.region(), SyncPoint
, (framepos_t
) (where
* speed
));
2137 } else if (Keyboard::modifier_state_equals (event
->state
, Keyboard::ModifierMask (Keyboard::PrimaryModifier
|Keyboard::TertiaryModifier
))) {
2139 align_region (rv
.region(), End
, (framepos_t
) (where
* speed
));
2143 align_region (rv
.region(), Start
, (framepos_t
) (where
* speed
));
2150 Editor::show_verbose_time_cursor (framepos_t frame
, double offset
, double xpos
, double ypos
)
2153 Timecode::Time timecode
;
2154 Timecode::BBT_Time bbt
;
2156 framepos_t frame_rate
;
2159 if (_session
== 0) {
2165 if (Profile
->get_sae() || Profile
->get_small_screen()) {
2166 m
= ARDOUR_UI::instance()->primary_clock
.mode();
2168 m
= ARDOUR_UI::instance()->secondary_clock
.mode();
2172 case AudioClock::BBT
:
2173 _session
->bbt_time (frame
, bbt
);
2174 snprintf (buf
, sizeof (buf
), "%02" PRIu32
"|%02" PRIu32
"|%02" PRIu32
, bbt
.bars
, bbt
.beats
, bbt
.ticks
);
2177 case AudioClock::Timecode
:
2178 _session
->timecode_time (frame
, timecode
);
2179 snprintf (buf
, sizeof (buf
), "%02" PRId32
":%02" PRId32
":%02" PRId32
":%02" PRId32
, timecode
.hours
, timecode
.minutes
, timecode
.seconds
, timecode
.frames
);
2182 case AudioClock::MinSec
:
2183 /* XXX this is copied from show_verbose_duration_cursor() */
2184 frame_rate
= _session
->frame_rate();
2185 hours
= frame
/ (frame_rate
* 3600);
2186 frame
= frame
% (frame_rate
* 3600);
2187 mins
= frame
/ (frame_rate
* 60);
2188 frame
= frame
% (frame_rate
* 60);
2189 secs
= (float) frame
/ (float) frame_rate
;
2190 snprintf (buf
, sizeof (buf
), "%02" PRId32
":%02" PRId32
":%07.4f", hours
, mins
, secs
);
2194 snprintf (buf
, sizeof(buf
), "%" PRIi64
, frame
);
2198 if (xpos
>= 0 && ypos
>=0) {
2199 set_verbose_canvas_cursor (buf
, xpos
+ offset
, ypos
+ offset
);
2201 set_verbose_canvas_cursor (buf
, _drags
->current_pointer_x() + offset
- horizontal_position(), _drags
->current_pointer_y() + offset
- vertical_adjustment
.get_value() + canvas_timebars_vsize
);
2203 show_verbose_canvas_cursor ();
2207 Editor::show_verbose_duration_cursor (framepos_t start
, framepos_t end
, double offset
, double xpos
, double ypos
)
2210 Timecode::Time timecode
;
2211 Timecode::BBT_Time sbbt
;
2212 Timecode::BBT_Time ebbt
;
2214 framepos_t distance
, frame_rate
;
2216 Meter
meter_at_start(_session
->tempo_map().meter_at(start
));
2218 if (_session
== 0) {
2224 if (Profile
->get_sae() || Profile
->get_small_screen()) {
2225 m
= ARDOUR_UI::instance()->primary_clock
.mode ();
2227 m
= ARDOUR_UI::instance()->secondary_clock
.mode ();
2231 case AudioClock::BBT
:
2232 _session
->bbt_time (start
, sbbt
);
2233 _session
->bbt_time (end
, ebbt
);
2236 /* XXX this computation won't work well if the
2237 user makes a selection that spans any meter changes.
2240 ebbt
.bars
-= sbbt
.bars
;
2241 if (ebbt
.beats
>= sbbt
.beats
) {
2242 ebbt
.beats
-= sbbt
.beats
;
2245 ebbt
.beats
= int(meter_at_start
.beats_per_bar()) + ebbt
.beats
- sbbt
.beats
;
2247 if (ebbt
.ticks
>= sbbt
.ticks
) {
2248 ebbt
.ticks
-= sbbt
.ticks
;
2251 ebbt
.ticks
= int(Timecode::BBT_Time::ticks_per_beat
) + ebbt
.ticks
- sbbt
.ticks
;
2254 snprintf (buf
, sizeof (buf
), "%02" PRIu32
"|%02" PRIu32
"|%02" PRIu32
, ebbt
.bars
, ebbt
.beats
, ebbt
.ticks
);
2257 case AudioClock::Timecode
:
2258 _session
->timecode_duration (end
- start
, timecode
);
2259 snprintf (buf
, sizeof (buf
), "%02" PRId32
":%02" PRId32
":%02" PRId32
":%02" PRId32
, timecode
.hours
, timecode
.minutes
, timecode
.seconds
, timecode
.frames
);
2262 case AudioClock::MinSec
:
2263 /* XXX this stuff should be elsewhere.. */
2264 distance
= end
- start
;
2265 frame_rate
= _session
->frame_rate();
2266 hours
= distance
/ (frame_rate
* 3600);
2267 distance
= distance
% (frame_rate
* 3600);
2268 mins
= distance
/ (frame_rate
* 60);
2269 distance
= distance
% (frame_rate
* 60);
2270 secs
= (float) distance
/ (float) frame_rate
;
2271 snprintf (buf
, sizeof (buf
), "%02" PRId32
":%02" PRId32
":%07.4f", hours
, mins
, secs
);
2275 snprintf (buf
, sizeof(buf
), "%" PRIi64
, end
- start
);
2279 if (xpos
>= 0 && ypos
>=0) {
2280 set_verbose_canvas_cursor (buf
, xpos
+ offset
, ypos
+ offset
);
2283 set_verbose_canvas_cursor (buf
, _drags
->current_pointer_x() + offset
, _drags
->current_pointer_y() + offset
);
2286 show_verbose_canvas_cursor ();
2290 Editor::collect_new_region_view (RegionView
* rv
)
2292 latest_regionviews
.push_back (rv
);
2296 Editor::collect_and_select_new_region_view (RegionView
* rv
)
2299 latest_regionviews
.push_back (rv
);
2303 Editor::cancel_selection ()
2305 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
2306 (*i
)->hide_selection ();
2309 selection
->clear ();
2310 clicked_selection
= 0;
2315 Editor::point_trim (GdkEvent
* event
, framepos_t new_bound
)
2317 RegionView
* rv
= clicked_regionview
;
2319 /* Choose action dependant on which button was pressed */
2320 switch (event
->button
.button
) {
2322 begin_reversible_command (_("start point trim"));
2324 if (selection
->selected (rv
)) {
2325 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin();
2326 i
!= selection
->regions
.by_layer().end(); ++i
)
2329 cerr
<< "region view contains null region" << endl
;
2332 if (!(*i
)->region()->locked()) {
2333 (*i
)->region()->clear_changes ();
2334 (*i
)->region()->trim_front (new_bound
, this);
2335 _session
->add_command(new StatefulDiffCommand ((*i
)->region()));
2340 if (!rv
->region()->locked()) {
2341 rv
->region()->clear_changes ();
2342 rv
->region()->trim_front (new_bound
, this);
2343 _session
->add_command(new StatefulDiffCommand (rv
->region()));
2347 commit_reversible_command();
2351 begin_reversible_command (_("End point trim"));
2353 if (selection
->selected (rv
)) {
2355 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
)
2357 if (!(*i
)->region()->locked()) {
2358 (*i
)->region()->clear_changes();
2359 (*i
)->region()->trim_end (new_bound
, this);
2360 _session
->add_command(new StatefulDiffCommand ((*i
)->region()));
2366 if (!rv
->region()->locked()) {
2367 rv
->region()->clear_changes ();
2368 rv
->region()->trim_end (new_bound
, this);
2369 _session
->add_command (new StatefulDiffCommand (rv
->region()));
2373 commit_reversible_command();
2382 Editor::hide_marker (ArdourCanvas::Item
* item
, GdkEvent
* /*event*/)
2387 if ((marker
= static_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
2388 fatal
<< _("programming error: marker canvas item has no marker object pointer!") << endmsg
;
2392 Location
* location
= find_location_from_marker (marker
, is_start
);
2393 location
->set_hidden (true, this);
2398 Editor::reposition_zoom_rect (framepos_t start
, framepos_t end
)
2400 double x1
= frame_to_pixel (start
);
2401 double x2
= frame_to_pixel (end
);
2402 double y2
= full_canvas_height
- 1.0;
2404 zoom_rect
->property_x1() = x1
;
2405 zoom_rect
->property_y1() = 1.0;
2406 zoom_rect
->property_x2() = x2
;
2407 zoom_rect
->property_y2() = y2
;
2412 Editor::mouse_rename_region (ArdourCanvas::Item
* /*item*/, GdkEvent
* /*event*/)
2414 using namespace Gtkmm2ext
;
2416 ArdourPrompter
prompter (false);
2418 prompter
.set_prompt (_("Name for region:"));
2419 prompter
.set_initial_text (clicked_regionview
->region()->name());
2420 prompter
.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT
);
2421 prompter
.set_response_sensitive (Gtk::RESPONSE_ACCEPT
, false);
2422 prompter
.show_all ();
2423 switch (prompter
.run ()) {
2424 case Gtk::RESPONSE_ACCEPT
:
2426 prompter
.get_result(str
);
2428 clicked_regionview
->region()->set_name (str
);
2437 Editor::mouse_brush_insert_region (RegionView
* rv
, framepos_t pos
)
2439 /* no brushing without a useful snap setting */
2441 switch (_snap_mode
) {
2443 return; /* can't work because it allows region to be placed anywhere */
2448 switch (_snap_type
) {
2456 /* don't brush a copy over the original */
2458 if (pos
== rv
->region()->position()) {
2462 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*>(&rv
->get_time_axis_view());
2464 if (rtv
== 0 || !rtv
->is_track()) {
2468 boost::shared_ptr
<Playlist
> playlist
= rtv
->playlist();
2469 double speed
= rtv
->track()->speed();
2471 playlist
->clear_changes ();
2472 boost::shared_ptr
<Region
> new_region (RegionFactory::create (rv
->region()));
2473 playlist
->add_region (new_region
, (framepos_t
) (pos
* speed
));
2474 _session
->add_command (new StatefulDiffCommand (playlist
));
2476 // playlist is frozen, so we have to update manually XXX this is disgusting
2478 playlist
->RegionAdded (new_region
); /* EMIT SIGNAL */
2482 Editor::track_height_step_timeout ()
2484 if (get_microseconds() - last_track_height_step_timestamp
< 250000) {
2485 current_stepping_trackview
= 0;
2492 Editor::add_region_drag (ArdourCanvas::Item
* item
, GdkEvent
* event
, RegionView
* region_view
)
2494 assert (region_view
);
2496 if (!region_view
->region()->playlist()) {
2500 _region_motion_group
->raise_to_top ();
2502 if (Config
->get_edit_mode() == Splice
) {
2503 _drags
->add (new RegionSpliceDrag (this, item
, region_view
, selection
->regions
.by_layer()));
2505 RegionSelection s
= get_equivalent_regions (selection
->regions
, ARDOUR::Properties::edit
.property_id
);
2506 _drags
->add (new RegionMoveDrag (this, item
, region_view
, s
.by_layer(), false, false));
2509 /* sync the canvas to what we think is its current state */
2510 update_canvas_now();
2514 Editor::add_region_copy_drag (ArdourCanvas::Item
* item
, GdkEvent
* event
, RegionView
* region_view
)
2516 assert (region_view
);
2518 if (!region_view
->region()->playlist()) {
2522 _region_motion_group
->raise_to_top ();
2524 RegionSelection s
= get_equivalent_regions (selection
->regions
, ARDOUR::Properties::edit
.property_id
);
2525 _drags
->add (new RegionMoveDrag (this, item
, region_view
, s
.by_layer(), false, true));
2529 Editor::add_region_brush_drag (ArdourCanvas::Item
* item
, GdkEvent
* event
, RegionView
* region_view
)
2531 assert (region_view
);
2533 if (!region_view
->region()->playlist()) {
2537 if (Config
->get_edit_mode() == Splice
) {
2541 RegionSelection s
= get_equivalent_regions (selection
->regions
, ARDOUR::Properties::edit
.property_id
);
2542 _drags
->add (new RegionMoveDrag (this, item
, region_view
, s
.by_layer(), true, false));
2544 begin_reversible_command (Operations::drag_region_brush
);
2547 /** Start a grab where a time range is selected, track(s) are selected, and the
2548 * user clicks and drags a region with a modifier in order to create a new region containing
2549 * the section of the clicked region that lies within the time range.
2552 Editor::start_selection_grab (ArdourCanvas::Item
* /*item*/, GdkEvent
* event
)
2554 if (clicked_regionview
== 0) {
2558 /* lets try to create new Region for the selection */
2560 vector
<boost::shared_ptr
<Region
> > new_regions
;
2561 create_region_from_selection (new_regions
);
2563 if (new_regions
.empty()) {
2567 /* XXX fix me one day to use all new regions */
2569 boost::shared_ptr
<Region
> region (new_regions
.front());
2571 /* add it to the current stream/playlist.
2573 tricky: the streamview for the track will add a new regionview. we will
2574 catch the signal it sends when it creates the regionview to
2575 set the regionview we want to then drag.
2578 latest_regionviews
.clear();
2579 sigc::connection c
= clicked_routeview
->view()->RegionViewAdded
.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view
));
2581 /* A selection grab currently creates two undo/redo operations, one for
2582 creating the new region and another for moving it.
2585 begin_reversible_command (Operations::selection_grab
);
2587 boost::shared_ptr
<Playlist
> playlist
= clicked_axisview
->playlist();
2589 playlist
->clear_changes ();
2590 clicked_routeview
->playlist()->add_region (region
, selection
->time
[clicked_selection
].start
);
2591 _session
->add_command(new StatefulDiffCommand (playlist
));
2593 commit_reversible_command ();
2597 if (latest_regionviews
.empty()) {
2598 /* something went wrong */
2602 /* we need to deselect all other regionviews, and select this one
2603 i'm ignoring undo stuff, because the region creation will take care of it
2605 selection
->set (latest_regionviews
);
2607 _drags
->set (new RegionMoveDrag (this, latest_regionviews
.front()->get_canvas_group(), latest_regionviews
.front(), latest_regionviews
, false, false), event
);
2613 if (_drags
->active ()) {
2616 selection
->clear ();
2621 Editor::set_internal_edit (bool yn
)
2623 _internal_editing
= yn
;
2626 mouse_select_button
.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2627 mouse_select_button
.get_image ()->show ();
2628 ARDOUR_UI::instance()->set_tip (mouse_select_button
, _("Draw/Edit MIDI Notes"));
2629 mouse_mode_toggled (mouse_mode
);
2633 mouse_select_button
.set_image (*(manage (new Image (::get_icon("tool_range")))));
2634 mouse_select_button
.get_image ()->show ();
2635 ARDOUR_UI::instance()->set_tip (mouse_select_button
, _("Select/Move Ranges"));
2636 mouse_mode_toggled (mouse_mode
); // sets cursor
2640 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2641 * used by the `join object/range' tool mode.
2644 Editor::update_join_object_range_location (double x
, double y
)
2646 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2647 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2648 that we're over requires searching the playlist.
2651 if (join_object_range_button
.get_active() == false || (mouse_mode
!= MouseRange
&& mouse_mode
!= MouseObject
)) {
2652 _join_object_range_state
= JOIN_OBJECT_RANGE_NONE
;
2656 if (mouse_mode
== MouseObject
) {
2657 _join_object_range_state
= JOIN_OBJECT_RANGE_OBJECT
;
2658 } else if (mouse_mode
== MouseRange
) {
2659 _join_object_range_state
= JOIN_OBJECT_RANGE_RANGE
;
2662 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2663 pair
<TimeAxisView
*, int> tvp
= trackview_by_y_position (y
+ vertical_adjustment
.get_value() - canvas_timebars_vsize
);
2667 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*> (tvp
.first
);
2672 rtv
->canvas_display()->w2i (cx
, cy
);
2674 double const c
= cy
/ rtv
->view()->child_height();
2676 double const f
= modf (c
, &d
);
2678 _join_object_range_state
= f
< 0.5 ? JOIN_OBJECT_RANGE_RANGE
: JOIN_OBJECT_RANGE_OBJECT
;
2684 Editor::effective_mouse_mode () const
2686 if (_join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
) {
2688 } else if (_join_object_range_state
== JOIN_OBJECT_RANGE_RANGE
) {
2696 Editor::remove_midi_note (ArdourCanvas::Item
* item
, GdkEvent
*)
2698 ArdourCanvas::CanvasNoteEvent
* e
= dynamic_cast<ArdourCanvas::CanvasNoteEvent
*> (item
);
2701 e
->region_view().delete_note (e
->note ());
2705 Editor::set_canvas_cursor_for_region_view (double x
, RegionView
* rv
)
2707 ArdourCanvas::Group
* g
= rv
->get_canvas_group ();
2708 ArdourCanvas::Group
* p
= g
->get_parent_group ();
2710 /* Compute x in region view parent coordinates */
2714 double x1
, x2
, y1
, y2
;
2715 g
->get_bounds (x1
, y1
, x2
, y2
);
2717 /* Halfway across the region */
2718 double const h
= (x1
+ x2
) / 2;
2720 Trimmable::CanTrim ct
= rv
->region()->can_trim ();
2722 if (ct
& Trimmable::FrontTrimEarlier
) {
2723 set_canvas_cursor (_cursors
->left_side_trim
);
2725 set_canvas_cursor (_cursors
->left_side_trim_right_only
);
2728 if (ct
& Trimmable::EndTrimLater
) {
2729 set_canvas_cursor (_cursors
->right_side_trim
);
2731 set_canvas_cursor (_cursors
->right_side_trim_left_only
);