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
225 Editor::set_mouse_mode (MouseMode m, bool force)
227 if (_drags->active ()) {
231 if (!force && m == mouse_mode) {
235 if (ARDOUR::Profile->get_mixbus()) {
236 if ( m == MouseCut) m = MouseObject;
239 Glib::RefPtr<Action> act;
243 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
247 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
251 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
255 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
259 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
263 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-content"));
267 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
273 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
276 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
277 tact->set_active (false);
278 tact->set_active (true);
280 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
284 Editor::mouse_mode_toggled (MouseMode m)
286 Glib::RefPtr<Action> act;
287 Glib::RefPtr<ToggleAction> tact;
289 if (ARDOUR::Profile->get_mixbus()) {
290 if ( m == MouseCut) m = MouseObject;
295 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
299 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
303 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
307 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
311 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
315 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-content"));
319 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
325 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
328 if (!tact->get_active()) {
329 /* this was just the notification that the old mode has been
330 * left. we'll get called again with the new mode active in a
336 if (_session && mouse_mode == MouseAudition) {
337 /* stop transport and reset default speed to avoid oddness with
339 _session->request_transport_speed (0.0, true);
346 /* this should generate a new enter event which will
347 trigger the appropiate cursor.
351 _track_canvas->re_enter ();
354 set_gain_envelope_visibility ();
356 update_time_selection_display ();
358 MouseModeChanged (); /* EMIT SIGNAL */
362 Editor::internal_editing() const
364 return mouse_mode == Editing::MouseContent || mouse_mode == Editing::MouseDraw;
368 Editor::update_time_selection_display ()
370 switch (mouse_mode) {
372 selection->clear_objects ();
375 selection->clear_time ();
381 Editor::step_mouse_mode (bool next)
383 const int n_mouse_modes = (int)MouseContent + 1;
384 int current = (int)current_mouse_mode();
386 set_mouse_mode((MouseMode)((current + 1) % n_mouse_modes));
388 set_mouse_mode((MouseMode)((current + n_mouse_modes - 1) % n_mouse_modes));
393 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
395 /* in object/audition/timefx/gain-automation mode,
396 any button press sets the selection if the object
397 can be selected. this is a bit of hack, because
398 we want to avoid this if the mouse operation is a
401 note: not dbl-click or triple-click
403 Also note that there is no region selection in internal edit mode, otherwise
404 for operations operating on the selection (e.g. cut) it is not obvious whether
405 to cut notes or regions.
408 MouseMode eff_mouse_mode = effective_mouse_mode ();
410 if (eff_mouse_mode == MouseCut) {
411 /* never change selection in cut mode */
415 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
416 /* context clicks are always about object properties, even if
417 we're in range mode within smart mode.
419 eff_mouse_mode = MouseObject;
422 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
423 if (get_smart_mode()) {
425 case FadeInHandleItem:
426 case FadeInTrimHandleItem:
427 case FadeOutHandleItem:
428 case FadeOutTrimHandleItem:
429 eff_mouse_mode = MouseObject;
436 if (((mouse_mode != MouseObject) &&
437 (mouse_mode != MouseAudition || item_type != RegionItem) &&
438 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
439 (mouse_mode != MouseDraw)) ||
440 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
444 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
446 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
448 /* almost no selection action on modified button-2 or button-3 events */
450 if (item_type != RegionItem && event->button.button != 2) {
456 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
457 bool press = (event->type == GDK_BUTTON_PRESS);
462 if (eff_mouse_mode != MouseRange) {
463 set_selected_regionview_from_click (press, op);
465 /* don't change the selection unless the
466 clicked track is not currently selected. if
467 so, "collapse" the selection to just this
470 if (!selection->selected (clicked_axisview)) {
471 set_selected_track_as_side_effect (Selection::Set);
475 if (eff_mouse_mode != MouseRange) {
476 set_selected_regionview_from_click (press, op);
481 case RegionViewNameHighlight:
483 case LeftFrameHandle:
484 case RightFrameHandle:
485 case FadeInHandleItem:
486 case FadeInTrimHandleItem:
488 case FadeOutHandleItem:
489 case FadeOutTrimHandleItem:
491 case StartCrossFadeItem:
492 case EndCrossFadeItem:
493 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
494 set_selected_regionview_from_click (press, op);
495 } else if (event->type == GDK_BUTTON_PRESS) {
496 set_selected_track_as_side_effect (op);
500 case ControlPointItem:
501 set_selected_track_as_side_effect (op);
502 if (eff_mouse_mode != MouseRange) {
503 set_selected_control_point_from_click (press, op);
508 /* for context click, select track */
509 if (event->button.button == 3) {
510 selection->clear_tracks ();
511 set_selected_track_as_side_effect (op);
515 case AutomationTrackItem:
516 set_selected_track_as_side_effect (op);
525 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
527 /* single mouse clicks on any of these item types operate
528 independent of mouse mode, mostly because they are
529 not on the main track canvas or because we want
533 NoteBase* note = NULL;
536 case PlayheadCursorItem:
537 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
541 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
542 hide_marker (item, event);
544 _drags->set (new MarkerDrag (this, item), event);
548 case TempoMarkerItem:
551 new TempoMarkerDrag (
554 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
561 case MeterMarkerItem:
564 new MeterMarkerDrag (
567 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
575 _drags->set (new VideoTimeLineDrag (this, item), event);
582 case TimecodeRulerItem:
583 case SamplesRulerItem:
584 case MinsecRulerItem:
586 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
587 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
593 case RangeMarkerBarItem:
594 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
595 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
596 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
597 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
599 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
604 case CdMarkerBarItem:
605 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
606 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
608 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
613 case TransportMarkerBarItem:
614 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
615 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
617 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
626 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
627 /* special case: allow trim of range selections in joined object mode;
628 in theory eff should equal MouseRange in this case, but it doesn't
629 because entering the range selection canvas item results in entered_regionview
630 being set to 0, so update_join_object_range_location acts as if we aren't
633 if (item_type == StartSelectionTrimItem) {
634 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
635 } else if (item_type == EndSelectionTrimItem) {
636 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
640 Editing::MouseMode eff = effective_mouse_mode ();
642 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
643 if (get_smart_mode()) {
645 case FadeInHandleItem:
646 case FadeInTrimHandleItem:
647 case FadeOutHandleItem:
648 case FadeOutTrimHandleItem:
659 case StartSelectionTrimItem:
660 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
663 case EndSelectionTrimItem:
664 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
668 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
669 start_selection_grab (item, event);
671 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
672 /* grab selection for moving */
673 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
675 /* this was debated, but decided the more common action was to
676 make a new selection */
677 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
682 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
683 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
685 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
690 case RegionViewNameHighlight:
691 if (!clicked_regionview->region()->locked()) {
692 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
698 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
699 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
701 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
710 case FadeInHandleItem:
711 case FadeOutHandleItem:
712 case LeftFrameHandle:
713 case RightFrameHandle:
714 case FeatureLineItem:
715 case RegionViewNameHighlight:
718 case AutomationTrackItem:
719 _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, current_canvas_cursor);
730 /* Existing note: allow trimming/motion */
731 if ((note = reinterpret_cast<NoteBase*> (item->get_data ("notebase")))) {
732 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
733 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
735 _drags->set (new NoteDrag (this, item), event);
740 case ControlPointItem:
741 _drags->set (new ControlPointDrag (this, item), event);
746 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
747 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
752 case AutomationTrackItem:
753 /* rubberband drag to select automation points */
754 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
763 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
764 event->type == GDK_BUTTON_PRESS) {
766 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
768 } else if (event->type == GDK_BUTTON_PRESS) {
771 case FadeInHandleItem:
773 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
777 case FadeOutHandleItem:
779 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
783 case StartCrossFadeItem:
784 case EndCrossFadeItem:
785 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
786 // if (!clicked_regionview->region()->locked()) {
787 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
792 case FeatureLineItem:
794 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
795 remove_transient(item);
799 _drags->set (new FeatureLineDrag (this, item), event);
805 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
806 /* click on an automation region view; do nothing here and let the ARV's signal handler
812 /* click on a normal region view */
813 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
814 add_region_copy_drag (item, event, clicked_regionview);
815 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
816 add_region_brush_drag (item, event, clicked_regionview);
818 add_region_drag (item, event, clicked_regionview);
822 _drags->start_grab (event);
826 case RegionViewNameHighlight:
827 case LeftFrameHandle:
828 case RightFrameHandle:
829 if (!clicked_regionview->region()->locked()) {
830 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
835 case FadeInTrimHandleItem:
836 case FadeOutTrimHandleItem:
837 if (!clicked_regionview->region()->locked()) {
838 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
845 /* rename happens on edit clicks */
846 if (clicked_regionview->get_name_highlight()) {
847 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
853 case ControlPointItem:
854 _drags->set (new ControlPointDrag (this, item), event);
858 case AutomationLineItem:
859 _drags->set (new LineDrag (this, item), event);
864 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
867 case AutomationTrackItem:
869 TimeAxisView* parent = clicked_axisview->get_parent ();
870 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
872 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
874 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
876 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
877 if (pl->n_regions() == 0) {
878 /* Parent has no regions; create one so that we have somewhere to put automation */
879 _drags->set (new RegionCreateDrag (this, item, parent), event);
881 /* See if there's a region before the click that we can extend, and extend it if so */
882 framepos_t const t = canvas_event_sample (event);
883 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
885 _drags->set (new RegionCreateDrag (this, item, parent), event);
887 prev->set_length (t - prev->position ());
891 /* rubberband drag to select automation points */
892 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
916 _drags->set (new LineDrag (this, item), event);
919 case ControlPointItem:
920 _drags->set (new ControlPointDrag (this, item), event);
926 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
928 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
930 double const y = event->button.y;
931 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
933 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
935 /* smart "join" mode: drag automation */
936 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
944 case AutomationLineItem:
945 _drags->set (new LineDrag (this, item), event);
949 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
950 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
951 /* Note is big and pointer is near the end, trim */
952 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
955 _drags->set (new NoteDrag (this, item), event);
962 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
963 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
974 if (item_type == NoteItem) {
975 /* resize-drag notes */
976 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
977 if (note->big_enough_to_trim()) {
978 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
982 } else if (clicked_regionview) {
984 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
990 _drags->set (new ScrubDrag (this, item), event);
992 scrub_reverse_distance = 0;
993 last_scrub_x = event->button.x;
994 scrubbing_direction = 0;
995 push_canvas_cursor (_cursors->transparent);
1007 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1009 Editing::MouseMode const eff = effective_mouse_mode ();
1012 switch (item_type) {
1014 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1015 add_region_copy_drag (item, event, clicked_regionview);
1017 add_region_drag (item, event, clicked_regionview);
1019 _drags->start_grab (event);
1022 case ControlPointItem:
1023 _drags->set (new ControlPointDrag (this, item), event);
1031 switch (item_type) {
1032 case RegionViewNameHighlight:
1033 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1037 case LeftFrameHandle:
1038 case RightFrameHandle:
1039 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1043 case RegionViewName:
1044 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1058 /* relax till release */
1070 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1072 if (event->type == GDK_2BUTTON_PRESS) {
1073 _drags->mark_double_click ();
1074 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1078 if (event->type != GDK_BUTTON_PRESS) {
1082 pre_press_cursor = current_canvas_cursor;
1084 _track_canvas->grab_focus();
1086 if (_session && _session->actively_recording()) {
1090 button_selection (item, event, item_type);
1092 if (!_drags->active () &&
1093 (Keyboard::is_delete_event (&event->button) ||
1094 Keyboard::is_context_menu_event (&event->button) ||
1095 Keyboard::is_edit_event (&event->button))) {
1097 /* handled by button release */
1101 //not rolling, range mode click + join_play_range : locate the PH here
1102 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_follow_edits() ) {
1103 framepos_t where = canvas_event_sample (event);
1105 _session->request_locate (where, false);
1108 switch (event->button.button) {
1110 return button_press_handler_1 (item, event, item_type);
1114 return button_press_handler_2 (item, event, item_type);
1121 return button_press_dispatch (&event->button);
1130 Editor::button_press_dispatch (GdkEventButton* ev)
1132 /* this function is intended only for buttons 4 and above.
1135 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1136 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1140 Editor::button_release_dispatch (GdkEventButton* ev)
1142 /* this function is intended only for buttons 4 and above.
1145 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1146 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1150 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1152 framepos_t where = canvas_event_sample (event);
1153 AutomationTimeAxisView* atv = 0;
1155 if (pre_press_cursor) {
1156 set_canvas_cursor (pre_press_cursor);
1157 pre_press_cursor = 0;
1160 /* no action if we're recording */
1162 if (_session && _session->actively_recording()) {
1166 /* see if we're finishing a drag */
1168 bool were_dragging = false;
1169 if (_drags->active ()) {
1170 bool const r = _drags->end_grab (event);
1172 /* grab dragged, so do nothing else */
1176 were_dragging = true;
1179 update_region_layering_order_editor ();
1181 /* edit events get handled here */
1183 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1184 switch (item_type) {
1186 show_region_properties ();
1189 case TempoMarkerItem: {
1191 TempoMarker* tempo_marker;
1193 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1194 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1195 abort(); /*NOTREACHED*/
1198 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1199 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1200 abort(); /*NOTREACHED*/
1203 edit_tempo_marker (*tempo_marker);
1207 case MeterMarkerItem: {
1209 MeterMarker* meter_marker;
1211 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1212 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1213 abort(); /*NOTREACHED*/
1216 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1217 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1218 abort(); /*NOTREACHED*/
1220 edit_meter_marker (*meter_marker);
1224 case RegionViewName:
1225 if (clicked_regionview->name_active()) {
1226 return mouse_rename_region (item, event);
1230 case ControlPointItem:
1231 edit_control_point (item);
1240 /* context menu events get handled here */
1241 if (Keyboard::is_context_menu_event (&event->button)) {
1243 context_click_event = *event;
1245 if (!_drags->active ()) {
1247 /* no matter which button pops up the context menu, tell the menu
1248 widget to use button 1 to drive menu selection.
1251 switch (item_type) {
1253 case FadeInHandleItem:
1254 case FadeInTrimHandleItem:
1255 case StartCrossFadeItem:
1256 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1260 case FadeOutHandleItem:
1261 case FadeOutTrimHandleItem:
1262 case EndCrossFadeItem:
1263 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1266 case LeftFrameHandle:
1267 case RightFrameHandle:
1271 popup_track_context_menu (1, event->button.time, item_type, false);
1275 case RegionViewNameHighlight:
1276 case RegionViewName:
1277 popup_track_context_menu (1, event->button.time, item_type, false);
1281 popup_track_context_menu (1, event->button.time, item_type, true);
1284 case AutomationTrackItem:
1285 popup_track_context_menu (1, event->button.time, item_type, false);
1289 case RangeMarkerBarItem:
1290 case TransportMarkerBarItem:
1291 case CdMarkerBarItem:
1295 case TimecodeRulerItem:
1296 case SamplesRulerItem:
1297 case MinsecRulerItem:
1299 popup_ruler_menu (where, item_type);
1303 marker_context_menu (&event->button, item);
1306 case TempoMarkerItem:
1307 tempo_or_meter_marker_context_menu (&event->button, item);
1310 case MeterMarkerItem:
1311 tempo_or_meter_marker_context_menu (&event->button, item);
1314 case CrossfadeViewItem:
1315 popup_track_context_menu (1, event->button.time, item_type, false);
1318 case ControlPointItem:
1319 popup_control_point_context_menu (item, event);
1330 /* delete events get handled here */
1332 Editing::MouseMode const eff = effective_mouse_mode ();
1334 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1336 switch (item_type) {
1337 case TempoMarkerItem:
1338 remove_tempo_marker (item);
1341 case MeterMarkerItem:
1342 remove_meter_marker (item);
1346 remove_marker (*item, event);
1350 if (eff == MouseObject) {
1351 remove_clicked_region ();
1355 case ControlPointItem:
1356 remove_control_point (item);
1360 remove_midi_note (item, event);
1369 switch (event->button.button) {
1372 switch (item_type) {
1373 /* see comments in button_press_handler */
1374 case PlayheadCursorItem:
1377 case AutomationLineItem:
1378 case StartSelectionTrimItem:
1379 case EndSelectionTrimItem:
1383 if (!_dragging_playhead) {
1384 snap_to_with_modifier (where, event, RoundNearest, true);
1385 mouse_add_new_marker (where);
1389 case CdMarkerBarItem:
1390 if (!_dragging_playhead) {
1391 // if we get here then a dragged range wasn't done
1392 snap_to_with_modifier (where, event, RoundNearest, true);
1393 mouse_add_new_marker (where, true);
1398 if (!_dragging_playhead) {
1399 snap_to_with_modifier (where, event);
1400 mouse_add_new_tempo_event (where);
1405 if (!_dragging_playhead) {
1406 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1411 case TimecodeRulerItem:
1412 case SamplesRulerItem:
1413 case MinsecRulerItem:
1424 switch (item_type) {
1427 /* check that we didn't drag before releasing, since
1428 its really annoying to create new control
1429 points when doing this.
1431 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1432 if (!were_dragging && arv) {
1433 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1434 arv->add_gain_point_event (item, event, with_guard_points);
1440 case AutomationTrackItem: {
1441 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1442 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1444 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1455 pop_canvas_cursor ();
1456 if (scrubbing_direction == 0) {
1457 /* no drag, just a click */
1458 switch (item_type) {
1460 play_selected_region ();
1466 /* make sure we stop */
1467 _session->request_transport_speed (0.0);
1476 /* do any (de)selection operations that should occur on button release */
1477 button_selection (item, event, item_type);
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
1508 // x_style_paste (where, 1.0);
1529 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1536 /* by the time we reach here, entered_regionview and entered trackview
1537 * will have already been set as appropriate. Things are done this
1538 * way because this method isn't passed a pointer to a variable type of
1539 * thing that is entered (which may or may not be canvas item).
1540 * (e.g. the actual entered regionview)
1543 choose_canvas_cursor_on_entry (&event->crossing, item_type);
1545 switch (item_type) {
1546 case ControlPointItem:
1547 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1548 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1551 fraction = 1.0 - (cp->get_y() / cp->line().height());
1553 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1554 _verbose_cursor->show ();
1559 if (mouse_mode == MouseDraw) {
1560 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1562 line->set_outline_color (ARDOUR_UI::config()->color ("entered gain line"));
1567 case AutomationLineItem:
1568 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1569 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1571 line->set_outline_color (ARDOUR_UI::config()->color ("entered automation line"));
1576 case AutomationTrackItem:
1577 AutomationTimeAxisView* atv;
1578 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1579 clear_entered_track = false;
1580 set_entered_track (atv);
1585 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1588 entered_marker = marker;
1589 marker->set_color_rgba (ARDOUR_UI::config()->color ("entered marker"));
1591 case MeterMarkerItem:
1592 case TempoMarkerItem:
1595 case FadeInHandleItem:
1596 case FadeInTrimHandleItem:
1597 if (mouse_mode == MouseObject) {
1598 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1600 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1601 rect->set_fill_color (rv->get_fill_color());
1606 case FadeOutHandleItem:
1607 case FadeOutTrimHandleItem:
1608 if (mouse_mode == MouseObject) {
1609 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1611 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1612 rect->set_fill_color (rv->get_fill_color ());
1617 case FeatureLineItem:
1619 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1620 line->set_outline_color (0xFF0000FF);
1631 /* third pass to handle entered track status in a comprehensible way.
1634 switch (item_type) {
1636 case AutomationLineItem:
1637 case ControlPointItem:
1638 /* these do not affect the current entered track state */
1639 clear_entered_track = false;
1642 case AutomationTrackItem:
1643 /* handled above already */
1655 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1663 reset_canvas_cursor ();
1665 switch (item_type) {
1666 case ControlPointItem:
1667 _verbose_cursor->hide ();
1671 case AutomationLineItem:
1672 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1674 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1676 line->set_outline_color (al->get_line_color());
1682 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1686 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1687 location_flags_changed (loc);
1690 case MeterMarkerItem:
1691 case TempoMarkerItem:
1694 case FadeInTrimHandleItem:
1695 case FadeOutTrimHandleItem:
1696 case FadeInHandleItem:
1697 case FadeOutHandleItem:
1699 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1701 rect->set_fill_color (ARDOUR_UI::config()->color ("inactive fade handle"));
1706 case AutomationTrackItem:
1709 case FeatureLineItem:
1711 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1712 line->set_outline_color (ARDOUR_UI::config()->color ("zero line"));
1724 Editor::scrub (framepos_t frame, double current_x)
1728 if (scrubbing_direction == 0) {
1730 _session->request_locate (frame, false);
1731 _session->request_transport_speed (0.1);
1732 scrubbing_direction = 1;
1736 if (last_scrub_x > current_x) {
1738 /* pointer moved to the left */
1740 if (scrubbing_direction > 0) {
1742 /* we reversed direction to go backwards */
1745 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1749 /* still moving to the left (backwards) */
1751 scrub_reversals = 0;
1752 scrub_reverse_distance = 0;
1754 delta = 0.01 * (last_scrub_x - current_x);
1755 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1759 /* pointer moved to the right */
1761 if (scrubbing_direction < 0) {
1762 /* we reversed direction to go forward */
1765 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1768 /* still moving to the right */
1770 scrub_reversals = 0;
1771 scrub_reverse_distance = 0;
1773 delta = 0.01 * (current_x - last_scrub_x);
1774 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1778 /* if there have been more than 2 opposite motion moves detected, or one that moves
1779 back more than 10 pixels, reverse direction
1782 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1784 if (scrubbing_direction > 0) {
1785 /* was forwards, go backwards */
1786 _session->request_transport_speed (-0.1);
1787 scrubbing_direction = -1;
1789 /* was backwards, go forwards */
1790 _session->request_transport_speed (0.1);
1791 scrubbing_direction = 1;
1794 scrub_reverse_distance = 0;
1795 scrub_reversals = 0;
1799 last_scrub_x = current_x;
1803 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1805 _last_motion_y = event->motion.y;
1807 if (event->motion.is_hint) {
1810 /* We call this so that MOTION_NOTIFY events continue to be
1811 delivered to the canvas. We need to do this because we set
1812 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1813 the density of the events, at the expense of a round-trip
1814 to the server. Given that this will mostly occur on cases
1815 where DISPLAY = :0.0, and given the cost of what the motion
1816 event might do, its a good tradeoff.
1819 _track_canvas->get_pointer (x, y);
1822 if (current_stepping_trackview) {
1823 /* don't keep the persistent stepped trackview if the mouse moves */
1824 current_stepping_trackview = 0;
1825 step_timeout.disconnect ();
1828 if (_session && _session->actively_recording()) {
1829 /* Sorry. no dragging stuff around while we record */
1833 update_join_object_range_location (event->motion.y);
1835 if (_drags->active ()) {
1836 return _drags->motion_handler (event, from_autoscroll);
1843 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1845 ControlPoint* control_point;
1847 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1848 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1849 abort(); /*NOTREACHED*/
1852 AutomationLine& line = control_point->line ();
1853 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
1854 /* we shouldn't remove the first or last gain point in region gain lines */
1855 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
1864 Editor::remove_control_point (ArdourCanvas::Item* item)
1866 if (!can_remove_control_point (item)) {
1870 ControlPoint* control_point;
1872 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1873 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1874 abort(); /*NOTREACHED*/
1877 control_point->line().remove_point (*control_point);
1881 Editor::edit_control_point (ArdourCanvas::Item* item)
1883 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1886 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1887 abort(); /*NOTREACHED*/
1890 ControlPointDialog d (p);
1893 if (d.run () != RESPONSE_ACCEPT) {
1897 p->line().modify_point_y (*p, d.get_y_fraction ());
1901 Editor::edit_notes (TimeAxisViewItem& tavi)
1903 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
1909 MidiRegionView::Selection const & s = mrv->selection();
1915 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
1919 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
1923 Editor::note_edit_done (int r, EditNoteDialog* d)
1930 Editor::visible_order_range (int* low, int* high) const
1932 *low = TimeAxisView::max_order ();
1935 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1937 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1939 if (!rtv->hidden()) {
1941 if (*high < rtv->order()) {
1942 *high = rtv->order ();
1945 if (*low > rtv->order()) {
1946 *low = rtv->order ();
1953 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1955 /* Either add to or set the set the region selection, unless
1956 this is an alignment click (control used)
1959 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1960 TimeAxisView* tv = &rv.get_time_axis_view();
1961 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1963 if (rtv && rtv->is_track()) {
1964 speed = rtv->track()->speed();
1967 framepos_t where = get_preferred_edit_position();
1971 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1973 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
1975 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1977 align_region (rv.region(), End, (framepos_t) (where * speed));
1981 align_region (rv.region(), Start, (framepos_t) (where * speed));
1988 Editor::collect_new_region_view (RegionView* rv)
1990 latest_regionviews.push_back (rv);
1994 Editor::collect_and_select_new_region_view (RegionView* rv)
1997 latest_regionviews.push_back (rv);
2001 Editor::cancel_selection ()
2003 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2004 (*i)->hide_selection ();
2007 selection->clear ();
2008 clicked_selection = 0;
2012 Editor::cancel_time_selection ()
2014 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2015 (*i)->hide_selection ();
2017 selection->time.clear ();
2018 clicked_selection = 0;
2022 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2024 RegionView* rv = clicked_regionview;
2026 /* Choose action dependant on which button was pressed */
2027 switch (event->button.button) {
2029 begin_reversible_command (_("start point trim"));
2031 if (selection->selected (rv)) {
2032 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2033 i != selection->regions.by_layer().end(); ++i)
2035 if (!(*i)->region()->locked()) {
2036 (*i)->region()->clear_changes ();
2037 (*i)->region()->trim_front (new_bound);
2038 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2043 if (!rv->region()->locked()) {
2044 rv->region()->clear_changes ();
2045 rv->region()->trim_front (new_bound);
2046 _session->add_command(new StatefulDiffCommand (rv->region()));
2050 commit_reversible_command();
2054 begin_reversible_command (_("End point trim"));
2056 if (selection->selected (rv)) {
2058 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2060 if (!(*i)->region()->locked()) {
2061 (*i)->region()->clear_changes();
2062 (*i)->region()->trim_end (new_bound);
2063 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2069 if (!rv->region()->locked()) {
2070 rv->region()->clear_changes ();
2071 rv->region()->trim_end (new_bound);
2072 _session->add_command (new StatefulDiffCommand (rv->region()));
2076 commit_reversible_command();
2085 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2090 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2091 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2092 abort(); /*NOTREACHED*/
2095 Location* location = find_location_from_marker (marker, is_start);
2096 location->set_hidden (true, this);
2100 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2102 using namespace Gtkmm2ext;
2104 ArdourPrompter prompter (false);
2106 prompter.set_prompt (_("Name for region:"));
2107 prompter.set_initial_text (clicked_regionview->region()->name());
2108 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2109 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2110 prompter.show_all ();
2111 switch (prompter.run ()) {
2112 case Gtk::RESPONSE_ACCEPT:
2114 prompter.get_result(str);
2116 clicked_regionview->region()->set_name (str);
2125 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2127 /* no brushing without a useful snap setting */
2129 switch (_snap_mode) {
2131 return; /* can't work because it allows region to be placed anywhere */
2136 switch (_snap_type) {
2144 /* don't brush a copy over the original */
2146 if (pos == rv->region()->position()) {
2150 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2152 if (rtv == 0 || !rtv->is_track()) {
2156 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2157 double speed = rtv->track()->speed();
2159 playlist->clear_changes ();
2160 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2161 playlist->add_region (new_region, (framepos_t) (pos * speed));
2162 _session->add_command (new StatefulDiffCommand (playlist));
2164 // playlist is frozen, so we have to update manually XXX this is disgusting
2166 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2170 Editor::track_height_step_timeout ()
2172 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2173 current_stepping_trackview = 0;
2180 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2182 assert (region_view);
2184 if (!region_view->region()->playlist()) {
2188 switch (Config->get_edit_mode()) {
2190 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2193 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2196 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2203 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2205 assert (region_view);
2207 if (!region_view->region()->playlist()) {
2211 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2215 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2217 assert (region_view);
2219 if (!region_view->region()->playlist()) {
2223 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2227 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2229 begin_reversible_command (Operations::drag_region_brush);
2232 /** Start a grab where a time range is selected, track(s) are selected, and the
2233 * user clicks and drags a region with a modifier in order to create a new region containing
2234 * the section of the clicked region that lies within the time range.
2237 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2239 if (clicked_regionview == 0) {
2243 /* lets try to create new Region for the selection */
2245 vector<boost::shared_ptr<Region> > new_regions;
2246 create_region_from_selection (new_regions);
2248 if (new_regions.empty()) {
2252 /* XXX fix me one day to use all new regions */
2254 boost::shared_ptr<Region> region (new_regions.front());
2256 /* add it to the current stream/playlist.
2258 tricky: the streamview for the track will add a new regionview. we will
2259 catch the signal it sends when it creates the regionview to
2260 set the regionview we want to then drag.
2263 latest_regionviews.clear();
2264 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2266 /* A selection grab currently creates two undo/redo operations, one for
2267 creating the new region and another for moving it.
2270 begin_reversible_command (Operations::selection_grab);
2272 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2274 playlist->clear_changes ();
2275 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2276 _session->add_command(new StatefulDiffCommand (playlist));
2280 if (latest_regionviews.empty()) {
2281 /* something went wrong */
2285 /* we need to deselect all other regionviews, and select this one
2286 i'm ignoring undo stuff, because the region creation will take care of it
2289 selection->set (latest_regionviews);
2291 commit_reversible_command ();
2293 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2299 if (_drags->active ()) {
2302 selection->clear ();
2308 /** Update _join_object_range_state which indicate whether we are over the top
2309 * or bottom half of a route view, used by the `join object/range' tool
2310 * mode. Coordinates in canvas space.
2313 Editor::update_join_object_range_location (double y)
2315 if (!get_smart_mode()) {
2316 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2320 JoinObjectRangeState const old = _join_object_range_state;
2322 if (mouse_mode == MouseObject) {
2323 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2324 } else if (mouse_mode == MouseRange) {
2325 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2328 if (entered_regionview) {
2330 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2331 double const c = item_space.y / entered_regionview->height();
2333 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2335 if (_join_object_range_state != old) {
2336 set_canvas_cursor (which_track_cursor ());
2339 } else if (entered_track) {
2341 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2343 if (entered_route_view) {
2348 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2350 double track_height = entered_route_view->view()->child_height();
2351 if (Config->get_show_name_highlight()) {
2352 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2354 double const c = cy / track_height;
2358 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2360 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2364 /* Other kinds of tracks use object mode */
2365 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2368 if (_join_object_range_state != old) {
2369 set_canvas_cursor (which_track_cursor ());
2375 Editor::effective_mouse_mode () const
2377 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2379 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2387 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2389 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2392 e->region_view().delete_note (e->note ());
2395 /** Obtain the pointer position in canvas coordinates */
2397 Editor::get_pointer_position (double& x, double& y) const
2400 _track_canvas->get_pointer (px, py);
2401 _track_canvas->window_to_canvas (px, py, x, y);