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 if (!was_internal && internal_editing()) {
300 /* switched to internal, switch to internal snap settings */
301 set_snap_to(internal_snap_type);
302 set_snap_mode(internal_snap_mode);
303 } else if (was_internal && !internal_editing()) {
304 /* switched out of internal, switch to non-internal snap settings */
305 set_snap_to(pre_internal_snap_type);
306 set_snap_mode(pre_internal_snap_mode);
311 /* this should generate a new enter event which will
312 trigger the appropiate cursor.
316 _track_canvas->re_enter ();
319 set_gain_envelope_visibility ();
321 update_time_selection_display ();
323 MouseModeChanged (); /* EMIT SIGNAL */
327 Editor::internal_editing() const
329 return mouse_mode == Editing::MouseContent || mouse_mode == Editing::MouseDraw;
333 Editor::update_time_selection_display ()
335 if (smart_mode_action->get_active()) {
336 /* not sure what to do here */
337 if (mouse_mode == MouseObject) {
341 switch (mouse_mode) {
343 selection->clear_objects ();
344 selection->ClearMidiNoteSelection(); //signal
347 selection->clear_objects ();
348 selection->clear_time ();
349 selection->clear_tracks ();
350 selection->ClearMidiNoteSelection(); //signal
354 //if we go into internal editing, clear everything in the outside world
355 selection->clear_objects ();
356 selection->clear_time ();
357 selection->clear_tracks ();
361 selection->clear_objects ();
362 selection->clear_time ();
363 selection->clear_tracks ();
370 Editor::step_mouse_mode (bool next)
372 const int n_mouse_modes = (int)MouseContent + 1;
373 int current = (int)current_mouse_mode();
375 set_mouse_mode((MouseMode)((current + 1) % n_mouse_modes));
377 set_mouse_mode((MouseMode)((current + n_mouse_modes - 1) % n_mouse_modes));
382 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
384 /* in object/audition/timefx/gain-automation mode,
385 any button press sets the selection if the object
386 can be selected. this is a bit of hack, because
387 we want to avoid this if the mouse operation is a
390 note: not dbl-click or triple-click
392 Also note that there is no region selection in internal edit mode, otherwise
393 for operations operating on the selection (e.g. cut) it is not obvious whether
394 to cut notes or regions.
397 MouseMode eff_mouse_mode = effective_mouse_mode ();
399 if (eff_mouse_mode == MouseCut) {
400 /* never change selection in cut mode */
404 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
405 /* context clicks are always about object properties, even if
406 we're in range mode within smart mode.
408 eff_mouse_mode = MouseObject;
411 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
412 if (get_smart_mode()) {
414 case FadeInHandleItem:
415 case FadeInTrimHandleItem:
416 case FadeOutHandleItem:
417 case FadeOutTrimHandleItem:
418 eff_mouse_mode = MouseObject;
425 if (((mouse_mode != MouseObject) &&
426 (mouse_mode != MouseAudition || item_type != RegionItem) &&
427 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
428 (mouse_mode != MouseDraw)) ||
429 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
433 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
435 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
437 /* almost no selection action on modified button-2 or button-3 events */
439 if (item_type != RegionItem && event->button.button != 2) {
445 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
446 bool press = (event->type == GDK_BUTTON_PRESS);
451 if (eff_mouse_mode != MouseRange) {
452 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 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 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 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);
504 case AutomationTrackItem:
505 set_selected_track_as_side_effect (op);
514 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
516 /* single mouse clicks on any of these item types operate
517 independent of mouse mode, mostly because they are
518 not on the main track canvas or because we want
522 NoteBase* note = NULL;
525 case PlayheadCursorItem:
526 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
530 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
531 hide_marker (item, event);
533 _drags->set (new MarkerDrag (this, item), event);
537 case TempoMarkerItem:
540 new TempoMarkerDrag (
543 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
550 case MeterMarkerItem:
553 new MeterMarkerDrag (
556 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
564 _drags->set (new VideoTimeLineDrag (this, item), event);
571 case TimecodeRulerItem:
572 case SamplesRulerItem:
573 case MinsecRulerItem:
575 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
576 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
582 case RangeMarkerBarItem:
583 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
584 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
585 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
586 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
588 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
593 case CdMarkerBarItem:
594 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
595 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
597 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
602 case TransportMarkerBarItem:
603 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
604 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
606 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
615 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
616 /* special case: allow trim of range selections in joined object mode;
617 in theory eff should equal MouseRange in this case, but it doesn't
618 because entering the range selection canvas item results in entered_regionview
619 being set to 0, so update_join_object_range_location acts as if we aren't
622 if (item_type == StartSelectionTrimItem) {
623 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
624 } else if (item_type == EndSelectionTrimItem) {
625 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
629 Editing::MouseMode eff = effective_mouse_mode ();
631 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
632 if (get_smart_mode()) {
634 case FadeInHandleItem:
635 case FadeInTrimHandleItem:
636 case FadeOutHandleItem:
637 case FadeOutTrimHandleItem:
648 case StartSelectionTrimItem:
649 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
652 case EndSelectionTrimItem:
653 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
657 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
658 start_selection_grab (item, event);
660 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
661 /* grab selection for moving */
662 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
664 /* this was debated, but decided the more common action was to
665 make a new selection */
666 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
671 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
672 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
674 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
679 case RegionViewNameHighlight:
680 if (!clicked_regionview->region()->locked()) {
681 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
687 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
688 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
690 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
699 case FadeInHandleItem:
700 case FadeOutHandleItem:
701 case LeftFrameHandle:
702 case RightFrameHandle:
703 case FeatureLineItem:
704 case RegionViewNameHighlight:
707 case AutomationTrackItem:
708 _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, current_canvas_cursor);
719 /* Existing note: allow trimming/motion */
720 if ((note = reinterpret_cast<NoteBase*> (item->get_data ("notebase")))) {
721 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
722 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
724 _drags->set (new NoteDrag (this, item), event);
729 case ControlPointItem:
730 _drags->set (new ControlPointDrag (this, item), event);
735 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
736 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
741 case AutomationTrackItem:
742 /* rubberband drag to select automation points */
743 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
752 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
753 event->type == GDK_BUTTON_PRESS) {
755 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
757 } else if (event->type == GDK_BUTTON_PRESS) {
760 case FadeInHandleItem:
762 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
766 case FadeOutHandleItem:
768 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
772 case StartCrossFadeItem:
773 case EndCrossFadeItem:
774 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
775 // if (!clicked_regionview->region()->locked()) {
776 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
781 case FeatureLineItem:
783 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
784 remove_transient(item);
788 _drags->set (new FeatureLineDrag (this, item), event);
794 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
795 /* click on an automation region view; do nothing here and let the ARV's signal handler
801 /* click on a normal region view */
802 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
803 add_region_copy_drag (item, event, clicked_regionview);
804 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
805 add_region_brush_drag (item, event, clicked_regionview);
807 add_region_drag (item, event, clicked_regionview);
811 _drags->start_grab (event);
815 case RegionViewNameHighlight:
816 case LeftFrameHandle:
817 case RightFrameHandle:
818 if (!clicked_regionview->region()->locked()) {
819 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
824 case FadeInTrimHandleItem:
825 case FadeOutTrimHandleItem:
826 if (!clicked_regionview->region()->locked()) {
827 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
834 /* rename happens on edit clicks */
835 if (clicked_regionview->get_name_highlight()) {
836 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
842 case ControlPointItem:
843 _drags->set (new ControlPointDrag (this, item), event);
847 case AutomationLineItem:
848 _drags->set (new LineDrag (this, item), event);
853 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
856 case AutomationTrackItem:
858 TimeAxisView* parent = clicked_axisview->get_parent ();
859 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
861 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
863 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
865 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
866 if (pl->n_regions() == 0) {
867 /* Parent has no regions; create one so that we have somewhere to put automation */
868 _drags->set (new RegionCreateDrag (this, item, parent), event);
870 /* See if there's a region before the click that we can extend, and extend it if so */
871 framepos_t const t = canvas_event_sample (event);
872 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
874 _drags->set (new RegionCreateDrag (this, item, parent), event);
876 prev->set_length (t - prev->position ());
880 /* rubberband drag to select automation points */
881 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
905 _drags->set (new LineDrag (this, item), event);
908 case ControlPointItem:
909 _drags->set (new ControlPointDrag (this, item), event);
915 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
917 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
919 double const y = event->button.y;
920 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
922 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
924 /* smart "join" mode: drag automation */
925 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
933 case AutomationLineItem:
934 _drags->set (new LineDrag (this, item), event);
938 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
939 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
940 /* Note is big and pointer is near the end, trim */
941 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
944 _drags->set (new NoteDrag (this, item), event);
951 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
952 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
963 if (item_type == NoteItem) {
964 /* resize-drag notes */
965 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
966 if (note->big_enough_to_trim()) {
967 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
971 } else if (clicked_regionview) {
973 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
979 _drags->set (new ScrubDrag (this, item), event);
981 scrub_reverse_distance = 0;
982 last_scrub_x = event->button.x;
983 scrubbing_direction = 0;
984 push_canvas_cursor (_cursors->transparent);
996 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
998 Editing::MouseMode const eff = effective_mouse_mode ();
1001 switch (item_type) {
1003 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1004 add_region_copy_drag (item, event, clicked_regionview);
1006 add_region_drag (item, event, clicked_regionview);
1008 _drags->start_grab (event);
1011 case ControlPointItem:
1012 _drags->set (new ControlPointDrag (this, item), event);
1020 switch (item_type) {
1021 case RegionViewNameHighlight:
1022 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1026 case LeftFrameHandle:
1027 case RightFrameHandle:
1028 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1032 case RegionViewName:
1033 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1047 /* relax till release */
1059 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1061 if (event->type == GDK_2BUTTON_PRESS) {
1062 _drags->mark_double_click ();
1063 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1067 if (event->type != GDK_BUTTON_PRESS) {
1071 pre_press_cursor = current_canvas_cursor;
1073 _track_canvas->grab_focus();
1075 if (_session && _session->actively_recording()) {
1079 button_selection (item, event, item_type);
1081 if (!_drags->active () &&
1082 (Keyboard::is_delete_event (&event->button) ||
1083 Keyboard::is_context_menu_event (&event->button) ||
1084 Keyboard::is_edit_event (&event->button))) {
1086 /* handled by button release */
1090 //not rolling, range mode click + join_play_range : locate the PH here
1091 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_follow_edits() ) {
1092 framepos_t where = canvas_event_sample (event);
1094 _session->request_locate (where, false);
1097 switch (event->button.button) {
1099 return button_press_handler_1 (item, event, item_type);
1103 return button_press_handler_2 (item, event, item_type);
1110 return button_press_dispatch (&event->button);
1119 Editor::button_press_dispatch (GdkEventButton* ev)
1121 /* this function is intended only for buttons 4 and above.
1124 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1125 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1129 Editor::button_release_dispatch (GdkEventButton* ev)
1131 /* this function is intended only for buttons 4 and above.
1134 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1135 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1139 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1141 framepos_t where = canvas_event_sample (event);
1142 AutomationTimeAxisView* atv = 0;
1144 if (pre_press_cursor) {
1145 set_canvas_cursor (pre_press_cursor);
1146 pre_press_cursor = 0;
1149 /* no action if we're recording */
1151 if (_session && _session->actively_recording()) {
1155 /* see if we're finishing a drag */
1157 bool were_dragging = false;
1158 if (_drags->active ()) {
1159 bool const r = _drags->end_grab (event);
1161 /* grab dragged, so do nothing else */
1165 were_dragging = true;
1168 update_region_layering_order_editor ();
1170 /* edit events get handled here */
1172 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1173 switch (item_type) {
1175 show_region_properties ();
1178 case TempoMarkerItem: {
1180 TempoMarker* tempo_marker;
1182 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1183 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1184 abort(); /*NOTREACHED*/
1187 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1188 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1189 abort(); /*NOTREACHED*/
1192 edit_tempo_marker (*tempo_marker);
1196 case MeterMarkerItem: {
1198 MeterMarker* meter_marker;
1200 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1201 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1202 abort(); /*NOTREACHED*/
1205 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1206 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1207 abort(); /*NOTREACHED*/
1209 edit_meter_marker (*meter_marker);
1213 case RegionViewName:
1214 if (clicked_regionview->name_active()) {
1215 return mouse_rename_region (item, event);
1219 case ControlPointItem:
1220 edit_control_point (item);
1229 /* context menu events get handled here */
1230 if (Keyboard::is_context_menu_event (&event->button)) {
1232 context_click_event = *event;
1234 if (!_drags->active ()) {
1236 /* no matter which button pops up the context menu, tell the menu
1237 widget to use button 1 to drive menu selection.
1240 switch (item_type) {
1242 case FadeInHandleItem:
1243 case FadeInTrimHandleItem:
1244 case StartCrossFadeItem:
1245 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1249 case FadeOutHandleItem:
1250 case FadeOutTrimHandleItem:
1251 case EndCrossFadeItem:
1252 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1255 case LeftFrameHandle:
1256 case RightFrameHandle:
1260 popup_track_context_menu (1, event->button.time, item_type, false);
1264 case RegionViewNameHighlight:
1265 case RegionViewName:
1266 popup_track_context_menu (1, event->button.time, item_type, false);
1270 popup_track_context_menu (1, event->button.time, item_type, true);
1273 case AutomationTrackItem:
1274 popup_track_context_menu (1, event->button.time, item_type, false);
1278 case RangeMarkerBarItem:
1279 case TransportMarkerBarItem:
1280 case CdMarkerBarItem:
1284 case TimecodeRulerItem:
1285 case SamplesRulerItem:
1286 case MinsecRulerItem:
1288 popup_ruler_menu (where, item_type);
1292 marker_context_menu (&event->button, item);
1295 case TempoMarkerItem:
1296 tempo_or_meter_marker_context_menu (&event->button, item);
1299 case MeterMarkerItem:
1300 tempo_or_meter_marker_context_menu (&event->button, item);
1303 case CrossfadeViewItem:
1304 popup_track_context_menu (1, event->button.time, item_type, false);
1307 case ControlPointItem:
1308 popup_control_point_context_menu (item, event);
1319 /* delete events get handled here */
1321 Editing::MouseMode const eff = effective_mouse_mode ();
1323 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1325 switch (item_type) {
1326 case TempoMarkerItem:
1327 remove_tempo_marker (item);
1330 case MeterMarkerItem:
1331 remove_meter_marker (item);
1335 remove_marker (*item, event);
1339 if (eff == MouseObject) {
1340 remove_clicked_region ();
1344 case ControlPointItem:
1345 remove_control_point (item);
1349 remove_midi_note (item, event);
1358 switch (event->button.button) {
1361 switch (item_type) {
1362 /* see comments in button_press_handler */
1363 case PlayheadCursorItem:
1366 case AutomationLineItem:
1367 case StartSelectionTrimItem:
1368 case EndSelectionTrimItem:
1372 if (!_dragging_playhead) {
1373 snap_to_with_modifier (where, event, RoundNearest, true);
1374 mouse_add_new_marker (where);
1378 case CdMarkerBarItem:
1379 if (!_dragging_playhead) {
1380 // if we get here then a dragged range wasn't done
1381 snap_to_with_modifier (where, event, RoundNearest, true);
1382 mouse_add_new_marker (where, true);
1387 if (!_dragging_playhead) {
1388 snap_to_with_modifier (where, event);
1389 mouse_add_new_tempo_event (where);
1394 if (!_dragging_playhead) {
1395 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1400 case TimecodeRulerItem:
1401 case SamplesRulerItem:
1402 case MinsecRulerItem:
1413 switch (item_type) {
1416 /* check that we didn't drag before releasing, since
1417 its really annoying to create new control
1418 points when doing this.
1420 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1421 if (!were_dragging && arv) {
1422 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1423 arv->add_gain_point_event (item, event, with_guard_points);
1429 case AutomationTrackItem: {
1430 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1431 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1433 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1444 pop_canvas_cursor ();
1445 if (scrubbing_direction == 0) {
1446 /* no drag, just a click */
1447 switch (item_type) {
1449 play_selected_region ();
1455 /* make sure we stop */
1456 _session->request_transport_speed (0.0);
1465 /* do any (de)selection operations that should occur on button release */
1466 button_selection (item, event, item_type);
1475 switch (item_type) {
1477 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1479 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1482 // Button2 click is unused
1497 // x_style_paste (where, 1.0);
1518 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1525 /* by the time we reach here, entered_regionview and entered trackview
1526 * will have already been set as appropriate. Things are done this
1527 * way because this method isn't passed a pointer to a variable type of
1528 * thing that is entered (which may or may not be canvas item).
1529 * (e.g. the actual entered regionview)
1532 choose_canvas_cursor_on_entry (&event->crossing, item_type);
1534 switch (item_type) {
1535 case ControlPointItem:
1536 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1537 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1540 fraction = 1.0 - (cp->get_y() / cp->line().height());
1542 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1543 _verbose_cursor->show ();
1548 if (mouse_mode == MouseDraw) {
1549 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1551 line->set_outline_color (ARDOUR_UI::config()->color ("entered gain line"));
1556 case AutomationLineItem:
1557 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1558 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1560 line->set_outline_color (ARDOUR_UI::config()->color ("entered automation line"));
1565 case AutomationTrackItem:
1566 AutomationTimeAxisView* atv;
1567 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1568 clear_entered_track = false;
1569 set_entered_track (atv);
1574 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1577 entered_marker = marker;
1578 marker->set_color_rgba (ARDOUR_UI::config()->color ("entered marker"));
1580 case MeterMarkerItem:
1581 case TempoMarkerItem:
1584 case FadeInHandleItem:
1585 case FadeInTrimHandleItem:
1586 if (mouse_mode == MouseObject) {
1587 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1589 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1590 rect->set_fill_color (rv->get_fill_color());
1595 case FadeOutHandleItem:
1596 case FadeOutTrimHandleItem:
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 FeatureLineItem:
1608 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1609 line->set_outline_color (0xFF0000FF);
1620 /* third pass to handle entered track status in a comprehensible way.
1623 switch (item_type) {
1625 case AutomationLineItem:
1626 case ControlPointItem:
1627 /* these do not affect the current entered track state */
1628 clear_entered_track = false;
1631 case AutomationTrackItem:
1632 /* handled above already */
1644 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1652 reset_canvas_cursor ();
1654 switch (item_type) {
1655 case ControlPointItem:
1656 _verbose_cursor->hide ();
1660 case AutomationLineItem:
1661 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1663 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1665 line->set_outline_color (al->get_line_color());
1671 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1675 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1676 location_flags_changed (loc);
1679 case MeterMarkerItem:
1680 case TempoMarkerItem:
1683 case FadeInTrimHandleItem:
1684 case FadeOutTrimHandleItem:
1685 case FadeInHandleItem:
1686 case FadeOutHandleItem:
1688 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1690 rect->set_fill_color (ARDOUR_UI::config()->color ("inactive fade handle"));
1695 case AutomationTrackItem:
1698 case FeatureLineItem:
1700 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1701 line->set_outline_color (ARDOUR_UI::config()->color ("zero line"));
1713 Editor::scrub (framepos_t frame, double current_x)
1717 if (scrubbing_direction == 0) {
1719 _session->request_locate (frame, false);
1720 _session->request_transport_speed (0.1);
1721 scrubbing_direction = 1;
1725 if (last_scrub_x > current_x) {
1727 /* pointer moved to the left */
1729 if (scrubbing_direction > 0) {
1731 /* we reversed direction to go backwards */
1734 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1738 /* still moving to the left (backwards) */
1740 scrub_reversals = 0;
1741 scrub_reverse_distance = 0;
1743 delta = 0.01 * (last_scrub_x - current_x);
1744 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1748 /* pointer moved to the right */
1750 if (scrubbing_direction < 0) {
1751 /* we reversed direction to go forward */
1754 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1757 /* still moving to the right */
1759 scrub_reversals = 0;
1760 scrub_reverse_distance = 0;
1762 delta = 0.01 * (current_x - last_scrub_x);
1763 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1767 /* if there have been more than 2 opposite motion moves detected, or one that moves
1768 back more than 10 pixels, reverse direction
1771 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1773 if (scrubbing_direction > 0) {
1774 /* was forwards, go backwards */
1775 _session->request_transport_speed (-0.1);
1776 scrubbing_direction = -1;
1778 /* was backwards, go forwards */
1779 _session->request_transport_speed (0.1);
1780 scrubbing_direction = 1;
1783 scrub_reverse_distance = 0;
1784 scrub_reversals = 0;
1788 last_scrub_x = current_x;
1792 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1794 _last_motion_y = event->motion.y;
1796 if (event->motion.is_hint) {
1799 /* We call this so that MOTION_NOTIFY events continue to be
1800 delivered to the canvas. We need to do this because we set
1801 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1802 the density of the events, at the expense of a round-trip
1803 to the server. Given that this will mostly occur on cases
1804 where DISPLAY = :0.0, and given the cost of what the motion
1805 event might do, its a good tradeoff.
1808 _track_canvas->get_pointer (x, y);
1811 if (current_stepping_trackview) {
1812 /* don't keep the persistent stepped trackview if the mouse moves */
1813 current_stepping_trackview = 0;
1814 step_timeout.disconnect ();
1817 if (_session && _session->actively_recording()) {
1818 /* Sorry. no dragging stuff around while we record */
1822 update_join_object_range_location (event->motion.y);
1824 if (_drags->active ()) {
1825 return _drags->motion_handler (event, from_autoscroll);
1832 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1834 ControlPoint* control_point;
1836 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1837 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1838 abort(); /*NOTREACHED*/
1841 AutomationLine& line = control_point->line ();
1842 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
1843 /* we shouldn't remove the first or last gain point in region gain lines */
1844 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
1853 Editor::remove_control_point (ArdourCanvas::Item* item)
1855 if (!can_remove_control_point (item)) {
1859 ControlPoint* control_point;
1861 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1862 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1863 abort(); /*NOTREACHED*/
1866 control_point->line().remove_point (*control_point);
1870 Editor::edit_control_point (ArdourCanvas::Item* item)
1872 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1875 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1876 abort(); /*NOTREACHED*/
1879 ControlPointDialog d (p);
1882 if (d.run () != RESPONSE_ACCEPT) {
1886 p->line().modify_point_y (*p, d.get_y_fraction ());
1890 Editor::edit_notes (TimeAxisViewItem& tavi)
1892 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
1898 MidiRegionView::Selection const & s = mrv->selection();
1904 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
1908 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
1912 Editor::note_edit_done (int r, EditNoteDialog* d)
1919 Editor::visible_order_range (int* low, int* high) const
1921 *low = TimeAxisView::max_order ();
1924 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1926 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1928 if (!rtv->hidden()) {
1930 if (*high < rtv->order()) {
1931 *high = rtv->order ();
1934 if (*low > rtv->order()) {
1935 *low = rtv->order ();
1942 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1944 /* Either add to or set the set the region selection, unless
1945 this is an alignment click (control used)
1948 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1949 TimeAxisView* tv = &rv.get_time_axis_view();
1950 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1952 if (rtv && rtv->is_track()) {
1953 speed = rtv->track()->speed();
1956 framepos_t where = get_preferred_edit_position();
1960 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1962 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
1964 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1966 align_region (rv.region(), End, (framepos_t) (where * speed));
1970 align_region (rv.region(), Start, (framepos_t) (where * speed));
1977 Editor::collect_new_region_view (RegionView* rv)
1979 latest_regionviews.push_back (rv);
1983 Editor::collect_and_select_new_region_view (RegionView* rv)
1986 latest_regionviews.push_back (rv);
1990 Editor::cancel_selection ()
1992 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1993 (*i)->hide_selection ();
1996 selection->clear ();
1997 clicked_selection = 0;
2001 Editor::cancel_time_selection ()
2003 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2004 (*i)->hide_selection ();
2006 selection->time.clear ();
2007 clicked_selection = 0;
2011 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2013 RegionView* rv = clicked_regionview;
2015 /* Choose action dependant on which button was pressed */
2016 switch (event->button.button) {
2018 begin_reversible_command (_("start point trim"));
2020 if (selection->selected (rv)) {
2021 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2022 i != selection->regions.by_layer().end(); ++i)
2024 if (!(*i)->region()->locked()) {
2025 (*i)->region()->clear_changes ();
2026 (*i)->region()->trim_front (new_bound);
2027 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2032 if (!rv->region()->locked()) {
2033 rv->region()->clear_changes ();
2034 rv->region()->trim_front (new_bound);
2035 _session->add_command(new StatefulDiffCommand (rv->region()));
2039 commit_reversible_command();
2043 begin_reversible_command (_("End point trim"));
2045 if (selection->selected (rv)) {
2047 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2049 if (!(*i)->region()->locked()) {
2050 (*i)->region()->clear_changes();
2051 (*i)->region()->trim_end (new_bound);
2052 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2058 if (!rv->region()->locked()) {
2059 rv->region()->clear_changes ();
2060 rv->region()->trim_end (new_bound);
2061 _session->add_command (new StatefulDiffCommand (rv->region()));
2065 commit_reversible_command();
2074 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2079 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2080 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2081 abort(); /*NOTREACHED*/
2084 Location* location = find_location_from_marker (marker, is_start);
2085 location->set_hidden (true, this);
2089 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2091 using namespace Gtkmm2ext;
2093 ArdourPrompter prompter (false);
2095 prompter.set_prompt (_("Name for region:"));
2096 prompter.set_initial_text (clicked_regionview->region()->name());
2097 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2098 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2099 prompter.show_all ();
2100 switch (prompter.run ()) {
2101 case Gtk::RESPONSE_ACCEPT:
2103 prompter.get_result(str);
2105 clicked_regionview->region()->set_name (str);
2114 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2116 /* no brushing without a useful snap setting */
2118 switch (_snap_mode) {
2120 return; /* can't work because it allows region to be placed anywhere */
2125 switch (_snap_type) {
2133 /* don't brush a copy over the original */
2135 if (pos == rv->region()->position()) {
2139 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2141 if (rtv == 0 || !rtv->is_track()) {
2145 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2146 double speed = rtv->track()->speed();
2148 playlist->clear_changes ();
2149 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2150 playlist->add_region (new_region, (framepos_t) (pos * speed));
2151 _session->add_command (new StatefulDiffCommand (playlist));
2153 // playlist is frozen, so we have to update manually XXX this is disgusting
2155 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2159 Editor::track_height_step_timeout ()
2161 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2162 current_stepping_trackview = 0;
2169 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2171 assert (region_view);
2173 if (!region_view->region()->playlist()) {
2177 switch (Config->get_edit_mode()) {
2179 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2182 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2185 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2192 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2194 assert (region_view);
2196 if (!region_view->region()->playlist()) {
2200 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2204 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2206 assert (region_view);
2208 if (!region_view->region()->playlist()) {
2212 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2216 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2218 begin_reversible_command (Operations::drag_region_brush);
2221 /** Start a grab where a time range is selected, track(s) are selected, and the
2222 * user clicks and drags a region with a modifier in order to create a new region containing
2223 * the section of the clicked region that lies within the time range.
2226 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2228 if (clicked_regionview == 0) {
2232 /* lets try to create new Region for the selection */
2234 vector<boost::shared_ptr<Region> > new_regions;
2235 create_region_from_selection (new_regions);
2237 if (new_regions.empty()) {
2241 /* XXX fix me one day to use all new regions */
2243 boost::shared_ptr<Region> region (new_regions.front());
2245 /* add it to the current stream/playlist.
2247 tricky: the streamview for the track will add a new regionview. we will
2248 catch the signal it sends when it creates the regionview to
2249 set the regionview we want to then drag.
2252 latest_regionviews.clear();
2253 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2255 /* A selection grab currently creates two undo/redo operations, one for
2256 creating the new region and another for moving it.
2259 begin_reversible_command (Operations::selection_grab);
2261 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2263 playlist->clear_changes ();
2264 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2265 _session->add_command(new StatefulDiffCommand (playlist));
2269 if (latest_regionviews.empty()) {
2270 /* something went wrong */
2274 /* we need to deselect all other regionviews, and select this one
2275 i'm ignoring undo stuff, because the region creation will take care of it
2278 selection->set (latest_regionviews);
2280 commit_reversible_command ();
2282 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2288 if (_drags->active ()) {
2291 selection->clear ();
2297 /** Update _join_object_range_state which indicate whether we are over the top
2298 * or bottom half of a route view, used by the `join object/range' tool
2299 * mode. Coordinates in canvas space.
2302 Editor::update_join_object_range_location (double y)
2304 if (!get_smart_mode()) {
2305 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2309 JoinObjectRangeState const old = _join_object_range_state;
2311 if (mouse_mode == MouseObject) {
2312 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2313 } else if (mouse_mode == MouseRange) {
2314 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2317 if (entered_regionview) {
2319 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2320 double const c = item_space.y / entered_regionview->height();
2322 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2324 if (_join_object_range_state != old) {
2325 set_canvas_cursor (which_track_cursor ());
2328 } else if (entered_track) {
2330 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2332 if (entered_route_view) {
2337 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2339 double track_height = entered_route_view->view()->child_height();
2340 if (Config->get_show_name_highlight()) {
2341 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2343 double const c = cy / track_height;
2347 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2349 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2353 /* Other kinds of tracks use object mode */
2354 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2357 if (_join_object_range_state != old) {
2358 set_canvas_cursor (which_track_cursor ());
2364 Editor::effective_mouse_mode () const
2366 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2368 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2376 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2378 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2381 e->region_view().delete_note (e->note ());
2384 /** Obtain the pointer position in canvas coordinates */
2386 Editor::get_pointer_position (double& x, double& y) const
2389 _track_canvas->get_pointer (px, py);
2390 _track_canvas->window_to_canvas (px, py, x, y);