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 */
1468 button_selection (item, event, item_type);
1477 switch (item_type) {
1479 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1481 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1484 // Button2 click is unused
1499 // x_style_paste (where, 1.0);
1520 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1527 /* by the time we reach here, entered_regionview and entered trackview
1528 * will have already been set as appropriate. Things are done this
1529 * way because this method isn't passed a pointer to a variable type of
1530 * thing that is entered (which may or may not be canvas item).
1531 * (e.g. the actual entered regionview)
1534 choose_canvas_cursor_on_entry (item_type);
1536 switch (item_type) {
1537 case ControlPointItem:
1538 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1539 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1542 fraction = 1.0 - (cp->get_y() / cp->line().height());
1544 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1545 _verbose_cursor->show ();
1550 if (mouse_mode == MouseDraw) {
1551 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1553 line->set_outline_color (ARDOUR_UI::config()->color ("entered gain line"));
1558 case AutomationLineItem:
1559 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1560 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1562 line->set_outline_color (ARDOUR_UI::config()->color ("entered automation line"));
1567 case AutomationTrackItem:
1568 AutomationTimeAxisView* atv;
1569 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1570 clear_entered_track = false;
1571 set_entered_track (atv);
1576 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1579 entered_marker = marker;
1580 marker->set_color_rgba (ARDOUR_UI::config()->color ("entered marker"));
1582 case MeterMarkerItem:
1583 case TempoMarkerItem:
1586 case FadeInHandleItem:
1587 case FadeInTrimHandleItem:
1588 if (mouse_mode == MouseObject) {
1589 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1591 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1592 rect->set_fill_color (rv->get_fill_color());
1597 case FadeOutHandleItem:
1598 case FadeOutTrimHandleItem:
1599 if (mouse_mode == MouseObject) {
1600 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1602 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1603 rect->set_fill_color (rv->get_fill_color ());
1608 case FeatureLineItem:
1610 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1611 line->set_outline_color (0xFF0000FF);
1622 /* third pass to handle entered track status in a comprehensible way.
1625 switch (item_type) {
1627 case AutomationLineItem:
1628 case ControlPointItem:
1629 /* these do not affect the current entered track state */
1630 clear_entered_track = false;
1633 case AutomationTrackItem:
1634 /* handled above already */
1646 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1654 if (!_enter_stack.empty()) {
1655 _enter_stack.pop_back();
1658 switch (item_type) {
1659 case ControlPointItem:
1660 _verbose_cursor->hide ();
1664 case AutomationLineItem:
1665 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1667 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1669 line->set_outline_color (al->get_line_color());
1675 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1679 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1680 location_flags_changed (loc);
1683 case MeterMarkerItem:
1684 case TempoMarkerItem:
1687 case FadeInTrimHandleItem:
1688 case FadeOutTrimHandleItem:
1689 case FadeInHandleItem:
1690 case FadeOutHandleItem:
1692 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1694 rect->set_fill_color (ARDOUR_UI::config()->color ("inactive fade handle"));
1699 case AutomationTrackItem:
1702 case FeatureLineItem:
1704 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1705 line->set_outline_color (ARDOUR_UI::config()->color ("zero line"));
1717 Editor::scrub (framepos_t frame, double current_x)
1721 if (scrubbing_direction == 0) {
1723 _session->request_locate (frame, false);
1724 _session->request_transport_speed (0.1);
1725 scrubbing_direction = 1;
1729 if (last_scrub_x > current_x) {
1731 /* pointer moved to the left */
1733 if (scrubbing_direction > 0) {
1735 /* we reversed direction to go backwards */
1738 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1742 /* still moving to the left (backwards) */
1744 scrub_reversals = 0;
1745 scrub_reverse_distance = 0;
1747 delta = 0.01 * (last_scrub_x - current_x);
1748 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1752 /* pointer moved to the right */
1754 if (scrubbing_direction < 0) {
1755 /* we reversed direction to go forward */
1758 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1761 /* still moving to the right */
1763 scrub_reversals = 0;
1764 scrub_reverse_distance = 0;
1766 delta = 0.01 * (current_x - last_scrub_x);
1767 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1771 /* if there have been more than 2 opposite motion moves detected, or one that moves
1772 back more than 10 pixels, reverse direction
1775 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1777 if (scrubbing_direction > 0) {
1778 /* was forwards, go backwards */
1779 _session->request_transport_speed (-0.1);
1780 scrubbing_direction = -1;
1782 /* was backwards, go forwards */
1783 _session->request_transport_speed (0.1);
1784 scrubbing_direction = 1;
1787 scrub_reverse_distance = 0;
1788 scrub_reversals = 0;
1792 last_scrub_x = current_x;
1796 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1798 _last_motion_y = event->motion.y;
1800 if (event->motion.is_hint) {
1803 /* We call this so that MOTION_NOTIFY events continue to be
1804 delivered to the canvas. We need to do this because we set
1805 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1806 the density of the events, at the expense of a round-trip
1807 to the server. Given that this will mostly occur on cases
1808 where DISPLAY = :0.0, and given the cost of what the motion
1809 event might do, its a good tradeoff.
1812 _track_canvas->get_pointer (x, y);
1815 if (current_stepping_trackview) {
1816 /* don't keep the persistent stepped trackview if the mouse moves */
1817 current_stepping_trackview = 0;
1818 step_timeout.disconnect ();
1821 if (_session && _session->actively_recording()) {
1822 /* Sorry. no dragging stuff around while we record */
1826 update_join_object_range_location (event->motion.y);
1828 if (_drags->active ()) {
1829 return _drags->motion_handler (event, from_autoscroll);
1836 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1838 ControlPoint* control_point;
1840 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1841 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1842 abort(); /*NOTREACHED*/
1845 AutomationLine& line = control_point->line ();
1846 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
1847 /* we shouldn't remove the first or last gain point in region gain lines */
1848 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
1857 Editor::remove_control_point (ArdourCanvas::Item* item)
1859 if (!can_remove_control_point (item)) {
1863 ControlPoint* control_point;
1865 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1866 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1867 abort(); /*NOTREACHED*/
1870 control_point->line().remove_point (*control_point);
1874 Editor::edit_control_point (ArdourCanvas::Item* item)
1876 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1879 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1880 abort(); /*NOTREACHED*/
1883 ControlPointDialog d (p);
1886 if (d.run () != RESPONSE_ACCEPT) {
1890 p->line().modify_point_y (*p, d.get_y_fraction ());
1894 Editor::edit_notes (MidiRegionView* mrv)
1896 MidiRegionView::Selection const & s = mrv->selection();
1902 EditNoteDialog* d = new EditNoteDialog (mrv, s);
1906 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
1910 Editor::note_edit_done (int r, EditNoteDialog* d)
1917 Editor::visible_order_range (int* low, int* high) const
1919 *low = TimeAxisView::max_order ();
1922 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1924 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1926 if (!rtv->hidden()) {
1928 if (*high < rtv->order()) {
1929 *high = rtv->order ();
1932 if (*low > rtv->order()) {
1933 *low = rtv->order ();
1940 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1942 /* Either add to or set the set the region selection, unless
1943 this is an alignment click (control used)
1946 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1947 TimeAxisView* tv = &rv.get_time_axis_view();
1948 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1950 if (rtv && rtv->is_track()) {
1951 speed = rtv->track()->speed();
1954 framepos_t where = get_preferred_edit_position();
1958 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1960 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
1962 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1964 align_region (rv.region(), End, (framepos_t) (where * speed));
1968 align_region (rv.region(), Start, (framepos_t) (where * speed));
1975 Editor::collect_new_region_view (RegionView* rv)
1977 latest_regionviews.push_back (rv);
1981 Editor::collect_and_select_new_region_view (RegionView* rv)
1984 latest_regionviews.push_back (rv);
1988 Editor::cancel_selection ()
1990 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1991 (*i)->hide_selection ();
1994 selection->clear ();
1995 clicked_selection = 0;
1999 Editor::cancel_time_selection ()
2001 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2002 (*i)->hide_selection ();
2004 selection->time.clear ();
2005 clicked_selection = 0;
2009 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2011 RegionView* rv = clicked_regionview;
2013 /* Choose action dependant on which button was pressed */
2014 switch (event->button.button) {
2016 begin_reversible_command (_("start point trim"));
2018 if (selection->selected (rv)) {
2019 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2020 i != selection->regions.by_layer().end(); ++i)
2022 if (!(*i)->region()->locked()) {
2023 (*i)->region()->clear_changes ();
2024 (*i)->region()->trim_front (new_bound);
2025 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2030 if (!rv->region()->locked()) {
2031 rv->region()->clear_changes ();
2032 rv->region()->trim_front (new_bound);
2033 _session->add_command(new StatefulDiffCommand (rv->region()));
2037 commit_reversible_command();
2041 begin_reversible_command (_("End point trim"));
2043 if (selection->selected (rv)) {
2045 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2047 if (!(*i)->region()->locked()) {
2048 (*i)->region()->clear_changes();
2049 (*i)->region()->trim_end (new_bound);
2050 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2056 if (!rv->region()->locked()) {
2057 rv->region()->clear_changes ();
2058 rv->region()->trim_end (new_bound);
2059 _session->add_command (new StatefulDiffCommand (rv->region()));
2063 commit_reversible_command();
2072 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2077 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2078 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2079 abort(); /*NOTREACHED*/
2082 Location* location = find_location_from_marker (marker, is_start);
2083 location->set_hidden (true, this);
2087 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2089 using namespace Gtkmm2ext;
2091 ArdourPrompter prompter (false);
2093 prompter.set_prompt (_("Name for region:"));
2094 prompter.set_initial_text (clicked_regionview->region()->name());
2095 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2096 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2097 prompter.show_all ();
2098 switch (prompter.run ()) {
2099 case Gtk::RESPONSE_ACCEPT:
2101 prompter.get_result(str);
2103 clicked_regionview->region()->set_name (str);
2112 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2114 /* no brushing without a useful snap setting */
2116 switch (_snap_mode) {
2118 return; /* can't work because it allows region to be placed anywhere */
2123 switch (_snap_type) {
2131 /* don't brush a copy over the original */
2133 if (pos == rv->region()->position()) {
2137 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2139 if (rtv == 0 || !rtv->is_track()) {
2143 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2144 double speed = rtv->track()->speed();
2146 playlist->clear_changes ();
2147 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2148 playlist->add_region (new_region, (framepos_t) (pos * speed));
2149 _session->add_command (new StatefulDiffCommand (playlist));
2151 // playlist is frozen, so we have to update manually XXX this is disgusting
2153 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2157 Editor::track_height_step_timeout ()
2159 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2160 current_stepping_trackview = 0;
2167 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2169 assert (region_view);
2171 if (!region_view->region()->playlist()) {
2175 switch (Config->get_edit_mode()) {
2177 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2180 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2183 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2190 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2192 assert (region_view);
2194 if (!region_view->region()->playlist()) {
2198 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2202 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2204 assert (region_view);
2206 if (!region_view->region()->playlist()) {
2210 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2214 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2216 begin_reversible_command (Operations::drag_region_brush);
2219 /** Start a grab where a time range is selected, track(s) are selected, and the
2220 * user clicks and drags a region with a modifier in order to create a new region containing
2221 * the section of the clicked region that lies within the time range.
2224 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2226 if (clicked_regionview == 0) {
2230 /* lets try to create new Region for the selection */
2232 vector<boost::shared_ptr<Region> > new_regions;
2233 create_region_from_selection (new_regions);
2235 if (new_regions.empty()) {
2239 /* XXX fix me one day to use all new regions */
2241 boost::shared_ptr<Region> region (new_regions.front());
2243 /* add it to the current stream/playlist.
2245 tricky: the streamview for the track will add a new regionview. we will
2246 catch the signal it sends when it creates the regionview to
2247 set the regionview we want to then drag.
2250 latest_regionviews.clear();
2251 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2253 /* A selection grab currently creates two undo/redo operations, one for
2254 creating the new region and another for moving it.
2257 begin_reversible_command (Operations::selection_grab);
2259 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2261 playlist->clear_changes ();
2262 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2263 _session->add_command(new StatefulDiffCommand (playlist));
2267 if (latest_regionviews.empty()) {
2268 /* something went wrong */
2272 /* we need to deselect all other regionviews, and select this one
2273 i'm ignoring undo stuff, because the region creation will take care of it
2276 selection->set (latest_regionviews);
2278 commit_reversible_command ();
2280 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2286 if (_drags->active ()) {
2289 selection->clear ();
2295 /** Update _join_object_range_state which indicate whether we are over the top
2296 * or bottom half of a route view, used by the `join object/range' tool
2297 * mode. Coordinates in canvas space.
2300 Editor::update_join_object_range_location (double y)
2302 if (!get_smart_mode()) {
2303 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2307 JoinObjectRangeState const old = _join_object_range_state;
2309 if (mouse_mode == MouseObject) {
2310 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2311 } else if (mouse_mode == MouseRange) {
2312 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2315 if (entered_regionview) {
2317 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2318 double const c = item_space.y / entered_regionview->height();
2320 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2322 Editor::EnterContext* ctx = get_enter_context(RegionItem);
2323 if (_join_object_range_state != old && ctx) {
2324 ctx->cursor_ctx->change(which_track_cursor());
2327 } else if (entered_track) {
2329 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2331 if (entered_route_view) {
2336 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2338 double track_height = entered_route_view->view()->child_height();
2339 if (ARDOUR_UI::config()->get_show_name_highlight()) {
2340 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2342 double const c = cy / track_height;
2346 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2348 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2352 /* Other kinds of tracks use object mode */
2353 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2356 Editor::EnterContext* ctx = get_enter_context(StreamItem);
2357 if (_join_object_range_state != old && ctx) {
2358 ctx->cursor_ctx->change(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);