2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include "pbd/error.h"
30 #include "pbd/enumwriter.h"
31 #include "pbd/memento_command.h"
32 #include "pbd/basename.h"
33 #include "pbd/stateful_diff_command.h"
35 #include "gtkmm2ext/bindings.h"
36 #include "gtkmm2ext/utils.h"
37 #include "gtkmm2ext/tearoff.h"
39 #include "canvas/canvas.h"
41 #include "ardour/audioregion.h"
42 #include "ardour/operations.h"
43 #include "ardour/playlist.h"
44 #include "ardour/profile.h"
45 #include "ardour/region_factory.h"
46 #include "ardour/route.h"
47 #include "ardour/session.h"
48 #include "ardour/types.h"
50 #include "ardour_ui.h"
53 #include "time_axis_view.h"
54 #include "audio_time_axis.h"
55 #include "audio_region_view.h"
56 #include "midi_region_view.h"
58 #include "streamview.h"
59 #include "region_gain_line.h"
60 #include "automation_time_axis.h"
61 #include "control_point.h"
63 #include "selection.h"
66 #include "rgb_macros.h"
67 #include "control_point_dialog.h"
68 #include "editor_drag.h"
69 #include "automation_region_view.h"
70 #include "edit_note_dialog.h"
71 #include "mouse_cursors.h"
72 #include "editor_cursors.h"
73 #include "verbose_cursor.h"
79 using namespace ARDOUR;
82 using namespace Editing;
83 using Gtkmm2ext::Keyboard;
86 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
88 /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
89 pays attentions to subwindows. this means that menu windows are ignored, and
90 if the pointer is in a menu, the return window from the call will be the
91 the regular subwindow *under* the menu.
93 this matters quite a lot if the pointer is moving around in a menu that overlaps
94 the track canvas because we will believe that we are within the track canvas
95 when we are not. therefore, we track enter/leave events for the track canvas
96 and allow that to override the result of gdk_window_get_pointer().
99 if (!within_track_canvas) {
104 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
106 if (!canvas_window) {
110 Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
112 if (!pointer_window) {
116 if (pointer_window != canvas_window) {
117 in_track_canvas = false;
121 in_track_canvas = true;
124 event.type = GDK_BUTTON_RELEASE;
128 where = window_event_sample (&event, 0, 0);
134 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
136 ArdourCanvas::Duple d;
138 if (!gdk_event_get_coords (event, &d.x, &d.y)) {
142 /* event coordinates are in window units, so convert to canvas
145 d = _track_canvas->window_to_canvas (d);
155 return pixel_to_sample (d.x);
159 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
164 /* event coordinates are already in canvas units */
166 if (!gdk_event_get_coords (event, &x, &y)) {
167 cerr << "!NO c COORDS for event type " << event->type << endl;
179 /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel
180 position is negative (as can be the case with motion events in particular),
181 the frame location is always positive.
184 return pixel_to_sample_from_event (x);
188 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
190 boost::shared_ptr<Trimmable> st = _trimmable.lock();
192 if (!st || st == t) {
198 Editor::set_current_movable (boost::shared_ptr<Movable> m)
200 boost::shared_ptr<Movable> sm = _movable.lock();
202 if (!sm || sm != m) {
208 Editor::mouse_mode_object_range_toggled()
210 MouseMode m = mouse_mode;
212 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
214 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
217 if (tact->get_active()) {
218 m = MouseObject; //Smart mode turned to ON, force editing to Object mode
221 set_mouse_mode(m, true); //call this so the button styles can get updated
224 static Glib::RefPtr<Action>
225 get_mouse_mode_action(MouseMode m)
229 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
231 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
233 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
235 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
237 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
239 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-content"));
241 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
243 return Glib::RefPtr<Action>();
247 Editor::set_mouse_mode (MouseMode m, bool force)
249 if (_drags->active ()) {
253 if (!force && m == mouse_mode) {
257 if (ARDOUR::Profile->get_mixbus()) {
258 if ( m == MouseCut) m = MouseObject;
261 Glib::RefPtr<Action> act = get_mouse_mode_action(m);
262 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
264 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
265 tact->set_active (false);
266 tact->set_active (true);
268 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
272 Editor::mouse_mode_toggled (MouseMode m)
274 if (ARDOUR::Profile->get_mixbus()) {
275 if ( m == MouseCut) m = MouseObject;
278 Glib::RefPtr<Action> act = get_mouse_mode_action(m);
279 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
281 if (!tact->get_active()) {
282 /* this was just the notification that the old mode has been
283 * left. we'll get called again with the new mode active in a
289 if (_session && mouse_mode == MouseAudition) {
290 /* stop transport and reset default speed to avoid oddness with
292 _session->request_transport_speed (0.0, true);
295 const bool was_internal = internal_editing();
299 if (!was_internal && internal_editing()) {
300 /* switched to internal, switch to internal snap settings */
301 set_snap_to(internal_snap_type);
302 set_snap_mode(internal_snap_mode);
303 } else if (was_internal && !internal_editing()) {
304 /* switched out of internal, switch to non-internal snap settings */
305 set_snap_to(pre_internal_snap_type);
306 set_snap_mode(pre_internal_snap_mode);
311 /* this should generate a new enter event which will
312 trigger the appropiate cursor.
316 _track_canvas->re_enter ();
319 set_gain_envelope_visibility ();
321 update_time_selection_display ();
323 MouseModeChanged (); /* EMIT SIGNAL */
327 Editor::internal_editing() const
329 return mouse_mode == Editing::MouseContent || mouse_mode == Editing::MouseDraw;
333 Editor::update_time_selection_display ()
335 if (smart_mode_action->get_active()) {
336 /* not sure what to do here */
337 if (mouse_mode == MouseObject) {
341 switch (mouse_mode) {
343 selection->clear_objects ();
344 selection->ClearMidiNoteSelection(); //signal
347 selection->clear_objects ();
348 selection->clear_time ();
349 selection->clear_tracks ();
350 selection->ClearMidiNoteSelection(); //signal
354 //if we go into internal editing, clear everything in the outside world
355 selection->clear_objects ();
356 selection->clear_time ();
357 selection->clear_tracks ();
361 selection->clear_objects ();
362 selection->clear_time ();
363 selection->clear_tracks ();
370 Editor::step_mouse_mode (bool next)
372 const int n_mouse_modes = (int)MouseContent + 1;
373 int current = (int)current_mouse_mode();
375 set_mouse_mode((MouseMode)((current + 1) % n_mouse_modes));
377 set_mouse_mode((MouseMode)((current + n_mouse_modes - 1) % n_mouse_modes));
382 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
384 /* in object/audition/timefx/gain-automation mode,
385 any button press sets the selection if the object
386 can be selected. this is a bit of hack, because
387 we want to avoid this if the mouse operation is a
390 note: not dbl-click or triple-click
392 Also note that there is no region selection in internal edit mode, otherwise
393 for operations operating on the selection (e.g. cut) it is not obvious whether
394 to cut notes or regions.
397 MouseMode eff_mouse_mode = effective_mouse_mode ();
399 if (eff_mouse_mode == MouseCut) {
400 /* never change selection in cut mode */
404 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
405 /* context clicks are always about object properties, even if
406 we're in range mode within smart mode.
408 eff_mouse_mode = MouseObject;
411 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
412 if (get_smart_mode()) {
414 case FadeInHandleItem:
415 case FadeInTrimHandleItem:
416 case FadeOutHandleItem:
417 case FadeOutTrimHandleItem:
418 eff_mouse_mode = MouseObject;
425 if (((mouse_mode != MouseObject) &&
426 (mouse_mode != MouseAudition || item_type != RegionItem) &&
427 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
428 (mouse_mode != MouseDraw)) ||
429 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
433 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
435 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
437 /* almost no selection action on modified button-2 or button-3 events */
439 if (item_type != RegionItem && event->button.button != 2) {
445 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
446 bool press = (event->type == GDK_BUTTON_PRESS);
451 if (eff_mouse_mode != MouseRange) {
452 set_selected_regionview_from_click (press, op);
454 /* don't change the selection unless the
455 clicked track is not currently selected. if
456 so, "collapse" the selection to just this
459 if (!selection->selected (clicked_axisview)) {
460 set_selected_track_as_side_effect (Selection::Set);
464 if (eff_mouse_mode != MouseRange) {
465 set_selected_regionview_from_click (press, op);
470 case RegionViewNameHighlight:
472 case LeftFrameHandle:
473 case RightFrameHandle:
474 case FadeInHandleItem:
475 case FadeInTrimHandleItem:
477 case FadeOutHandleItem:
478 case FadeOutTrimHandleItem:
480 case StartCrossFadeItem:
481 case EndCrossFadeItem:
482 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
483 set_selected_regionview_from_click (press, op);
484 } else if (event->type == GDK_BUTTON_PRESS) {
485 set_selected_track_as_side_effect (op);
489 case ControlPointItem:
490 set_selected_track_as_side_effect (op);
491 if (eff_mouse_mode != MouseRange) {
492 set_selected_control_point_from_click (press, op);
497 /* for context click, select track */
498 if (event->button.button == 3) {
499 selection->clear_tracks ();
500 set_selected_track_as_side_effect (op);
504 case AutomationTrackItem:
505 set_selected_track_as_side_effect (op);
514 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
516 /* single mouse clicks on any of these item types operate
517 independent of mouse mode, mostly because they are
518 not on the main track canvas or because we want
522 NoteBase* note = NULL;
525 case PlayheadCursorItem:
526 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
530 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
531 hide_marker (item, event);
533 _drags->set (new MarkerDrag (this, item), event);
537 case TempoMarkerItem:
540 new TempoMarkerDrag (
543 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
550 case MeterMarkerItem:
553 new MeterMarkerDrag (
556 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
564 _drags->set (new VideoTimeLineDrag (this, item), event);
571 case TimecodeRulerItem:
572 case SamplesRulerItem:
573 case MinsecRulerItem:
575 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
576 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
582 case RangeMarkerBarItem:
583 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
584 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
585 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
586 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
588 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
593 case CdMarkerBarItem:
594 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
595 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
597 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
602 case TransportMarkerBarItem:
603 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
604 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
606 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
615 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
616 /* special case: allow trim of range selections in joined object mode;
617 in theory eff should equal MouseRange in this case, but it doesn't
618 because entering the range selection canvas item results in entered_regionview
619 being set to 0, so update_join_object_range_location acts as if we aren't
622 if (item_type == StartSelectionTrimItem) {
623 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
624 } else if (item_type == EndSelectionTrimItem) {
625 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
629 Editing::MouseMode eff = effective_mouse_mode ();
631 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
632 if (get_smart_mode()) {
634 case FadeInHandleItem:
635 case FadeInTrimHandleItem:
636 case FadeOutHandleItem:
637 case FadeOutTrimHandleItem:
648 case StartSelectionTrimItem:
649 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
652 case EndSelectionTrimItem:
653 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
657 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
658 start_selection_grab (item, event);
660 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
661 /* grab selection for moving */
662 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
664 /* this was debated, but decided the more common action was to
665 make a new selection */
666 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
671 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
672 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
674 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
679 case RegionViewNameHighlight:
680 if (!clicked_regionview->region()->locked()) {
681 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
687 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
688 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
690 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
699 case FadeInHandleItem:
700 case FadeOutHandleItem:
701 case LeftFrameHandle:
702 case RightFrameHandle:
703 case FeatureLineItem:
704 case RegionViewNameHighlight:
707 case AutomationTrackItem:
708 _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, get_canvas_cursor());
719 /* Existing note: allow trimming/motion */
720 if ((note = reinterpret_cast<NoteBase*> (item->get_data ("notebase")))) {
721 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
722 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
724 _drags->set (new NoteDrag (this, item), event);
729 case ControlPointItem:
730 _drags->set (new ControlPointDrag (this, item), event);
735 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
736 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
741 case AutomationTrackItem:
742 /* rubberband drag to select automation points */
743 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
752 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
753 event->type == GDK_BUTTON_PRESS) {
755 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
757 } else if (event->type == GDK_BUTTON_PRESS) {
760 case FadeInHandleItem:
762 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
766 case FadeOutHandleItem:
768 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
772 case StartCrossFadeItem:
773 case EndCrossFadeItem:
774 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
775 // if (!clicked_regionview->region()->locked()) {
776 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
781 case FeatureLineItem:
783 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
784 remove_transient(item);
788 _drags->set (new FeatureLineDrag (this, item), event);
794 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
795 /* click on an automation region view; do nothing here and let the ARV's signal handler
801 /* click on a normal region view */
802 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
803 add_region_copy_drag (item, event, clicked_regionview);
804 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
805 add_region_brush_drag (item, event, clicked_regionview);
807 add_region_drag (item, event, clicked_regionview);
811 _drags->start_grab (event);
815 case RegionViewNameHighlight:
816 case LeftFrameHandle:
817 case RightFrameHandle:
818 if (!clicked_regionview->region()->locked()) {
819 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
824 case FadeInTrimHandleItem:
825 case FadeOutTrimHandleItem:
826 if (!clicked_regionview->region()->locked()) {
827 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
834 /* rename happens on edit clicks */
835 if (clicked_regionview->get_name_highlight()) {
836 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
842 case ControlPointItem:
843 _drags->set (new ControlPointDrag (this, item), event);
847 case AutomationLineItem:
848 _drags->set (new LineDrag (this, item), event);
853 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
856 case AutomationTrackItem:
858 TimeAxisView* parent = clicked_axisview->get_parent ();
859 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
861 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
863 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
865 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
866 if (pl->n_regions() == 0) {
867 /* Parent has no regions; create one so that we have somewhere to put automation */
868 _drags->set (new RegionCreateDrag (this, item, parent), event);
870 /* See if there's a region before the click that we can extend, and extend it if so */
871 framepos_t const t = canvas_event_sample (event);
872 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
874 _drags->set (new RegionCreateDrag (this, item, parent), event);
876 prev->set_length (t - prev->position ());
880 /* rubberband drag to select automation points */
881 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
905 _drags->set (new LineDrag (this, item), event);
908 case ControlPointItem:
909 _drags->set (new ControlPointDrag (this, item), event);
915 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
917 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
919 double const y = event->button.y;
920 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
922 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
924 /* smart "join" mode: drag automation */
925 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
933 case AutomationLineItem:
934 _drags->set (new LineDrag (this, item), event);
938 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
939 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
940 /* Note is big and pointer is near the end, trim */
941 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
944 _drags->set (new NoteDrag (this, item), event);
951 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
952 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
963 if (item_type == NoteItem) {
964 /* resize-drag notes */
965 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
966 if (note->big_enough_to_trim()) {
967 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
971 } else if (clicked_regionview) {
973 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
979 _drags->set (new ScrubDrag (this, item), event, _cursors->transparent);
981 scrub_reverse_distance = 0;
982 last_scrub_x = event->button.x;
983 scrubbing_direction = 0;
995 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
997 Editing::MouseMode const eff = effective_mouse_mode ();
1000 switch (item_type) {
1002 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1003 add_region_copy_drag (item, event, clicked_regionview);
1005 add_region_drag (item, event, clicked_regionview);
1007 _drags->start_grab (event);
1010 case ControlPointItem:
1011 _drags->set (new ControlPointDrag (this, item), event);
1019 switch (item_type) {
1020 case RegionViewNameHighlight:
1021 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1025 case LeftFrameHandle:
1026 case RightFrameHandle:
1027 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1031 case RegionViewName:
1032 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1046 /* relax till release */
1058 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1060 if (event->type == GDK_2BUTTON_PRESS) {
1061 _drags->mark_double_click ();
1062 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1066 if (event->type != GDK_BUTTON_PRESS) {
1070 _track_canvas->grab_focus();
1072 if (_session && _session->actively_recording()) {
1076 button_selection (item, event, item_type);
1078 if (!_drags->active () &&
1079 (Keyboard::is_delete_event (&event->button) ||
1080 Keyboard::is_context_menu_event (&event->button) ||
1081 Keyboard::is_edit_event (&event->button))) {
1083 /* handled by button release */
1087 //not rolling, range mode click + join_play_range : locate the PH here
1088 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_follow_edits() ) {
1089 framepos_t where = canvas_event_sample (event);
1091 _session->request_locate (where, false);
1094 switch (event->button.button) {
1096 return button_press_handler_1 (item, event, item_type);
1100 return button_press_handler_2 (item, event, item_type);
1107 return button_press_dispatch (&event->button);
1116 Editor::button_press_dispatch (GdkEventButton* ev)
1118 /* this function is intended only for buttons 4 and above.
1121 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1122 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1126 Editor::button_release_dispatch (GdkEventButton* ev)
1128 /* this function is intended only for buttons 4 and above.
1131 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1132 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1136 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1138 framepos_t where = canvas_event_sample (event);
1139 AutomationTimeAxisView* atv = 0;
1141 _press_cursor_ctx.reset();
1143 /* no action if we're recording */
1145 if (_session && _session->actively_recording()) {
1149 /* see if we're finishing a drag */
1151 bool were_dragging = false;
1152 if (_drags->active ()) {
1153 bool const r = _drags->end_grab (event);
1155 /* grab dragged, so do nothing else */
1159 were_dragging = true;
1162 update_region_layering_order_editor ();
1164 /* edit events get handled here */
1166 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1167 switch (item_type) {
1169 show_region_properties ();
1172 case TempoMarkerItem: {
1174 TempoMarker* tempo_marker;
1176 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1177 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1178 abort(); /*NOTREACHED*/
1181 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1182 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1183 abort(); /*NOTREACHED*/
1186 edit_tempo_marker (*tempo_marker);
1190 case MeterMarkerItem: {
1192 MeterMarker* meter_marker;
1194 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1195 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1196 abort(); /*NOTREACHED*/
1199 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1200 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1201 abort(); /*NOTREACHED*/
1203 edit_meter_marker (*meter_marker);
1207 case RegionViewName:
1208 if (clicked_regionview->name_active()) {
1209 return mouse_rename_region (item, event);
1213 case ControlPointItem:
1214 edit_control_point (item);
1223 /* context menu events get handled here */
1224 if (Keyboard::is_context_menu_event (&event->button)) {
1226 context_click_event = *event;
1228 if (!_drags->active ()) {
1230 /* no matter which button pops up the context menu, tell the menu
1231 widget to use button 1 to drive menu selection.
1234 switch (item_type) {
1236 case FadeInHandleItem:
1237 case FadeInTrimHandleItem:
1238 case StartCrossFadeItem:
1239 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1243 case FadeOutHandleItem:
1244 case FadeOutTrimHandleItem:
1245 case EndCrossFadeItem:
1246 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1249 case LeftFrameHandle:
1250 case RightFrameHandle:
1254 popup_track_context_menu (1, event->button.time, item_type, false);
1258 case RegionViewNameHighlight:
1259 case RegionViewName:
1260 popup_track_context_menu (1, event->button.time, item_type, false);
1264 popup_track_context_menu (1, event->button.time, item_type, true);
1267 case AutomationTrackItem:
1268 popup_track_context_menu (1, event->button.time, item_type, false);
1272 case RangeMarkerBarItem:
1273 case TransportMarkerBarItem:
1274 case CdMarkerBarItem:
1278 case TimecodeRulerItem:
1279 case SamplesRulerItem:
1280 case MinsecRulerItem:
1282 popup_ruler_menu (where, item_type);
1286 marker_context_menu (&event->button, item);
1289 case TempoMarkerItem:
1290 tempo_or_meter_marker_context_menu (&event->button, item);
1293 case MeterMarkerItem:
1294 tempo_or_meter_marker_context_menu (&event->button, item);
1297 case CrossfadeViewItem:
1298 popup_track_context_menu (1, event->button.time, item_type, false);
1301 case ControlPointItem:
1302 popup_control_point_context_menu (item, event);
1313 /* delete events get handled here */
1315 Editing::MouseMode const eff = effective_mouse_mode ();
1317 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1319 switch (item_type) {
1320 case TempoMarkerItem:
1321 remove_tempo_marker (item);
1324 case MeterMarkerItem:
1325 remove_meter_marker (item);
1329 remove_marker (*item, event);
1333 if (eff == MouseObject) {
1334 remove_clicked_region ();
1338 case ControlPointItem:
1339 remove_control_point (item);
1343 remove_midi_note (item, event);
1352 switch (event->button.button) {
1355 switch (item_type) {
1356 /* see comments in button_press_handler */
1357 case PlayheadCursorItem:
1360 case AutomationLineItem:
1361 case StartSelectionTrimItem:
1362 case EndSelectionTrimItem:
1366 if (!_dragging_playhead) {
1367 snap_to_with_modifier (where, event, RoundNearest, true);
1368 mouse_add_new_marker (where);
1372 case CdMarkerBarItem:
1373 if (!_dragging_playhead) {
1374 // if we get here then a dragged range wasn't done
1375 snap_to_with_modifier (where, event, RoundNearest, true);
1376 mouse_add_new_marker (where, true);
1381 if (!_dragging_playhead) {
1382 snap_to_with_modifier (where, event);
1383 mouse_add_new_tempo_event (where);
1388 if (!_dragging_playhead) {
1389 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1394 case TimecodeRulerItem:
1395 case SamplesRulerItem:
1396 case MinsecRulerItem:
1407 switch (item_type) {
1410 /* check that we didn't drag before releasing, since
1411 its really annoying to create new control
1412 points when doing this.
1414 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1415 if (!were_dragging && arv) {
1416 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1417 arv->add_gain_point_event (item, event, with_guard_points);
1423 case AutomationTrackItem: {
1424 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1425 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1427 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1438 if (scrubbing_direction == 0) {
1439 /* no drag, just a click */
1440 switch (item_type) {
1442 play_selected_region ();
1448 /* make sure we stop */
1449 _session->request_transport_speed (0.0);
1458 /* do any (de)selection operations that should occur on button release */
1459 button_selection (item, event, item_type);
1468 switch (item_type) {
1470 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1472 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1475 // Button2 click is unused
1490 // x_style_paste (where, 1.0);
1511 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1518 /* by the time we reach here, entered_regionview and entered trackview
1519 * will have already been set as appropriate. Things are done this
1520 * way because this method isn't passed a pointer to a variable type of
1521 * thing that is entered (which may or may not be canvas item).
1522 * (e.g. the actual entered regionview)
1525 choose_canvas_cursor_on_entry (item_type);
1527 switch (item_type) {
1528 case ControlPointItem:
1529 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1530 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1533 fraction = 1.0 - (cp->get_y() / cp->line().height());
1535 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1536 _verbose_cursor->show ();
1541 if (mouse_mode == MouseDraw) {
1542 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1544 line->set_outline_color (ARDOUR_UI::config()->color ("entered gain line"));
1549 case AutomationLineItem:
1550 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1551 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1553 line->set_outline_color (ARDOUR_UI::config()->color ("entered automation line"));
1558 case AutomationTrackItem:
1559 AutomationTimeAxisView* atv;
1560 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1561 clear_entered_track = false;
1562 set_entered_track (atv);
1567 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1570 entered_marker = marker;
1571 marker->set_color_rgba (ARDOUR_UI::config()->color ("entered marker"));
1573 case MeterMarkerItem:
1574 case TempoMarkerItem:
1577 case FadeInHandleItem:
1578 case FadeInTrimHandleItem:
1579 if (mouse_mode == MouseObject) {
1580 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1582 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1583 rect->set_fill_color (rv->get_fill_color());
1588 case FadeOutHandleItem:
1589 case FadeOutTrimHandleItem:
1590 if (mouse_mode == MouseObject) {
1591 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1593 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1594 rect->set_fill_color (rv->get_fill_color ());
1599 case FeatureLineItem:
1601 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1602 line->set_outline_color (0xFF0000FF);
1613 /* third pass to handle entered track status in a comprehensible way.
1616 switch (item_type) {
1618 case AutomationLineItem:
1619 case ControlPointItem:
1620 /* these do not affect the current entered track state */
1621 clear_entered_track = false;
1624 case AutomationTrackItem:
1625 /* handled above already */
1637 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1645 _enter_cursor_ctx.reset();
1646 _entered_item_type = NoItem;
1648 switch (item_type) {
1649 case ControlPointItem:
1650 _verbose_cursor->hide ();
1654 case AutomationLineItem:
1655 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1657 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1659 line->set_outline_color (al->get_line_color());
1665 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1669 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1670 location_flags_changed (loc);
1673 case MeterMarkerItem:
1674 case TempoMarkerItem:
1677 case FadeInTrimHandleItem:
1678 case FadeOutTrimHandleItem:
1679 case FadeInHandleItem:
1680 case FadeOutHandleItem:
1682 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1684 rect->set_fill_color (ARDOUR_UI::config()->color ("inactive fade handle"));
1689 case AutomationTrackItem:
1692 case FeatureLineItem:
1694 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1695 line->set_outline_color (ARDOUR_UI::config()->color ("zero line"));
1707 Editor::scrub (framepos_t frame, double current_x)
1711 if (scrubbing_direction == 0) {
1713 _session->request_locate (frame, false);
1714 _session->request_transport_speed (0.1);
1715 scrubbing_direction = 1;
1719 if (last_scrub_x > current_x) {
1721 /* pointer moved to the left */
1723 if (scrubbing_direction > 0) {
1725 /* we reversed direction to go backwards */
1728 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1732 /* still moving to the left (backwards) */
1734 scrub_reversals = 0;
1735 scrub_reverse_distance = 0;
1737 delta = 0.01 * (last_scrub_x - current_x);
1738 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1742 /* pointer moved to the right */
1744 if (scrubbing_direction < 0) {
1745 /* we reversed direction to go forward */
1748 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1751 /* still moving to the right */
1753 scrub_reversals = 0;
1754 scrub_reverse_distance = 0;
1756 delta = 0.01 * (current_x - last_scrub_x);
1757 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1761 /* if there have been more than 2 opposite motion moves detected, or one that moves
1762 back more than 10 pixels, reverse direction
1765 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1767 if (scrubbing_direction > 0) {
1768 /* was forwards, go backwards */
1769 _session->request_transport_speed (-0.1);
1770 scrubbing_direction = -1;
1772 /* was backwards, go forwards */
1773 _session->request_transport_speed (0.1);
1774 scrubbing_direction = 1;
1777 scrub_reverse_distance = 0;
1778 scrub_reversals = 0;
1782 last_scrub_x = current_x;
1786 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1788 _last_motion_y = event->motion.y;
1790 if (event->motion.is_hint) {
1793 /* We call this so that MOTION_NOTIFY events continue to be
1794 delivered to the canvas. We need to do this because we set
1795 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1796 the density of the events, at the expense of a round-trip
1797 to the server. Given that this will mostly occur on cases
1798 where DISPLAY = :0.0, and given the cost of what the motion
1799 event might do, its a good tradeoff.
1802 _track_canvas->get_pointer (x, y);
1805 if (current_stepping_trackview) {
1806 /* don't keep the persistent stepped trackview if the mouse moves */
1807 current_stepping_trackview = 0;
1808 step_timeout.disconnect ();
1811 if (_session && _session->actively_recording()) {
1812 /* Sorry. no dragging stuff around while we record */
1816 update_join_object_range_location (event->motion.y);
1818 if (_drags->active ()) {
1819 return _drags->motion_handler (event, from_autoscroll);
1826 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1828 ControlPoint* control_point;
1830 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1831 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1832 abort(); /*NOTREACHED*/
1835 AutomationLine& line = control_point->line ();
1836 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
1837 /* we shouldn't remove the first or last gain point in region gain lines */
1838 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
1847 Editor::remove_control_point (ArdourCanvas::Item* item)
1849 if (!can_remove_control_point (item)) {
1853 ControlPoint* control_point;
1855 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1856 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1857 abort(); /*NOTREACHED*/
1860 control_point->line().remove_point (*control_point);
1864 Editor::edit_control_point (ArdourCanvas::Item* item)
1866 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1869 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1870 abort(); /*NOTREACHED*/
1873 ControlPointDialog d (p);
1876 if (d.run () != RESPONSE_ACCEPT) {
1880 p->line().modify_point_y (*p, d.get_y_fraction ());
1884 Editor::edit_notes (TimeAxisViewItem& tavi)
1886 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
1892 MidiRegionView::Selection const & s = mrv->selection();
1898 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
1902 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
1906 Editor::note_edit_done (int r, EditNoteDialog* d)
1913 Editor::visible_order_range (int* low, int* high) const
1915 *low = TimeAxisView::max_order ();
1918 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1920 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1922 if (!rtv->hidden()) {
1924 if (*high < rtv->order()) {
1925 *high = rtv->order ();
1928 if (*low > rtv->order()) {
1929 *low = rtv->order ();
1936 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1938 /* Either add to or set the set the region selection, unless
1939 this is an alignment click (control used)
1942 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1943 TimeAxisView* tv = &rv.get_time_axis_view();
1944 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1946 if (rtv && rtv->is_track()) {
1947 speed = rtv->track()->speed();
1950 framepos_t where = get_preferred_edit_position();
1954 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1956 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
1958 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1960 align_region (rv.region(), End, (framepos_t) (where * speed));
1964 align_region (rv.region(), Start, (framepos_t) (where * speed));
1971 Editor::collect_new_region_view (RegionView* rv)
1973 latest_regionviews.push_back (rv);
1977 Editor::collect_and_select_new_region_view (RegionView* rv)
1980 latest_regionviews.push_back (rv);
1984 Editor::cancel_selection ()
1986 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1987 (*i)->hide_selection ();
1990 selection->clear ();
1991 clicked_selection = 0;
1995 Editor::cancel_time_selection ()
1997 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1998 (*i)->hide_selection ();
2000 selection->time.clear ();
2001 clicked_selection = 0;
2005 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2007 RegionView* rv = clicked_regionview;
2009 /* Choose action dependant on which button was pressed */
2010 switch (event->button.button) {
2012 begin_reversible_command (_("start point trim"));
2014 if (selection->selected (rv)) {
2015 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2016 i != selection->regions.by_layer().end(); ++i)
2018 if (!(*i)->region()->locked()) {
2019 (*i)->region()->clear_changes ();
2020 (*i)->region()->trim_front (new_bound);
2021 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2026 if (!rv->region()->locked()) {
2027 rv->region()->clear_changes ();
2028 rv->region()->trim_front (new_bound);
2029 _session->add_command(new StatefulDiffCommand (rv->region()));
2033 commit_reversible_command();
2037 begin_reversible_command (_("End point trim"));
2039 if (selection->selected (rv)) {
2041 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2043 if (!(*i)->region()->locked()) {
2044 (*i)->region()->clear_changes();
2045 (*i)->region()->trim_end (new_bound);
2046 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2052 if (!rv->region()->locked()) {
2053 rv->region()->clear_changes ();
2054 rv->region()->trim_end (new_bound);
2055 _session->add_command (new StatefulDiffCommand (rv->region()));
2059 commit_reversible_command();
2068 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2073 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2074 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2075 abort(); /*NOTREACHED*/
2078 Location* location = find_location_from_marker (marker, is_start);
2079 location->set_hidden (true, this);
2083 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2085 using namespace Gtkmm2ext;
2087 ArdourPrompter prompter (false);
2089 prompter.set_prompt (_("Name for region:"));
2090 prompter.set_initial_text (clicked_regionview->region()->name());
2091 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2092 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2093 prompter.show_all ();
2094 switch (prompter.run ()) {
2095 case Gtk::RESPONSE_ACCEPT:
2097 prompter.get_result(str);
2099 clicked_regionview->region()->set_name (str);
2108 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2110 /* no brushing without a useful snap setting */
2112 switch (_snap_mode) {
2114 return; /* can't work because it allows region to be placed anywhere */
2119 switch (_snap_type) {
2127 /* don't brush a copy over the original */
2129 if (pos == rv->region()->position()) {
2133 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2135 if (rtv == 0 || !rtv->is_track()) {
2139 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2140 double speed = rtv->track()->speed();
2142 playlist->clear_changes ();
2143 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2144 playlist->add_region (new_region, (framepos_t) (pos * speed));
2145 _session->add_command (new StatefulDiffCommand (playlist));
2147 // playlist is frozen, so we have to update manually XXX this is disgusting
2149 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2153 Editor::track_height_step_timeout ()
2155 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2156 current_stepping_trackview = 0;
2163 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2165 assert (region_view);
2167 if (!region_view->region()->playlist()) {
2171 switch (Config->get_edit_mode()) {
2173 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2176 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2179 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2186 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2188 assert (region_view);
2190 if (!region_view->region()->playlist()) {
2194 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2198 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2200 assert (region_view);
2202 if (!region_view->region()->playlist()) {
2206 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2210 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2212 begin_reversible_command (Operations::drag_region_brush);
2215 /** Start a grab where a time range is selected, track(s) are selected, and the
2216 * user clicks and drags a region with a modifier in order to create a new region containing
2217 * the section of the clicked region that lies within the time range.
2220 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2222 if (clicked_regionview == 0) {
2226 /* lets try to create new Region for the selection */
2228 vector<boost::shared_ptr<Region> > new_regions;
2229 create_region_from_selection (new_regions);
2231 if (new_regions.empty()) {
2235 /* XXX fix me one day to use all new regions */
2237 boost::shared_ptr<Region> region (new_regions.front());
2239 /* add it to the current stream/playlist.
2241 tricky: the streamview for the track will add a new regionview. we will
2242 catch the signal it sends when it creates the regionview to
2243 set the regionview we want to then drag.
2246 latest_regionviews.clear();
2247 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2249 /* A selection grab currently creates two undo/redo operations, one for
2250 creating the new region and another for moving it.
2253 begin_reversible_command (Operations::selection_grab);
2255 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2257 playlist->clear_changes ();
2258 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2259 _session->add_command(new StatefulDiffCommand (playlist));
2263 if (latest_regionviews.empty()) {
2264 /* something went wrong */
2268 /* we need to deselect all other regionviews, and select this one
2269 i'm ignoring undo stuff, because the region creation will take care of it
2272 selection->set (latest_regionviews);
2274 commit_reversible_command ();
2276 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2282 if (_drags->active ()) {
2285 selection->clear ();
2291 /** Update _join_object_range_state which indicate whether we are over the top
2292 * or bottom half of a route view, used by the `join object/range' tool
2293 * mode. Coordinates in canvas space.
2296 Editor::update_join_object_range_location (double y)
2298 if (!get_smart_mode()) {
2299 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2303 JoinObjectRangeState const old = _join_object_range_state;
2305 if (mouse_mode == MouseObject) {
2306 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2307 } else if (mouse_mode == MouseRange) {
2308 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2311 if (entered_regionview) {
2313 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2314 double const c = item_space.y / entered_regionview->height();
2316 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2318 if (_join_object_range_state != old && _enter_cursor_ctx) {
2319 _enter_cursor_ctx->change(which_track_cursor());
2322 } else if (entered_track) {
2324 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2326 if (entered_route_view) {
2331 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2333 double track_height = entered_route_view->view()->child_height();
2334 if (Config->get_show_name_highlight()) {
2335 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2337 double const c = cy / track_height;
2341 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2343 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2347 /* Other kinds of tracks use object mode */
2348 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2351 if (_join_object_range_state != old && _enter_cursor_ctx) {
2352 _enter_cursor_ctx->change(which_track_cursor());
2358 Editor::effective_mouse_mode () const
2360 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2362 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2370 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2372 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2375 e->region_view().delete_note (e->note ());
2378 /** Obtain the pointer position in canvas coordinates */
2380 Editor::get_pointer_position (double& x, double& y) const
2383 _track_canvas->get_pointer (px, py);
2384 _track_canvas->window_to_canvas (px, py, x, y);