2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include "pbd/error.h"
30 #include "pbd/enumwriter.h"
31 #include "pbd/memento_command.h"
32 #include "pbd/basename.h"
33 #include "pbd/stateful_diff_command.h"
35 #include "gtkmm2ext/bindings.h"
36 #include "gtkmm2ext/utils.h"
37 #include "gtkmm2ext/tearoff.h"
39 #include "canvas/canvas.h"
41 #include "ardour/audioregion.h"
42 #include "ardour/operations.h"
43 #include "ardour/playlist.h"
44 #include "ardour/profile.h"
45 #include "ardour/region_factory.h"
46 #include "ardour/route.h"
47 #include "ardour/session.h"
48 #include "ardour/types.h"
50 #include "ardour_ui.h"
53 #include "time_axis_view.h"
54 #include "audio_time_axis.h"
55 #include "audio_region_view.h"
56 #include "midi_region_view.h"
58 #include "streamview.h"
59 #include "region_gain_line.h"
60 #include "automation_time_axis.h"
61 #include "control_point.h"
63 #include "selection.h"
66 #include "rgb_macros.h"
67 #include "control_point_dialog.h"
68 #include "editor_drag.h"
69 #include "automation_region_view.h"
70 #include "edit_note_dialog.h"
71 #include "mouse_cursors.h"
72 #include "editor_cursors.h"
73 #include "verbose_cursor.h"
79 using namespace ARDOUR;
82 using namespace Editing;
83 using Gtkmm2ext::Keyboard;
86 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
88 /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
89 pays attentions to subwindows. this means that menu windows are ignored, and
90 if the pointer is in a menu, the return window from the call will be the
91 the regular subwindow *under* the menu.
93 this matters quite a lot if the pointer is moving around in a menu that overlaps
94 the track canvas because we will believe that we are within the track canvas
95 when we are not. therefore, we track enter/leave events for the track canvas
96 and allow that to override the result of gdk_window_get_pointer().
99 if (!within_track_canvas) {
104 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
106 if (!canvas_window) {
110 Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
112 if (!pointer_window) {
116 if (pointer_window != canvas_window) {
117 in_track_canvas = false;
121 in_track_canvas = true;
124 event.type = GDK_BUTTON_RELEASE;
128 where = window_event_sample (&event, 0, 0);
134 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
136 ArdourCanvas::Duple d;
138 if (!gdk_event_get_coords (event, &d.x, &d.y)) {
142 /* event coordinates are in window units, so convert to canvas
145 d = _track_canvas->window_to_canvas (d);
155 return pixel_to_sample (d.x);
159 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
164 /* event coordinates are already in canvas units */
166 if (!gdk_event_get_coords (event, &x, &y)) {
167 cerr << "!NO c COORDS for event type " << event->type << endl;
179 /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel
180 position is negative (as can be the case with motion events in particular),
181 the frame location is always positive.
184 return pixel_to_sample_from_event (x);
188 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
190 boost::shared_ptr<Trimmable> st = _trimmable.lock();
192 if (!st || st == t) {
198 Editor::set_current_movable (boost::shared_ptr<Movable> m)
200 boost::shared_ptr<Movable> sm = _movable.lock();
202 if (!sm || sm != m) {
208 Editor::mouse_mode_object_range_toggled()
210 MouseMode m = mouse_mode;
212 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
214 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
217 if (tact->get_active()) {
218 m = MouseObject; //Smart mode turned to ON, force editing to Object mode
221 set_mouse_mode(m, true); //call this so the button styles can get updated
224 static Glib::RefPtr<Action>
225 get_mouse_mode_action(MouseMode m)
229 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
231 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
233 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
235 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
237 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
239 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-content"));
241 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
243 return Glib::RefPtr<Action>();
247 Editor::set_mouse_mode (MouseMode m, bool force)
249 if (_drags->active ()) {
253 if (!force && m == mouse_mode) {
257 if (ARDOUR::Profile->get_mixbus()) {
258 if ( m == MouseCut) m = MouseObject;
261 Glib::RefPtr<Action> act = get_mouse_mode_action(m);
262 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
264 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
265 tact->set_active (false);
266 tact->set_active (true);
268 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
272 Editor::mouse_mode_toggled (MouseMode m)
274 if (ARDOUR::Profile->get_mixbus()) {
275 if ( m == MouseCut) m = MouseObject;
278 Glib::RefPtr<Action> act = get_mouse_mode_action(m);
279 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
281 if (!tact->get_active()) {
282 /* this was just the notification that the old mode has been
283 * left. we'll get called again with the new mode active in a
289 if (_session && mouse_mode == MouseAudition) {
290 /* stop transport and reset default speed to avoid oddness with
292 _session->request_transport_speed (0.0, true);
295 const bool was_internal = internal_editing();
299 /* Switch snap type/mode if we're moving to/from an internal tool. Note
300 this must toggle the actions and not call set_snap_*() directly,
301 otherwise things get out of sync and the combo box stops working. */
302 if (!was_internal && internal_editing()) {
303 snap_type_action(internal_snap_type)->set_active(true);
304 snap_mode_action(internal_snap_mode)->set_active(true);
305 } else if (was_internal && !internal_editing()) {
306 snap_type_action(pre_internal_snap_type)->set_active(true);
307 snap_mode_action(pre_internal_snap_mode)->set_active(true);
312 /* this should generate a new enter event which will
313 trigger the appropiate cursor.
317 _track_canvas->re_enter ();
320 set_gain_envelope_visibility ();
322 update_time_selection_display ();
324 update_all_enter_cursors ();
326 MouseModeChanged (); /* EMIT SIGNAL */
330 Editor::internal_editing() const
332 return mouse_mode == Editing::MouseContent || mouse_mode == Editing::MouseDraw;
336 Editor::update_time_selection_display ()
338 if (smart_mode_action->get_active()) {
339 /* not sure what to do here */
340 if (mouse_mode == MouseObject) {
344 switch (mouse_mode) {
346 selection->clear_objects ();
347 selection->ClearMidiNoteSelection(); //signal
350 selection->clear_objects ();
351 selection->clear_time ();
352 selection->clear_tracks ();
353 selection->ClearMidiNoteSelection(); //signal
357 //if we go into internal editing, clear everything in the outside world
358 selection->clear_objects ();
359 selection->clear_time ();
360 selection->clear_tracks ();
364 selection->clear_objects ();
365 selection->clear_time ();
366 selection->clear_tracks ();
373 Editor::step_mouse_mode (bool next)
375 const int n_mouse_modes = (int)MouseContent + 1;
376 int current = (int)current_mouse_mode();
378 set_mouse_mode((MouseMode)((current + 1) % n_mouse_modes));
380 set_mouse_mode((MouseMode)((current + n_mouse_modes - 1) % n_mouse_modes));
385 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
387 /* in object/audition/timefx/gain-automation mode,
388 any button press sets the selection if the object
389 can be selected. this is a bit of hack, because
390 we want to avoid this if the mouse operation is a
393 note: not dbl-click or triple-click
395 Also note that there is no region selection in internal edit mode, otherwise
396 for operations operating on the selection (e.g. cut) it is not obvious whether
397 to cut notes or regions.
400 MouseMode eff_mouse_mode = effective_mouse_mode ();
402 if (eff_mouse_mode == MouseCut) {
403 /* never change selection in cut mode */
407 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
408 /* context clicks are always about object properties, even if
409 we're in range mode within smart mode.
411 eff_mouse_mode = MouseObject;
414 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
415 if (get_smart_mode()) {
417 case FadeInHandleItem:
418 case FadeInTrimHandleItem:
419 case FadeOutHandleItem:
420 case FadeOutTrimHandleItem:
421 eff_mouse_mode = MouseObject;
428 if (((mouse_mode != MouseObject) &&
429 (mouse_mode != MouseAudition || item_type != RegionItem) &&
430 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
431 (mouse_mode != MouseDraw)) ||
432 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
436 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
438 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
440 /* almost no selection action on modified button-2 or button-3 events */
442 if (item_type != RegionItem && event->button.button != 2) {
448 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
449 bool press = (event->type == GDK_BUTTON_PRESS);
454 if (eff_mouse_mode != MouseRange) {
455 set_selected_regionview_from_click (press, op);
457 /* don't change the selection unless the
458 clicked track is not currently selected. if
459 so, "collapse" the selection to just this
462 if (!selection->selected (clicked_axisview)) {
463 set_selected_track_as_side_effect (Selection::Set);
467 if (eff_mouse_mode != MouseRange) {
468 set_selected_regionview_from_click (press, op);
473 case RegionViewNameHighlight:
475 case LeftFrameHandle:
476 case RightFrameHandle:
477 case FadeInHandleItem:
478 case FadeInTrimHandleItem:
480 case FadeOutHandleItem:
481 case FadeOutTrimHandleItem:
483 case StartCrossFadeItem:
484 case EndCrossFadeItem:
485 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
486 set_selected_regionview_from_click (press, op);
487 } else if (event->type == GDK_BUTTON_PRESS) {
488 set_selected_track_as_side_effect (op);
492 case ControlPointItem:
493 set_selected_track_as_side_effect (op);
494 if (eff_mouse_mode != MouseRange) {
495 set_selected_control_point_from_click (press, op);
500 /* for context click, select track */
501 if (event->button.button == 3) {
502 selection->clear_tracks ();
503 set_selected_track_as_side_effect (op);
507 case AutomationTrackItem:
508 set_selected_track_as_side_effect (op);
517 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
519 /* single mouse clicks on any of these item types operate
520 independent of mouse mode, mostly because they are
521 not on the main track canvas or because we want
525 NoteBase* note = NULL;
528 case PlayheadCursorItem:
529 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
533 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
534 hide_marker (item, event);
536 _drags->set (new MarkerDrag (this, item), event);
540 case TempoMarkerItem:
543 new TempoMarkerDrag (
546 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
553 case MeterMarkerItem:
556 new MeterMarkerDrag (
559 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
567 _drags->set (new VideoTimeLineDrag (this, item), event);
574 case TimecodeRulerItem:
575 case SamplesRulerItem:
576 case MinsecRulerItem:
578 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
579 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
585 case RangeMarkerBarItem:
586 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
587 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
588 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
589 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
591 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
596 case CdMarkerBarItem:
597 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
598 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
600 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
605 case TransportMarkerBarItem:
606 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
607 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
609 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
618 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
619 /* special case: allow trim of range selections in joined object mode;
620 in theory eff should equal MouseRange in this case, but it doesn't
621 because entering the range selection canvas item results in entered_regionview
622 being set to 0, so update_join_object_range_location acts as if we aren't
625 if (item_type == StartSelectionTrimItem) {
626 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
627 } else if (item_type == EndSelectionTrimItem) {
628 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
632 Editing::MouseMode eff = effective_mouse_mode ();
634 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
635 if (get_smart_mode()) {
637 case FadeInHandleItem:
638 case FadeInTrimHandleItem:
639 case FadeOutHandleItem:
640 case FadeOutTrimHandleItem:
651 case StartSelectionTrimItem:
652 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
655 case EndSelectionTrimItem:
656 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
660 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
661 start_selection_grab (item, event);
663 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
664 /* grab selection for moving */
665 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
667 /* this was debated, but decided the more common action was to
668 make a new selection */
669 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
674 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
675 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
677 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
682 case RegionViewNameHighlight:
683 if (!clicked_regionview->region()->locked()) {
684 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
690 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
691 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
693 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
702 case FadeInHandleItem:
703 case FadeOutHandleItem:
704 case LeftFrameHandle:
705 case RightFrameHandle:
706 case FeatureLineItem:
707 case RegionViewNameHighlight:
710 case AutomationTrackItem:
711 _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, get_canvas_cursor());
722 /* Existing note: allow trimming/motion */
723 if ((note = reinterpret_cast<NoteBase*> (item->get_data ("notebase")))) {
724 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
725 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
727 _drags->set (new NoteDrag (this, item), event);
732 case ControlPointItem:
733 _drags->set (new ControlPointDrag (this, item), event);
738 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
739 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
744 case AutomationTrackItem:
745 /* rubberband drag to select automation points */
746 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
755 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
756 event->type == GDK_BUTTON_PRESS) {
758 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
760 } else if (event->type == GDK_BUTTON_PRESS) {
763 case FadeInHandleItem:
765 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
769 case FadeOutHandleItem:
771 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
775 case StartCrossFadeItem:
776 case EndCrossFadeItem:
777 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
778 // if (!clicked_regionview->region()->locked()) {
779 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
784 case FeatureLineItem:
786 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
787 remove_transient(item);
791 _drags->set (new FeatureLineDrag (this, item), event);
797 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
798 /* click on an automation region view; do nothing here and let the ARV's signal handler
804 /* click on a normal region view */
805 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
806 add_region_copy_drag (item, event, clicked_regionview);
807 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
808 add_region_brush_drag (item, event, clicked_regionview);
810 add_region_drag (item, event, clicked_regionview);
814 _drags->start_grab (event);
818 case RegionViewNameHighlight:
819 case LeftFrameHandle:
820 case RightFrameHandle:
821 if (!clicked_regionview->region()->locked()) {
822 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
827 case FadeInTrimHandleItem:
828 case FadeOutTrimHandleItem:
829 if (!clicked_regionview->region()->locked()) {
830 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
837 /* rename happens on edit clicks */
838 if (clicked_regionview->get_name_highlight()) {
839 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
845 case ControlPointItem:
846 _drags->set (new ControlPointDrag (this, item), event);
850 case AutomationLineItem:
851 _drags->set (new LineDrag (this, item), event);
856 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
859 case AutomationTrackItem:
861 TimeAxisView* parent = clicked_axisview->get_parent ();
862 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
864 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
866 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
868 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
869 if (pl->n_regions() == 0) {
870 /* Parent has no regions; create one so that we have somewhere to put automation */
871 _drags->set (new RegionCreateDrag (this, item, parent), event);
873 /* See if there's a region before the click that we can extend, and extend it if so */
874 framepos_t const t = canvas_event_sample (event);
875 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
877 _drags->set (new RegionCreateDrag (this, item, parent), event);
879 prev->set_length (t - prev->position ());
883 /* rubberband drag to select automation points */
884 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
908 _drags->set (new LineDrag (this, item), event);
911 case ControlPointItem:
912 _drags->set (new ControlPointDrag (this, item), event);
918 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
920 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
922 double const y = event->button.y;
923 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
925 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
927 /* smart "join" mode: drag automation */
928 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
936 case AutomationLineItem:
937 _drags->set (new LineDrag (this, item), event);
941 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
942 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
943 /* Note is big and pointer is near the end, trim */
944 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
947 _drags->set (new NoteDrag (this, item), event);
954 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
955 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
966 if (item_type == NoteItem) {
967 /* resize-drag notes */
968 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
969 if (note->big_enough_to_trim()) {
970 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
974 } else if (clicked_regionview) {
976 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
982 _drags->set (new ScrubDrag (this, item), event, _cursors->transparent);
984 scrub_reverse_distance = 0;
985 last_scrub_x = event->button.x;
986 scrubbing_direction = 0;
998 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1000 Editing::MouseMode const eff = effective_mouse_mode ();
1003 switch (item_type) {
1005 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1006 add_region_copy_drag (item, event, clicked_regionview);
1008 add_region_drag (item, event, clicked_regionview);
1010 _drags->start_grab (event);
1013 case ControlPointItem:
1014 _drags->set (new ControlPointDrag (this, item), event);
1022 switch (item_type) {
1023 case RegionViewNameHighlight:
1024 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1028 case LeftFrameHandle:
1029 case RightFrameHandle:
1030 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1034 case RegionViewName:
1035 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1049 /* relax till release */
1061 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1063 if (event->type == GDK_2BUTTON_PRESS) {
1064 _drags->mark_double_click ();
1065 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1069 if (event->type != GDK_BUTTON_PRESS) {
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 ) && ARDOUR_UI::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 _press_cursor_ctx.reset();
1146 /* no action if we're recording */
1148 if (_session && _session->actively_recording()) {
1152 /* see if we're finishing a drag */
1154 bool were_dragging = false;
1155 if (_drags->active ()) {
1156 bool const r = _drags->end_grab (event);
1158 /* grab dragged, so do nothing else */
1162 were_dragging = true;
1165 update_region_layering_order_editor ();
1167 /* edit events get handled here */
1169 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1170 switch (item_type) {
1172 show_region_properties ();
1175 case TempoMarkerItem: {
1177 TempoMarker* tempo_marker;
1179 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1180 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1181 abort(); /*NOTREACHED*/
1184 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1185 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1186 abort(); /*NOTREACHED*/
1189 edit_tempo_marker (*tempo_marker);
1193 case MeterMarkerItem: {
1195 MeterMarker* meter_marker;
1197 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1198 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1199 abort(); /*NOTREACHED*/
1202 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1203 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1204 abort(); /*NOTREACHED*/
1206 edit_meter_marker (*meter_marker);
1210 case RegionViewName:
1211 if (clicked_regionview->name_active()) {
1212 return mouse_rename_region (item, event);
1216 case ControlPointItem:
1217 edit_control_point (item);
1226 /* context menu events get handled here */
1227 if (Keyboard::is_context_menu_event (&event->button)) {
1229 context_click_event = *event;
1231 if (!_drags->active ()) {
1233 /* no matter which button pops up the context menu, tell the menu
1234 widget to use button 1 to drive menu selection.
1237 switch (item_type) {
1239 case FadeInHandleItem:
1240 case FadeInTrimHandleItem:
1241 case StartCrossFadeItem:
1242 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1246 case FadeOutHandleItem:
1247 case FadeOutTrimHandleItem:
1248 case EndCrossFadeItem:
1249 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1252 case LeftFrameHandle:
1253 case RightFrameHandle:
1257 popup_track_context_menu (1, event->button.time, item_type, false);
1261 case RegionViewNameHighlight:
1262 case RegionViewName:
1263 popup_track_context_menu (1, event->button.time, item_type, false);
1267 popup_track_context_menu (1, event->button.time, item_type, true);
1270 case AutomationTrackItem:
1271 popup_track_context_menu (1, event->button.time, item_type, false);
1275 case RangeMarkerBarItem:
1276 case TransportMarkerBarItem:
1277 case CdMarkerBarItem:
1281 case TimecodeRulerItem:
1282 case SamplesRulerItem:
1283 case MinsecRulerItem:
1285 popup_ruler_menu (where, item_type);
1289 marker_context_menu (&event->button, item);
1292 case TempoMarkerItem:
1293 tempo_or_meter_marker_context_menu (&event->button, item);
1296 case MeterMarkerItem:
1297 tempo_or_meter_marker_context_menu (&event->button, item);
1300 case CrossfadeViewItem:
1301 popup_track_context_menu (1, event->button.time, item_type, false);
1304 case ControlPointItem:
1305 popup_control_point_context_menu (item, event);
1309 if (internal_editing()) {
1310 popup_note_context_menu (item, event);
1322 /* delete events get handled here */
1324 Editing::MouseMode const eff = effective_mouse_mode ();
1326 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1328 switch (item_type) {
1329 case TempoMarkerItem:
1330 remove_tempo_marker (item);
1333 case MeterMarkerItem:
1334 remove_meter_marker (item);
1338 remove_marker (*item, event);
1342 if (eff == MouseObject) {
1343 remove_clicked_region ();
1347 case ControlPointItem:
1348 remove_control_point (item);
1352 remove_midi_note (item, event);
1361 switch (event->button.button) {
1364 switch (item_type) {
1365 /* see comments in button_press_handler */
1366 case PlayheadCursorItem:
1369 case AutomationLineItem:
1370 case StartSelectionTrimItem:
1371 case EndSelectionTrimItem:
1375 if (!_dragging_playhead) {
1376 snap_to_with_modifier (where, event, RoundNearest, true);
1377 mouse_add_new_marker (where);
1381 case CdMarkerBarItem:
1382 if (!_dragging_playhead) {
1383 // if we get here then a dragged range wasn't done
1384 snap_to_with_modifier (where, event, RoundNearest, true);
1385 mouse_add_new_marker (where, true);
1390 if (!_dragging_playhead) {
1391 snap_to_with_modifier (where, event);
1392 mouse_add_new_tempo_event (where);
1397 if (!_dragging_playhead) {
1398 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1403 case TimecodeRulerItem:
1404 case SamplesRulerItem:
1405 case MinsecRulerItem:
1416 switch (item_type) {
1419 /* check that we didn't drag before releasing, since
1420 its really annoying to create new control
1421 points when doing this.
1423 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1424 if (!were_dragging && arv) {
1425 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1426 arv->add_gain_point_event (item, event, with_guard_points);
1432 case AutomationTrackItem: {
1433 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1434 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1436 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1447 if (scrubbing_direction == 0) {
1448 /* no drag, just a click */
1449 switch (item_type) {
1451 play_selected_region ();
1457 /* make sure we stop */
1458 _session->request_transport_speed (0.0);
1467 /* do any (de)selection operations that should occur on button release */
1469 begin_reversible_selection_op (_("Button Select"));
1470 button_selection (item, event, item_type);
1471 commit_reversible_selection_op ();
1481 switch (item_type) {
1483 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1485 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1488 // Button2 click is unused
1503 // x_style_paste (where, 1.0);
1524 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1531 /* by the time we reach here, entered_regionview and entered trackview
1532 * will have already been set as appropriate. Things are done this
1533 * way because this method isn't passed a pointer to a variable type of
1534 * thing that is entered (which may or may not be canvas item).
1535 * (e.g. the actual entered regionview)
1538 choose_canvas_cursor_on_entry (item_type);
1540 switch (item_type) {
1541 case ControlPointItem:
1542 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1543 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1546 fraction = 1.0 - (cp->get_y() / cp->line().height());
1548 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1549 _verbose_cursor->show ();
1554 if (mouse_mode == MouseDraw) {
1555 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1557 line->set_outline_color (ARDOUR_UI::config()->color ("entered gain line"));
1562 case AutomationLineItem:
1563 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1564 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1566 line->set_outline_color (ARDOUR_UI::config()->color ("entered automation line"));
1571 case AutomationTrackItem:
1572 AutomationTimeAxisView* atv;
1573 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1574 clear_entered_track = false;
1575 set_entered_track (atv);
1580 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1583 entered_marker = marker;
1584 marker->set_color_rgba (ARDOUR_UI::config()->color ("entered marker"));
1586 case MeterMarkerItem:
1587 case TempoMarkerItem:
1590 case FadeInHandleItem:
1591 case FadeInTrimHandleItem:
1592 if (mouse_mode == MouseObject) {
1593 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1595 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1596 rect->set_fill_color (rv->get_fill_color());
1601 case FadeOutHandleItem:
1602 case FadeOutTrimHandleItem:
1603 if (mouse_mode == MouseObject) {
1604 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1606 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1607 rect->set_fill_color (rv->get_fill_color ());
1612 case FeatureLineItem:
1614 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1615 line->set_outline_color (0xFF0000FF);
1626 /* third pass to handle entered track status in a comprehensible way.
1629 switch (item_type) {
1631 case AutomationLineItem:
1632 case ControlPointItem:
1633 /* these do not affect the current entered track state */
1634 clear_entered_track = false;
1637 case AutomationTrackItem:
1638 /* handled above already */
1650 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1658 if (!_enter_stack.empty()) {
1659 _enter_stack.pop_back();
1662 switch (item_type) {
1663 case ControlPointItem:
1664 _verbose_cursor->hide ();
1668 case AutomationLineItem:
1669 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1671 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1673 line->set_outline_color (al->get_line_color());
1679 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1683 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1684 location_flags_changed (loc);
1687 case MeterMarkerItem:
1688 case TempoMarkerItem:
1691 case FadeInTrimHandleItem:
1692 case FadeOutTrimHandleItem:
1693 case FadeInHandleItem:
1694 case FadeOutHandleItem:
1696 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1698 rect->set_fill_color (ARDOUR_UI::config()->color ("inactive fade handle"));
1703 case AutomationTrackItem:
1706 case FeatureLineItem:
1708 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1709 line->set_outline_color (ARDOUR_UI::config()->color ("zero line"));
1721 Editor::scrub (framepos_t frame, double current_x)
1725 if (scrubbing_direction == 0) {
1727 _session->request_locate (frame, false);
1728 _session->request_transport_speed (0.1);
1729 scrubbing_direction = 1;
1733 if (last_scrub_x > current_x) {
1735 /* pointer moved to the left */
1737 if (scrubbing_direction > 0) {
1739 /* we reversed direction to go backwards */
1742 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1746 /* still moving to the left (backwards) */
1748 scrub_reversals = 0;
1749 scrub_reverse_distance = 0;
1751 delta = 0.01 * (last_scrub_x - current_x);
1752 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1756 /* pointer moved to the right */
1758 if (scrubbing_direction < 0) {
1759 /* we reversed direction to go forward */
1762 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1765 /* still moving to the right */
1767 scrub_reversals = 0;
1768 scrub_reverse_distance = 0;
1770 delta = 0.01 * (current_x - last_scrub_x);
1771 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1775 /* if there have been more than 2 opposite motion moves detected, or one that moves
1776 back more than 10 pixels, reverse direction
1779 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1781 if (scrubbing_direction > 0) {
1782 /* was forwards, go backwards */
1783 _session->request_transport_speed (-0.1);
1784 scrubbing_direction = -1;
1786 /* was backwards, go forwards */
1787 _session->request_transport_speed (0.1);
1788 scrubbing_direction = 1;
1791 scrub_reverse_distance = 0;
1792 scrub_reversals = 0;
1796 last_scrub_x = current_x;
1800 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1802 _last_motion_y = event->motion.y;
1804 if (event->motion.is_hint) {
1807 /* We call this so that MOTION_NOTIFY events continue to be
1808 delivered to the canvas. We need to do this because we set
1809 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1810 the density of the events, at the expense of a round-trip
1811 to the server. Given that this will mostly occur on cases
1812 where DISPLAY = :0.0, and given the cost of what the motion
1813 event might do, its a good tradeoff.
1816 _track_canvas->get_pointer (x, y);
1819 if (current_stepping_trackview) {
1820 /* don't keep the persistent stepped trackview if the mouse moves */
1821 current_stepping_trackview = 0;
1822 step_timeout.disconnect ();
1825 if (_session && _session->actively_recording()) {
1826 /* Sorry. no dragging stuff around while we record */
1830 update_join_object_range_location (event->motion.y);
1832 if (_drags->active ()) {
1833 return _drags->motion_handler (event, from_autoscroll);
1840 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1842 ControlPoint* control_point;
1844 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1845 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1846 abort(); /*NOTREACHED*/
1849 AutomationLine& line = control_point->line ();
1850 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
1851 /* we shouldn't remove the first or last gain point in region gain lines */
1852 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
1861 Editor::remove_control_point (ArdourCanvas::Item* item)
1863 if (!can_remove_control_point (item)) {
1867 ControlPoint* control_point;
1869 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1870 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1871 abort(); /*NOTREACHED*/
1874 control_point->line().remove_point (*control_point);
1878 Editor::edit_control_point (ArdourCanvas::Item* item)
1880 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1883 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1884 abort(); /*NOTREACHED*/
1887 ControlPointDialog d (p);
1890 if (d.run () != RESPONSE_ACCEPT) {
1894 p->line().modify_point_y (*p, d.get_y_fraction ());
1898 Editor::edit_notes (MidiRegionView* mrv)
1900 MidiRegionView::Selection const & s = mrv->selection();
1906 EditNoteDialog* d = new EditNoteDialog (mrv, s);
1910 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
1914 Editor::note_edit_done (int r, EditNoteDialog* d)
1921 Editor::visible_order_range (int* low, int* high) const
1923 *low = TimeAxisView::max_order ();
1926 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1928 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1930 if (!rtv->hidden()) {
1932 if (*high < rtv->order()) {
1933 *high = rtv->order ();
1936 if (*low > rtv->order()) {
1937 *low = rtv->order ();
1944 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1946 /* Either add to or set the set the region selection, unless
1947 this is an alignment click (control used)
1950 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1951 TimeAxisView* tv = &rv.get_time_axis_view();
1952 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1954 if (rtv && rtv->is_track()) {
1955 speed = rtv->track()->speed();
1958 framepos_t where = get_preferred_edit_position();
1962 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1964 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
1966 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1968 align_region (rv.region(), End, (framepos_t) (where * speed));
1972 align_region (rv.region(), Start, (framepos_t) (where * speed));
1979 Editor::collect_new_region_view (RegionView* rv)
1981 latest_regionviews.push_back (rv);
1985 Editor::collect_and_select_new_region_view (RegionView* rv)
1988 latest_regionviews.push_back (rv);
1992 Editor::cancel_selection ()
1994 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1995 (*i)->hide_selection ();
1998 selection->clear ();
1999 clicked_selection = 0;
2003 Editor::cancel_time_selection ()
2005 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2006 (*i)->hide_selection ();
2008 selection->time.clear ();
2009 clicked_selection = 0;
2013 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2015 RegionView* rv = clicked_regionview;
2017 /* Choose action dependant on which button was pressed */
2018 switch (event->button.button) {
2020 begin_reversible_command (_("start point trim"));
2022 if (selection->selected (rv)) {
2023 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2024 i != selection->regions.by_layer().end(); ++i)
2026 if (!(*i)->region()->locked()) {
2027 (*i)->region()->clear_changes ();
2028 (*i)->region()->trim_front (new_bound);
2029 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2034 if (!rv->region()->locked()) {
2035 rv->region()->clear_changes ();
2036 rv->region()->trim_front (new_bound);
2037 _session->add_command(new StatefulDiffCommand (rv->region()));
2041 commit_reversible_command();
2045 begin_reversible_command (_("End point trim"));
2047 if (selection->selected (rv)) {
2049 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2051 if (!(*i)->region()->locked()) {
2052 (*i)->region()->clear_changes();
2053 (*i)->region()->trim_end (new_bound);
2054 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2060 if (!rv->region()->locked()) {
2061 rv->region()->clear_changes ();
2062 rv->region()->trim_end (new_bound);
2063 _session->add_command (new StatefulDiffCommand (rv->region()));
2067 commit_reversible_command();
2076 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2081 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2082 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2083 abort(); /*NOTREACHED*/
2086 Location* location = find_location_from_marker (marker, is_start);
2087 location->set_hidden (true, this);
2091 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2093 using namespace Gtkmm2ext;
2095 ArdourPrompter prompter (false);
2097 prompter.set_prompt (_("Name for region:"));
2098 prompter.set_initial_text (clicked_regionview->region()->name());
2099 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2100 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2101 prompter.show_all ();
2102 switch (prompter.run ()) {
2103 case Gtk::RESPONSE_ACCEPT:
2105 prompter.get_result(str);
2107 clicked_regionview->region()->set_name (str);
2116 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2118 /* no brushing without a useful snap setting */
2120 switch (_snap_mode) {
2122 return; /* can't work because it allows region to be placed anywhere */
2127 switch (_snap_type) {
2135 /* don't brush a copy over the original */
2137 if (pos == rv->region()->position()) {
2141 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2143 if (rtv == 0 || !rtv->is_track()) {
2147 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2148 double speed = rtv->track()->speed();
2150 playlist->clear_changes ();
2151 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2152 playlist->add_region (new_region, (framepos_t) (pos * speed));
2153 _session->add_command (new StatefulDiffCommand (playlist));
2155 // playlist is frozen, so we have to update manually XXX this is disgusting
2157 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2161 Editor::track_height_step_timeout ()
2163 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2164 current_stepping_trackview = 0;
2171 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2173 assert (region_view);
2175 if (!region_view->region()->playlist()) {
2179 switch (Config->get_edit_mode()) {
2181 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2184 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2187 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2194 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2196 assert (region_view);
2198 if (!region_view->region()->playlist()) {
2202 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2206 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2208 assert (region_view);
2210 if (!region_view->region()->playlist()) {
2214 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2218 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2220 begin_reversible_command (Operations::drag_region_brush);
2223 /** Start a grab where a time range is selected, track(s) are selected, and the
2224 * user clicks and drags a region with a modifier in order to create a new region containing
2225 * the section of the clicked region that lies within the time range.
2228 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2230 if (clicked_regionview == 0) {
2234 /* lets try to create new Region for the selection */
2236 vector<boost::shared_ptr<Region> > new_regions;
2237 create_region_from_selection (new_regions);
2239 if (new_regions.empty()) {
2243 /* XXX fix me one day to use all new regions */
2245 boost::shared_ptr<Region> region (new_regions.front());
2247 /* add it to the current stream/playlist.
2249 tricky: the streamview for the track will add a new regionview. we will
2250 catch the signal it sends when it creates the regionview to
2251 set the regionview we want to then drag.
2254 latest_regionviews.clear();
2255 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2257 /* A selection grab currently creates two undo/redo operations, one for
2258 creating the new region and another for moving it.
2261 begin_reversible_command (Operations::selection_grab);
2263 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2265 playlist->clear_changes ();
2266 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2267 _session->add_command(new StatefulDiffCommand (playlist));
2271 if (latest_regionviews.empty()) {
2272 /* something went wrong */
2276 /* we need to deselect all other regionviews, and select this one
2277 i'm ignoring undo stuff, because the region creation will take care of it
2280 selection->set (latest_regionviews);
2282 commit_reversible_command ();
2284 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2290 if (_drags->active ()) {
2293 selection->clear ();
2299 /** Update _join_object_range_state which indicate whether we are over the top
2300 * or bottom half of a route view, used by the `join object/range' tool
2301 * mode. Coordinates in canvas space.
2304 Editor::update_join_object_range_location (double y)
2306 if (!get_smart_mode()) {
2307 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2311 JoinObjectRangeState const old = _join_object_range_state;
2313 if (mouse_mode == MouseObject) {
2314 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2315 } else if (mouse_mode == MouseRange) {
2316 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2319 if (entered_regionview) {
2321 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2322 double const c = item_space.y / entered_regionview->height();
2324 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2326 Editor::EnterContext* ctx = get_enter_context(RegionItem);
2327 if (_join_object_range_state != old && ctx) {
2328 ctx->cursor_ctx->change(which_track_cursor());
2331 } else if (entered_track) {
2333 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2335 if (entered_route_view) {
2340 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2342 double track_height = entered_route_view->view()->child_height();
2343 if (ARDOUR_UI::config()->get_show_name_highlight()) {
2344 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2346 double const c = cy / track_height;
2350 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2352 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2356 /* Other kinds of tracks use object mode */
2357 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2360 Editor::EnterContext* ctx = get_enter_context(StreamItem);
2361 if (_join_object_range_state != old && ctx) {
2362 ctx->cursor_ctx->change(which_track_cursor());
2368 Editor::effective_mouse_mode () const
2370 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2372 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2380 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2382 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2385 e->region_view().delete_note (e->note ());
2388 /** Obtain the pointer position in canvas coordinates */
2390 Editor::get_pointer_position (double& x, double& y) const
2393 _track_canvas->get_pointer (px, py);
2394 _track_canvas->window_to_canvas (px, py, x, y);