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.
29 #include "pbd/error.h"
30 #include "pbd/enumwriter.h"
31 #include "pbd/memento_command.h"
32 #include "pbd/basename.h"
33 #include "pbd/stateful_diff_command.h"
35 #include "gtkmm2ext/bindings.h"
36 #include "gtkmm2ext/utils.h"
37 #include "gtkmm2ext/tearoff.h"
39 #include "canvas/canvas.h"
41 #include "ardour/audioregion.h"
42 #include "ardour/operations.h"
43 #include "ardour/playlist.h"
44 #include "ardour/profile.h"
45 #include "ardour/region_factory.h"
46 #include "ardour/route.h"
47 #include "ardour/session.h"
48 #include "ardour/types.h"
50 #include "ardour_ui.h"
53 #include "time_axis_view.h"
54 #include "audio_time_axis.h"
55 #include "audio_region_view.h"
56 #include "midi_region_view.h"
58 #include "streamview.h"
59 #include "region_gain_line.h"
60 #include "automation_time_axis.h"
61 #include "control_point.h"
63 #include "selection.h"
66 #include "rgb_macros.h"
67 #include "control_point_dialog.h"
68 #include "editor_drag.h"
69 #include "automation_region_view.h"
70 #include "edit_note_dialog.h"
71 #include "mouse_cursors.h"
72 #include "editor_cursors.h"
73 #include "verbose_cursor.h"
79 using namespace ARDOUR;
82 using namespace Editing;
83 using Gtkmm2ext::Keyboard;
86 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
88 /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
89 pays attentions to subwindows. this means that menu windows are ignored, and
90 if the pointer is in a menu, the return window from the call will be the
91 the regular subwindow *under* the menu.
93 this matters quite a lot if the pointer is moving around in a menu that overlaps
94 the track canvas because we will believe that we are within the track canvas
95 when we are not. therefore, we track enter/leave events for the track canvas
96 and allow that to override the result of gdk_window_get_pointer().
99 if (!within_track_canvas) {
104 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
106 if (!canvas_window) {
110 Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
112 if (!pointer_window) {
116 if (pointer_window != canvas_window) {
117 in_track_canvas = false;
121 in_track_canvas = true;
124 event.type = GDK_BUTTON_RELEASE;
128 where = window_event_sample (&event, 0, 0);
134 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
136 ArdourCanvas::Duple d;
138 if (!gdk_event_get_coords (event, &d.x, &d.y)) {
142 /* event coordinates are in window units, so convert to canvas
145 d = _track_canvas->window_to_canvas (d);
155 return pixel_to_sample (d.x);
159 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
164 /* event coordinates are already in canvas units */
166 if (!gdk_event_get_coords (event, &x, &y)) {
167 cerr << "!NO c COORDS for event type " << event->type << endl;
179 /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel
180 position is negative (as can be the case with motion events in particular),
181 the frame location is always positive.
184 return pixel_to_sample_from_event (x);
188 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
190 boost::shared_ptr<Trimmable> st = _trimmable.lock();
192 if (!st || st == t) {
198 Editor::set_current_movable (boost::shared_ptr<Movable> m)
200 boost::shared_ptr<Movable> sm = _movable.lock();
202 if (!sm || sm != m) {
208 Editor::mouse_mode_object_range_toggled()
210 MouseMode m = mouse_mode;
212 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
214 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
217 if (tact->get_active()) {
218 m = MouseObject; //Smart mode turned to ON, force editing to Object mode
221 set_mouse_mode(m, true); //call this so the button styles can get updated
224 static Glib::RefPtr<Action>
225 get_mouse_mode_action(MouseMode m)
229 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
231 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
233 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
235 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
237 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
239 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-content"));
241 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
243 return Glib::RefPtr<Action>();
247 Editor::set_mouse_mode (MouseMode m, bool force)
249 if (_drags->active ()) {
253 if (!force && m == mouse_mode) {
257 if (ARDOUR::Profile->get_mixbus()) {
258 if ( m == MouseCut) m = MouseObject;
261 Glib::RefPtr<Action> act = get_mouse_mode_action(m);
262 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
264 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
265 tact->set_active (false);
266 tact->set_active (true);
268 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
272 Editor::mouse_mode_toggled (MouseMode m)
274 if (ARDOUR::Profile->get_mixbus()) {
275 if ( m == MouseCut) m = MouseObject;
278 Glib::RefPtr<Action> act = get_mouse_mode_action(m);
279 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
281 if (!tact->get_active()) {
282 /* this was just the notification that the old mode has been
283 * left. we'll get called again with the new mode active in a
289 if (_session && mouse_mode == MouseAudition) {
290 /* stop transport and reset default speed to avoid oddness with
292 _session->request_transport_speed (0.0, true);
295 const bool was_internal = internal_editing();
299 /* Switch snap type/mode if we're moving to/from an internal tool. Note
300 this must toggle the actions and not call set_snap_*() directly,
301 otherwise things get out of sync and the combo box stops working. */
302 if (!was_internal && internal_editing()) {
303 snap_type_action(internal_snap_type)->set_active(true);
304 snap_mode_action(internal_snap_mode)->set_active(true);
305 } else if (was_internal && !internal_editing()) {
306 snap_type_action(pre_internal_snap_type)->set_active(true);
307 snap_mode_action(pre_internal_snap_mode)->set_active(true);
312 /* this should generate a new enter event which will
313 trigger the appropiate cursor.
317 _track_canvas->re_enter ();
320 set_gain_envelope_visibility ();
322 update_time_selection_display ();
324 update_all_enter_cursors ();
326 MouseModeChanged (); /* EMIT SIGNAL */
330 Editor::internal_editing() const
332 return mouse_mode == Editing::MouseContent || mouse_mode == Editing::MouseDraw;
336 Editor::update_time_selection_display ()
338 switch (mouse_mode) {
340 selection->clear_objects ();
341 selection->ClearMidiNoteSelection(); //signal
344 selection->clear_objects ();
345 selection->clear_time ();
346 selection->clear_tracks ();
347 selection->ClearMidiNoteSelection(); //signal
351 //if we go into internal editing, clear everything in the outside world
352 selection->clear_objects ();
353 selection->clear_time ();
354 selection->clear_tracks ();
358 selection->clear_objects ();
359 selection->clear_time ();
360 selection->clear_tracks ();
366 Editor::step_mouse_mode (bool next)
368 const int n_mouse_modes = (int)MouseContent + 1;
369 int current = (int)current_mouse_mode();
371 set_mouse_mode((MouseMode)((current + 1) % n_mouse_modes));
373 set_mouse_mode((MouseMode)((current + n_mouse_modes - 1) % n_mouse_modes));
378 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
380 /* in object/audition/timefx/gain-automation mode,
381 any button press sets the selection if the object
382 can be selected. this is a bit of hack, because
383 we want to avoid this if the mouse operation is a
386 note: not dbl-click or triple-click
388 Also note that there is no region selection in internal edit mode, otherwise
389 for operations operating on the selection (e.g. cut) it is not obvious whether
390 to cut notes or regions.
393 MouseMode eff_mouse_mode = effective_mouse_mode ();
395 if (eff_mouse_mode == MouseCut) {
396 /* never change selection in cut mode */
400 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
401 /* context clicks are always about object properties, even if
402 we're in range mode within smart mode.
404 eff_mouse_mode = MouseObject;
407 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
408 if (get_smart_mode()) {
410 case FadeInHandleItem:
411 case FadeInTrimHandleItem:
412 case FadeOutHandleItem:
413 case FadeOutTrimHandleItem:
414 eff_mouse_mode = MouseObject;
421 if (((mouse_mode != MouseObject) &&
422 (mouse_mode != MouseAudition || item_type != RegionItem) &&
423 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
424 (mouse_mode != MouseDraw)) ||
425 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
429 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
431 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
433 /* almost no selection action on modified button-2 or button-3 events */
435 if (item_type != RegionItem && event->button.button != 2) {
441 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
442 bool press = (event->type == GDK_BUTTON_PRESS);
445 _mouse_changed_selection = false;
451 if (eff_mouse_mode != MouseRange) {
452 _mouse_changed_selection = set_selected_regionview_from_click (press, op);
454 /* don't change the selection unless the
455 clicked track is not currently selected. if
456 so, "collapse" the selection to just this
459 if (!selection->selected (clicked_axisview)) {
460 set_selected_track_as_side_effect (Selection::Set);
464 if (eff_mouse_mode != MouseRange) {
465 _mouse_changed_selection = set_selected_regionview_from_click (press, op);
470 case RegionViewNameHighlight:
472 case LeftFrameHandle:
473 case RightFrameHandle:
474 case FadeInHandleItem:
475 case FadeInTrimHandleItem:
477 case FadeOutHandleItem:
478 case FadeOutTrimHandleItem:
480 case StartCrossFadeItem:
481 case EndCrossFadeItem:
482 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
483 _mouse_changed_selection = set_selected_regionview_from_click (press, op);
484 } else if (event->type == GDK_BUTTON_PRESS) {
485 set_selected_track_as_side_effect (op);
489 case ControlPointItem:
490 set_selected_track_as_side_effect (op);
491 if (eff_mouse_mode != MouseRange) {
492 _mouse_changed_selection = set_selected_control_point_from_click (press, op);
497 /* for context click, select track */
498 if (event->button.button == 3) {
499 selection->clear_tracks ();
500 set_selected_track_as_side_effect (op);
501 _mouse_changed_selection = true;
505 case AutomationTrackItem:
506 set_selected_track_as_side_effect (op);
513 if ((!press) && _mouse_changed_selection) {
514 begin_reversible_selection_op (_("Button Selection"));
515 commit_reversible_selection_op ();
516 _mouse_changed_selection = false;
521 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
523 /* single mouse clicks on any of these item types operate
524 independent of mouse mode, mostly because they are
525 not on the main track canvas or because we want
529 NoteBase* note = NULL;
532 case PlayheadCursorItem:
533 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
537 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
538 hide_marker (item, event);
540 _drags->set (new MarkerDrag (this, item), event);
544 case TempoMarkerItem:
547 new TempoMarkerDrag (
550 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
557 case MeterMarkerItem:
560 new MeterMarkerDrag (
563 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
571 _drags->set (new VideoTimeLineDrag (this, item), event);
578 case TimecodeRulerItem:
579 case SamplesRulerItem:
580 case MinsecRulerItem:
582 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
583 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
589 case RangeMarkerBarItem:
590 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
591 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
592 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
593 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
595 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
600 case CdMarkerBarItem:
601 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
602 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
604 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
609 case TransportMarkerBarItem:
610 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
611 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
613 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
622 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
623 /* special case: allow trim of range selections in joined object mode;
624 in theory eff should equal MouseRange in this case, but it doesn't
625 because entering the range selection canvas item results in entered_regionview
626 being set to 0, so update_join_object_range_location acts as if we aren't
629 if (item_type == StartSelectionTrimItem) {
630 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
631 } else if (item_type == EndSelectionTrimItem) {
632 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
636 Editing::MouseMode eff = effective_mouse_mode ();
638 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
639 if (get_smart_mode()) {
641 case FadeInHandleItem:
642 case FadeInTrimHandleItem:
643 case FadeOutHandleItem:
644 case FadeOutTrimHandleItem:
655 case StartSelectionTrimItem:
656 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
659 case EndSelectionTrimItem:
660 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
664 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
665 start_selection_grab (item, event);
667 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
668 /* grab selection for moving */
669 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
671 /* this was debated, but decided the more common action was to
672 make a new selection */
673 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
678 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
679 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
681 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
686 case RegionViewNameHighlight:
687 if (!clicked_regionview->region()->locked()) {
688 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
694 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
695 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
697 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
706 case FadeInHandleItem:
707 case FadeOutHandleItem:
708 case LeftFrameHandle:
709 case RightFrameHandle:
710 case FeatureLineItem:
711 case RegionViewNameHighlight:
714 case AutomationTrackItem:
715 _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, get_canvas_cursor());
726 /* Existing note: allow trimming/motion */
727 if ((note = reinterpret_cast<NoteBase*> (item->get_data ("notebase")))) {
728 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
729 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
731 _drags->set (new NoteDrag (this, item), event);
736 case ControlPointItem:
737 _drags->set (new ControlPointDrag (this, item), event);
742 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
743 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
748 case AutomationTrackItem:
749 /* rubberband drag to select automation points */
750 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
760 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
761 event->type == GDK_BUTTON_PRESS) {
763 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
765 } else if (event->type == GDK_BUTTON_PRESS) {
768 case FadeInHandleItem:
770 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
774 case FadeOutHandleItem:
776 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
780 case StartCrossFadeItem:
781 case EndCrossFadeItem:
782 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
783 // if (!clicked_regionview->region()->locked()) {
784 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
789 case FeatureLineItem:
791 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
792 remove_transient(item);
796 _drags->set (new FeatureLineDrag (this, item), event);
802 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
803 /* click on an automation region view; do nothing here and let the ARV's signal handler
809 /* click on a normal region view */
810 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
811 add_region_copy_drag (item, event, clicked_regionview);
812 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
813 add_region_brush_drag (item, event, clicked_regionview);
815 add_region_drag (item, event, clicked_regionview);
819 _drags->start_grab (event);
823 case RegionViewNameHighlight:
824 case LeftFrameHandle:
825 case RightFrameHandle:
826 if (!clicked_regionview->region()->locked()) {
827 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
832 case FadeInTrimHandleItem:
833 case FadeOutTrimHandleItem:
834 if (!clicked_regionview->region()->locked()) {
835 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
842 /* rename happens on edit clicks */
843 if (clicked_regionview->get_name_highlight()) {
844 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
850 case ControlPointItem:
851 _drags->set (new ControlPointDrag (this, item), event);
855 case AutomationLineItem:
856 _drags->set (new LineDrag (this, item), event);
861 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
864 case AutomationTrackItem:
866 TimeAxisView* parent = clicked_axisview->get_parent ();
867 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
869 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
871 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
873 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
874 if (pl->n_regions() == 0) {
875 /* Parent has no regions; create one so that we have somewhere to put automation */
876 _drags->set (new RegionCreateDrag (this, item, parent), event);
878 /* See if there's a region before the click that we can extend, and extend it if so */
879 framepos_t const t = canvas_event_sample (event);
880 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
882 _drags->set (new RegionCreateDrag (this, item, parent), event);
884 prev->set_length (t - prev->position ());
888 /* rubberband drag to select automation points */
889 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
913 _drags->set (new LineDrag (this, item), event);
916 case ControlPointItem:
917 _drags->set (new ControlPointDrag (this, item), event);
923 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
925 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
927 double const y = event->button.y;
928 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
930 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
932 /* smart "join" mode: drag automation */
933 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
941 case AutomationLineItem:
942 _drags->set (new LineDrag (this, item), event);
946 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
947 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
948 /* Note is big and pointer is near the end, trim */
949 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
952 _drags->set (new NoteDrag (this, item), event);
959 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
960 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
971 if (item_type == NoteItem) {
972 /* resize-drag notes */
973 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
974 if (note->big_enough_to_trim()) {
975 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
979 } else if (clicked_regionview) {
981 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
987 _drags->set (new ScrubDrag (this, item), event, _cursors->transparent);
989 scrub_reverse_distance = 0;
990 last_scrub_x = event->button.x;
991 scrubbing_direction = 0;
1003 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1005 Editing::MouseMode const eff = effective_mouse_mode ();
1008 switch (item_type) {
1010 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1011 add_region_copy_drag (item, event, clicked_regionview);
1013 add_region_drag (item, event, clicked_regionview);
1015 _drags->start_grab (event);
1018 case ControlPointItem:
1019 _drags->set (new ControlPointDrag (this, item), event);
1027 switch (item_type) {
1028 case RegionViewNameHighlight:
1029 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1033 case LeftFrameHandle:
1034 case RightFrameHandle:
1035 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1039 case RegionViewName:
1040 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1054 /* relax till release */
1066 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1068 if (event->type == GDK_2BUTTON_PRESS) {
1069 _drags->mark_double_click ();
1070 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1074 if (event->type != GDK_BUTTON_PRESS) {
1078 _track_canvas->grab_focus();
1080 if (_session && _session->actively_recording()) {
1084 button_selection (item, event, item_type);
1086 if (!_drags->active () &&
1087 (Keyboard::is_delete_event (&event->button) ||
1088 Keyboard::is_context_menu_event (&event->button) ||
1089 Keyboard::is_edit_event (&event->button))) {
1091 /* handled by button release */
1095 //not rolling, range mode click + join_play_range : locate the PH here
1096 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && ARDOUR_UI::config()->get_follow_edits() ) {
1097 framepos_t where = canvas_event_sample (event);
1099 _session->request_locate (where, false);
1102 switch (event->button.button) {
1104 return button_press_handler_1 (item, event, item_type);
1108 return button_press_handler_2 (item, event, item_type);
1115 return button_press_dispatch (&event->button);
1124 Editor::button_press_dispatch (GdkEventButton* ev)
1126 /* this function is intended only for buttons 4 and above.
1129 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1130 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1134 Editor::button_release_dispatch (GdkEventButton* ev)
1136 /* this function is intended only for buttons 4 and above.
1139 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1140 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1144 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1146 framepos_t where = canvas_event_sample (event);
1147 AutomationTimeAxisView* atv = 0;
1149 _press_cursor_ctx.reset();
1151 /* no action if we're recording */
1153 if (_session && _session->actively_recording()) {
1157 /* see if we're finishing a drag */
1159 bool were_dragging = false;
1160 if (_drags->active ()) {
1161 bool const r = _drags->end_grab (event);
1163 /* grab dragged, so do nothing else */
1167 were_dragging = true;
1170 update_region_layering_order_editor ();
1172 /* edit events get handled here */
1174 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1175 switch (item_type) {
1177 show_region_properties ();
1180 case TempoMarkerItem: {
1182 TempoMarker* tempo_marker;
1184 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1185 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1186 abort(); /*NOTREACHED*/
1189 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1190 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1191 abort(); /*NOTREACHED*/
1194 edit_tempo_marker (*tempo_marker);
1198 case MeterMarkerItem: {
1200 MeterMarker* meter_marker;
1202 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1203 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1204 abort(); /*NOTREACHED*/
1207 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1208 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1209 abort(); /*NOTREACHED*/
1211 edit_meter_marker (*meter_marker);
1215 case RegionViewName:
1216 if (clicked_regionview->name_active()) {
1217 return mouse_rename_region (item, event);
1221 case ControlPointItem:
1222 edit_control_point (item);
1231 /* context menu events get handled here */
1232 if (Keyboard::is_context_menu_event (&event->button)) {
1234 context_click_event = *event;
1236 if (!_drags->active ()) {
1238 /* no matter which button pops up the context menu, tell the menu
1239 widget to use button 1 to drive menu selection.
1242 switch (item_type) {
1244 case FadeInHandleItem:
1245 case FadeInTrimHandleItem:
1246 case StartCrossFadeItem:
1247 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1251 case FadeOutHandleItem:
1252 case FadeOutTrimHandleItem:
1253 case EndCrossFadeItem:
1254 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1257 case LeftFrameHandle:
1258 case RightFrameHandle:
1262 popup_track_context_menu (1, event->button.time, item_type, false);
1266 case RegionViewNameHighlight:
1267 case RegionViewName:
1268 popup_track_context_menu (1, event->button.time, item_type, false);
1272 popup_track_context_menu (1, event->button.time, item_type, true);
1275 case AutomationTrackItem:
1276 popup_track_context_menu (1, event->button.time, item_type, false);
1280 case RangeMarkerBarItem:
1281 case TransportMarkerBarItem:
1282 case CdMarkerBarItem:
1286 case TimecodeRulerItem:
1287 case SamplesRulerItem:
1288 case MinsecRulerItem:
1290 popup_ruler_menu (where, item_type);
1294 marker_context_menu (&event->button, item);
1297 case TempoMarkerItem:
1298 tempo_or_meter_marker_context_menu (&event->button, item);
1301 case MeterMarkerItem:
1302 tempo_or_meter_marker_context_menu (&event->button, item);
1305 case CrossfadeViewItem:
1306 popup_track_context_menu (1, event->button.time, item_type, false);
1309 case ControlPointItem:
1310 popup_control_point_context_menu (item, event);
1314 if (internal_editing()) {
1315 popup_note_context_menu (item, event);
1327 /* delete events get handled here */
1329 Editing::MouseMode const eff = effective_mouse_mode ();
1331 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1333 switch (item_type) {
1334 case TempoMarkerItem:
1335 remove_tempo_marker (item);
1338 case MeterMarkerItem:
1339 remove_meter_marker (item);
1343 remove_marker (*item, event);
1347 if (eff == MouseObject) {
1348 remove_clicked_region ();
1352 case ControlPointItem:
1353 remove_control_point (item);
1357 remove_midi_note (item, event);
1366 switch (event->button.button) {
1369 switch (item_type) {
1370 /* see comments in button_press_handler */
1371 case PlayheadCursorItem:
1374 case AutomationLineItem:
1375 case StartSelectionTrimItem:
1376 case EndSelectionTrimItem:
1380 if (!_dragging_playhead) {
1381 snap_to_with_modifier (where, event, RoundNearest, true);
1382 mouse_add_new_marker (where);
1386 case CdMarkerBarItem:
1387 if (!_dragging_playhead) {
1388 // if we get here then a dragged range wasn't done
1389 snap_to_with_modifier (where, event, RoundNearest, true);
1390 mouse_add_new_marker (where, true);
1395 if (!_dragging_playhead) {
1396 snap_to_with_modifier (where, event);
1397 mouse_add_new_tempo_event (where);
1402 if (!_dragging_playhead) {
1403 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1408 case TimecodeRulerItem:
1409 case SamplesRulerItem:
1410 case MinsecRulerItem:
1421 switch (item_type) {
1424 /* check that we didn't drag before releasing, since
1425 its really annoying to create new control
1426 points when doing this.
1428 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1429 if (!were_dragging && arv) {
1430 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1431 arv->add_gain_point_event (item, event, with_guard_points);
1437 case AutomationTrackItem: {
1438 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1439 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1441 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1452 if (scrubbing_direction == 0) {
1453 /* no drag, just a click */
1454 switch (item_type) {
1456 play_selected_region ();
1462 /* make sure we stop */
1463 _session->request_transport_speed (0.0);
1472 /* do any (de)selection operations that should occur on button release */
1473 button_selection (item, event, item_type);
1483 switch (item_type) {
1485 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1487 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1490 // Button2 click is unused
1505 // x_style_paste (where, 1.0);
1526 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1533 /* by the time we reach here, entered_regionview and entered trackview
1534 * will have already been set as appropriate. Things are done this
1535 * way because this method isn't passed a pointer to a variable type of
1536 * thing that is entered (which may or may not be canvas item).
1537 * (e.g. the actual entered regionview)
1540 choose_canvas_cursor_on_entry (item_type);
1542 switch (item_type) {
1543 case ControlPointItem:
1544 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1545 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1548 fraction = 1.0 - (cp->get_y() / cp->line().height());
1550 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1551 _verbose_cursor->show ();
1556 if (mouse_mode == MouseDraw) {
1557 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1559 line->set_outline_color (ARDOUR_UI::config()->color ("entered gain line"));
1564 case AutomationLineItem:
1565 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1566 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1568 line->set_outline_color (ARDOUR_UI::config()->color ("entered automation line"));
1573 case AutomationTrackItem:
1574 AutomationTimeAxisView* atv;
1575 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1576 clear_entered_track = false;
1577 set_entered_track (atv);
1582 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1585 entered_marker = marker;
1586 marker->set_color_rgba (ARDOUR_UI::config()->color ("entered marker"));
1588 case MeterMarkerItem:
1589 case TempoMarkerItem:
1592 case FadeInHandleItem:
1593 case FadeInTrimHandleItem:
1594 if (mouse_mode == MouseObject) {
1595 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1597 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1598 rect->set_fill_color (rv->get_fill_color());
1603 case FadeOutHandleItem:
1604 case FadeOutTrimHandleItem:
1605 if (mouse_mode == MouseObject) {
1606 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1608 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1609 rect->set_fill_color (rv->get_fill_color ());
1614 case FeatureLineItem:
1616 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1617 line->set_outline_color (0xFF0000FF);
1628 /* third pass to handle entered track status in a comprehensible way.
1631 switch (item_type) {
1633 case AutomationLineItem:
1634 case ControlPointItem:
1635 /* these do not affect the current entered track state */
1636 clear_entered_track = false;
1639 case AutomationTrackItem:
1640 /* handled above already */
1652 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1660 if (!_enter_stack.empty()) {
1661 _enter_stack.pop_back();
1664 switch (item_type) {
1665 case ControlPointItem:
1666 _verbose_cursor->hide ();
1670 case AutomationLineItem:
1671 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1673 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1675 line->set_outline_color (al->get_line_color());
1681 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1685 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1686 location_flags_changed (loc);
1689 case MeterMarkerItem:
1690 case TempoMarkerItem:
1693 case FadeInTrimHandleItem:
1694 case FadeOutTrimHandleItem:
1695 case FadeInHandleItem:
1696 case FadeOutHandleItem:
1698 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1700 rect->set_fill_color (ARDOUR_UI::config()->color ("inactive fade handle"));
1705 case AutomationTrackItem:
1708 case FeatureLineItem:
1710 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1711 line->set_outline_color (ARDOUR_UI::config()->color ("zero line"));
1723 Editor::scrub (framepos_t frame, double current_x)
1727 if (scrubbing_direction == 0) {
1729 _session->request_locate (frame, false);
1730 _session->request_transport_speed (0.1);
1731 scrubbing_direction = 1;
1735 if (last_scrub_x > current_x) {
1737 /* pointer moved to the left */
1739 if (scrubbing_direction > 0) {
1741 /* we reversed direction to go backwards */
1744 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1748 /* still moving to the left (backwards) */
1750 scrub_reversals = 0;
1751 scrub_reverse_distance = 0;
1753 delta = 0.01 * (last_scrub_x - current_x);
1754 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1758 /* pointer moved to the right */
1760 if (scrubbing_direction < 0) {
1761 /* we reversed direction to go forward */
1764 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1767 /* still moving to the right */
1769 scrub_reversals = 0;
1770 scrub_reverse_distance = 0;
1772 delta = 0.01 * (current_x - last_scrub_x);
1773 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1777 /* if there have been more than 2 opposite motion moves detected, or one that moves
1778 back more than 10 pixels, reverse direction
1781 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1783 if (scrubbing_direction > 0) {
1784 /* was forwards, go backwards */
1785 _session->request_transport_speed (-0.1);
1786 scrubbing_direction = -1;
1788 /* was backwards, go forwards */
1789 _session->request_transport_speed (0.1);
1790 scrubbing_direction = 1;
1793 scrub_reverse_distance = 0;
1794 scrub_reversals = 0;
1798 last_scrub_x = current_x;
1802 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1804 _last_motion_y = event->motion.y;
1806 if (event->motion.is_hint) {
1809 /* We call this so that MOTION_NOTIFY events continue to be
1810 delivered to the canvas. We need to do this because we set
1811 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1812 the density of the events, at the expense of a round-trip
1813 to the server. Given that this will mostly occur on cases
1814 where DISPLAY = :0.0, and given the cost of what the motion
1815 event might do, its a good tradeoff.
1818 _track_canvas->get_pointer (x, y);
1821 if (current_stepping_trackview) {
1822 /* don't keep the persistent stepped trackview if the mouse moves */
1823 current_stepping_trackview = 0;
1824 step_timeout.disconnect ();
1827 if (_session && _session->actively_recording()) {
1828 /* Sorry. no dragging stuff around while we record */
1832 update_join_object_range_location (event->motion.y);
1834 if (_drags->active ()) {
1835 return _drags->motion_handler (event, from_autoscroll);
1842 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1844 ControlPoint* control_point;
1846 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1847 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1848 abort(); /*NOTREACHED*/
1851 AutomationLine& line = control_point->line ();
1852 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
1853 /* we shouldn't remove the first or last gain point in region gain lines */
1854 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
1863 Editor::remove_control_point (ArdourCanvas::Item* item)
1865 if (!can_remove_control_point (item)) {
1869 ControlPoint* control_point;
1871 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1872 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1873 abort(); /*NOTREACHED*/
1876 control_point->line().remove_point (*control_point);
1880 Editor::edit_control_point (ArdourCanvas::Item* item)
1882 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1885 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1886 abort(); /*NOTREACHED*/
1889 ControlPointDialog d (p);
1892 if (d.run () != RESPONSE_ACCEPT) {
1896 p->line().modify_point_y (*p, d.get_y_fraction ());
1900 Editor::edit_notes (MidiRegionView* mrv)
1902 MidiRegionView::Selection const & s = mrv->selection();
1908 EditNoteDialog* d = new EditNoteDialog (mrv, s);
1912 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
1916 Editor::note_edit_done (int r, EditNoteDialog* d)
1923 Editor::visible_order_range (int* low, int* high) const
1925 *low = TimeAxisView::max_order ();
1928 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1930 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1932 if (!rtv->hidden()) {
1934 if (*high < rtv->order()) {
1935 *high = rtv->order ();
1938 if (*low > rtv->order()) {
1939 *low = rtv->order ();
1946 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1948 /* Either add to or set the set the region selection, unless
1949 this is an alignment click (control used)
1952 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1953 TimeAxisView* tv = &rv.get_time_axis_view();
1954 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1956 if (rtv && rtv->is_track()) {
1957 speed = rtv->track()->speed();
1960 framepos_t where = get_preferred_edit_position();
1964 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1966 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
1968 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1970 align_region (rv.region(), End, (framepos_t) (where * speed));
1974 align_region (rv.region(), Start, (framepos_t) (where * speed));
1981 Editor::collect_new_region_view (RegionView* rv)
1983 latest_regionviews.push_back (rv);
1987 Editor::collect_and_select_new_region_view (RegionView* rv)
1990 latest_regionviews.push_back (rv);
1994 Editor::cancel_selection ()
1996 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1997 (*i)->hide_selection ();
2000 selection->clear ();
2001 clicked_selection = 0;
2005 Editor::cancel_time_selection ()
2007 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2008 (*i)->hide_selection ();
2010 selection->time.clear ();
2011 clicked_selection = 0;
2015 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2017 RegionView* rv = clicked_regionview;
2019 /* Choose action dependant on which button was pressed */
2020 switch (event->button.button) {
2022 begin_reversible_command (_("start point trim"));
2024 if (selection->selected (rv)) {
2025 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2026 i != selection->regions.by_layer().end(); ++i)
2028 if (!(*i)->region()->locked()) {
2029 (*i)->region()->clear_changes ();
2030 (*i)->region()->trim_front (new_bound);
2031 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2036 if (!rv->region()->locked()) {
2037 rv->region()->clear_changes ();
2038 rv->region()->trim_front (new_bound);
2039 _session->add_command(new StatefulDiffCommand (rv->region()));
2043 commit_reversible_command();
2047 begin_reversible_command (_("End point trim"));
2049 if (selection->selected (rv)) {
2051 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2053 if (!(*i)->region()->locked()) {
2054 (*i)->region()->clear_changes();
2055 (*i)->region()->trim_end (new_bound);
2056 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2062 if (!rv->region()->locked()) {
2063 rv->region()->clear_changes ();
2064 rv->region()->trim_end (new_bound);
2065 _session->add_command (new StatefulDiffCommand (rv->region()));
2069 commit_reversible_command();
2078 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2083 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2084 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2085 abort(); /*NOTREACHED*/
2088 Location* location = find_location_from_marker (marker, is_start);
2089 location->set_hidden (true, this);
2093 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2095 using namespace Gtkmm2ext;
2097 ArdourPrompter prompter (false);
2099 prompter.set_prompt (_("Name for region:"));
2100 prompter.set_initial_text (clicked_regionview->region()->name());
2101 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2102 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2103 prompter.show_all ();
2104 switch (prompter.run ()) {
2105 case Gtk::RESPONSE_ACCEPT:
2107 prompter.get_result(str);
2109 clicked_regionview->region()->set_name (str);
2118 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2120 /* no brushing without a useful snap setting */
2122 switch (_snap_mode) {
2124 return; /* can't work because it allows region to be placed anywhere */
2129 switch (_snap_type) {
2137 /* don't brush a copy over the original */
2139 if (pos == rv->region()->position()) {
2143 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2145 if (rtv == 0 || !rtv->is_track()) {
2149 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2150 double speed = rtv->track()->speed();
2152 playlist->clear_changes ();
2153 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2154 playlist->add_region (new_region, (framepos_t) (pos * speed));
2155 _session->add_command (new StatefulDiffCommand (playlist));
2157 // playlist is frozen, so we have to update manually XXX this is disgusting
2159 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2163 Editor::track_height_step_timeout ()
2165 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2166 current_stepping_trackview = 0;
2173 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2175 assert (region_view);
2177 if (!region_view->region()->playlist()) {
2181 switch (Config->get_edit_mode()) {
2183 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2186 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2189 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2196 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2198 assert (region_view);
2200 if (!region_view->region()->playlist()) {
2204 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2208 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2210 assert (region_view);
2212 if (!region_view->region()->playlist()) {
2216 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2220 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2222 begin_reversible_command (Operations::drag_region_brush);
2225 /** Start a grab where a time range is selected, track(s) are selected, and the
2226 * user clicks and drags a region with a modifier in order to create a new region containing
2227 * the section of the clicked region that lies within the time range.
2230 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2232 if (clicked_regionview == 0) {
2236 /* lets try to create new Region for the selection */
2238 vector<boost::shared_ptr<Region> > new_regions;
2239 create_region_from_selection (new_regions);
2241 if (new_regions.empty()) {
2245 /* XXX fix me one day to use all new regions */
2247 boost::shared_ptr<Region> region (new_regions.front());
2249 /* add it to the current stream/playlist.
2251 tricky: the streamview for the track will add a new regionview. we will
2252 catch the signal it sends when it creates the regionview to
2253 set the regionview we want to then drag.
2256 latest_regionviews.clear();
2257 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2259 /* A selection grab currently creates two undo/redo operations, one for
2260 creating the new region and another for moving it.
2263 begin_reversible_command (Operations::selection_grab);
2265 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2267 playlist->clear_changes ();
2268 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2269 _session->add_command(new StatefulDiffCommand (playlist));
2273 if (latest_regionviews.empty()) {
2274 /* something went wrong */
2278 /* we need to deselect all other regionviews, and select this one
2279 i'm ignoring undo stuff, because the region creation will take care of it
2282 selection->set (latest_regionviews);
2284 commit_reversible_command ();
2286 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2292 if (_drags->active ()) {
2295 selection->clear ();
2301 /** Update _join_object_range_state which indicate whether we are over the top
2302 * or bottom half of a route view, used by the `join object/range' tool
2303 * mode. Coordinates in canvas space.
2306 Editor::update_join_object_range_location (double y)
2308 if (!get_smart_mode()) {
2309 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2313 JoinObjectRangeState const old = _join_object_range_state;
2315 if (mouse_mode == MouseObject) {
2316 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2317 } else if (mouse_mode == MouseRange) {
2318 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2321 if (entered_regionview) {
2323 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2324 double const c = item_space.y / entered_regionview->height();
2326 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2328 Editor::EnterContext* ctx = get_enter_context(RegionItem);
2329 if (_join_object_range_state != old && ctx) {
2330 ctx->cursor_ctx->change(which_track_cursor());
2333 } else if (entered_track) {
2335 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2337 if (entered_route_view) {
2342 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2344 double track_height = entered_route_view->view()->child_height();
2345 if (ARDOUR_UI::config()->get_show_name_highlight()) {
2346 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2348 double const c = cy / track_height;
2352 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2354 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2358 /* Other kinds of tracks use object mode */
2359 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2362 Editor::EnterContext* ctx = get_enter_context(StreamItem);
2363 if (_join_object_range_state != old && ctx) {
2364 ctx->cursor_ctx->change(which_track_cursor());
2370 Editor::effective_mouse_mode () const
2372 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2374 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2382 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2384 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2387 e->region_view().delete_note (e->note ());
2390 /** Obtain the pointer position in canvas coordinates */
2392 Editor::get_pointer_position (double& x, double& y) const
2395 _track_canvas->get_pointer (px, py);
2396 _track_canvas->window_to_canvas (px, py, x, y);