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);
452 _mouse_changed_selection = false;
458 if (eff_mouse_mode != MouseRange) {
459 _mouse_changed_selection = set_selected_regionview_from_click (press, op);
461 /* don't change the selection unless the
462 clicked track is not currently selected. if
463 so, "collapse" the selection to just this
466 if (!selection->selected (clicked_axisview)) {
467 set_selected_track_as_side_effect (Selection::Set);
471 if (eff_mouse_mode != MouseRange) {
472 _mouse_changed_selection = set_selected_regionview_from_click (press, op);
477 case RegionViewNameHighlight:
479 case LeftFrameHandle:
480 case RightFrameHandle:
481 case FadeInHandleItem:
482 case FadeInTrimHandleItem:
484 case FadeOutHandleItem:
485 case FadeOutTrimHandleItem:
487 case StartCrossFadeItem:
488 case EndCrossFadeItem:
489 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
490 _mouse_changed_selection = set_selected_regionview_from_click (press, op);
491 } else if (event->type == GDK_BUTTON_PRESS) {
492 set_selected_track_as_side_effect (op);
496 case ControlPointItem:
497 set_selected_track_as_side_effect (op);
498 if (eff_mouse_mode != MouseRange) {
499 _mouse_changed_selection = set_selected_control_point_from_click (press, op);
504 /* for context click, select track */
505 if (event->button.button == 3) {
506 selection->clear_tracks ();
507 set_selected_track_as_side_effect (op);
508 _mouse_changed_selection = true;
512 case AutomationTrackItem:
513 set_selected_track_as_side_effect (op);
520 if ((!press) && _mouse_changed_selection) {
521 begin_reversible_selection_op (_("Button Selection"));
522 commit_reversible_selection_op ();
523 _mouse_changed_selection = false;
528 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
530 /* single mouse clicks on any of these item types operate
531 independent of mouse mode, mostly because they are
532 not on the main track canvas or because we want
536 NoteBase* note = NULL;
539 case PlayheadCursorItem:
540 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
544 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
545 hide_marker (item, event);
547 _drags->set (new MarkerDrag (this, item), event);
551 case TempoMarkerItem:
554 new TempoMarkerDrag (
557 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
564 case MeterMarkerItem:
567 new MeterMarkerDrag (
570 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
578 _drags->set (new VideoTimeLineDrag (this, item), event);
585 case TimecodeRulerItem:
586 case SamplesRulerItem:
587 case MinsecRulerItem:
589 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
590 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
596 case RangeMarkerBarItem:
597 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
598 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
599 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
600 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
602 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
607 case CdMarkerBarItem:
608 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
609 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
611 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
616 case TransportMarkerBarItem:
617 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
618 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
620 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
629 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
630 /* special case: allow trim of range selections in joined object mode;
631 in theory eff should equal MouseRange in this case, but it doesn't
632 because entering the range selection canvas item results in entered_regionview
633 being set to 0, so update_join_object_range_location acts as if we aren't
636 if (item_type == StartSelectionTrimItem) {
637 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
638 } else if (item_type == EndSelectionTrimItem) {
639 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
643 Editing::MouseMode eff = effective_mouse_mode ();
645 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
646 if (get_smart_mode()) {
648 case FadeInHandleItem:
649 case FadeInTrimHandleItem:
650 case FadeOutHandleItem:
651 case FadeOutTrimHandleItem:
662 case StartSelectionTrimItem:
663 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
666 case EndSelectionTrimItem:
667 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
671 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
672 start_selection_grab (item, event);
674 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
675 /* grab selection for moving */
676 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
678 /* this was debated, but decided the more common action was to
679 make a new selection */
680 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
685 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
686 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
688 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
693 case RegionViewNameHighlight:
694 if (!clicked_regionview->region()->locked()) {
695 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
701 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
702 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
704 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
713 case FadeInHandleItem:
714 case FadeOutHandleItem:
715 case LeftFrameHandle:
716 case RightFrameHandle:
717 case FeatureLineItem:
718 case RegionViewNameHighlight:
721 case AutomationTrackItem:
722 _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, get_canvas_cursor());
733 /* Existing note: allow trimming/motion */
734 if ((note = reinterpret_cast<NoteBase*> (item->get_data ("notebase")))) {
735 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
736 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
738 _drags->set (new NoteDrag (this, item), event);
743 case ControlPointItem:
744 _drags->set (new ControlPointDrag (this, item), event);
749 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
750 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
755 case AutomationTrackItem:
756 /* rubberband drag to select automation points */
757 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
767 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
768 event->type == GDK_BUTTON_PRESS) {
770 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
772 } else if (event->type == GDK_BUTTON_PRESS) {
775 case FadeInHandleItem:
777 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
781 case FadeOutHandleItem:
783 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
787 case StartCrossFadeItem:
788 case EndCrossFadeItem:
789 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
790 // if (!clicked_regionview->region()->locked()) {
791 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
796 case FeatureLineItem:
798 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
799 remove_transient(item);
803 _drags->set (new FeatureLineDrag (this, item), event);
809 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
810 /* click on an automation region view; do nothing here and let the ARV's signal handler
816 /* click on a normal region view */
817 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
818 add_region_copy_drag (item, event, clicked_regionview);
819 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
820 add_region_brush_drag (item, event, clicked_regionview);
822 add_region_drag (item, event, clicked_regionview);
826 _drags->start_grab (event);
830 case RegionViewNameHighlight:
831 case LeftFrameHandle:
832 case RightFrameHandle:
833 if (!clicked_regionview->region()->locked()) {
834 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
839 case FadeInTrimHandleItem:
840 case FadeOutTrimHandleItem:
841 if (!clicked_regionview->region()->locked()) {
842 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
849 /* rename happens on edit clicks */
850 if (clicked_regionview->get_name_highlight()) {
851 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
857 case ControlPointItem:
858 _drags->set (new ControlPointDrag (this, item), event);
862 case AutomationLineItem:
863 _drags->set (new LineDrag (this, item), event);
868 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
871 case AutomationTrackItem:
873 TimeAxisView* parent = clicked_axisview->get_parent ();
874 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
876 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
878 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
880 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
881 if (pl->n_regions() == 0) {
882 /* Parent has no regions; create one so that we have somewhere to put automation */
883 _drags->set (new RegionCreateDrag (this, item, parent), event);
885 /* See if there's a region before the click that we can extend, and extend it if so */
886 framepos_t const t = canvas_event_sample (event);
887 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
889 _drags->set (new RegionCreateDrag (this, item, parent), event);
891 prev->set_length (t - prev->position ());
895 /* rubberband drag to select automation points */
896 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
920 _drags->set (new LineDrag (this, item), event);
923 case ControlPointItem:
924 _drags->set (new ControlPointDrag (this, item), event);
930 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
932 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
934 double const y = event->button.y;
935 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
937 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
939 /* smart "join" mode: drag automation */
940 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
948 case AutomationLineItem:
949 _drags->set (new LineDrag (this, item), event);
953 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
954 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
955 /* Note is big and pointer is near the end, trim */
956 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
959 _drags->set (new NoteDrag (this, item), event);
966 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
967 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
978 if (item_type == NoteItem) {
979 /* resize-drag notes */
980 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
981 if (note->big_enough_to_trim()) {
982 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
986 } else if (clicked_regionview) {
988 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
994 _drags->set (new ScrubDrag (this, item), event, _cursors->transparent);
996 scrub_reverse_distance = 0;
997 last_scrub_x = event->button.x;
998 scrubbing_direction = 0;
1010 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1012 Editing::MouseMode const eff = effective_mouse_mode ();
1015 switch (item_type) {
1017 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1018 add_region_copy_drag (item, event, clicked_regionview);
1020 add_region_drag (item, event, clicked_regionview);
1022 _drags->start_grab (event);
1025 case ControlPointItem:
1026 _drags->set (new ControlPointDrag (this, item), event);
1034 switch (item_type) {
1035 case RegionViewNameHighlight:
1036 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1040 case LeftFrameHandle:
1041 case RightFrameHandle:
1042 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1046 case RegionViewName:
1047 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1061 /* relax till release */
1073 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1075 if (event->type == GDK_2BUTTON_PRESS) {
1076 _drags->mark_double_click ();
1077 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1081 if (event->type != GDK_BUTTON_PRESS) {
1085 _track_canvas->grab_focus();
1087 if (_session && _session->actively_recording()) {
1091 button_selection (item, event, item_type);
1093 if (!_drags->active () &&
1094 (Keyboard::is_delete_event (&event->button) ||
1095 Keyboard::is_context_menu_event (&event->button) ||
1096 Keyboard::is_edit_event (&event->button))) {
1098 /* handled by button release */
1102 //not rolling, range mode click + join_play_range : locate the PH here
1103 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && ARDOUR_UI::config()->get_follow_edits() ) {
1104 framepos_t where = canvas_event_sample (event);
1106 _session->request_locate (where, false);
1109 switch (event->button.button) {
1111 return button_press_handler_1 (item, event, item_type);
1115 return button_press_handler_2 (item, event, item_type);
1122 return button_press_dispatch (&event->button);
1131 Editor::button_press_dispatch (GdkEventButton* ev)
1133 /* this function is intended only for buttons 4 and above.
1136 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1137 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1141 Editor::button_release_dispatch (GdkEventButton* ev)
1143 /* this function is intended only for buttons 4 and above.
1146 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1147 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1151 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1153 framepos_t where = canvas_event_sample (event);
1154 AutomationTimeAxisView* atv = 0;
1156 _press_cursor_ctx.reset();
1158 /* no action if we're recording */
1160 if (_session && _session->actively_recording()) {
1164 /* see if we're finishing a drag */
1166 bool were_dragging = false;
1167 if (_drags->active ()) {
1168 bool const r = _drags->end_grab (event);
1170 /* grab dragged, so do nothing else */
1174 were_dragging = true;
1177 update_region_layering_order_editor ();
1179 /* edit events get handled here */
1181 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1182 switch (item_type) {
1184 show_region_properties ();
1187 case TempoMarkerItem: {
1189 TempoMarker* tempo_marker;
1191 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1192 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1193 abort(); /*NOTREACHED*/
1196 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1197 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1198 abort(); /*NOTREACHED*/
1201 edit_tempo_marker (*tempo_marker);
1205 case MeterMarkerItem: {
1207 MeterMarker* meter_marker;
1209 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1210 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1211 abort(); /*NOTREACHED*/
1214 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1215 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1216 abort(); /*NOTREACHED*/
1218 edit_meter_marker (*meter_marker);
1222 case RegionViewName:
1223 if (clicked_regionview->name_active()) {
1224 return mouse_rename_region (item, event);
1228 case ControlPointItem:
1229 edit_control_point (item);
1238 /* context menu events get handled here */
1239 if (Keyboard::is_context_menu_event (&event->button)) {
1241 context_click_event = *event;
1243 if (!_drags->active ()) {
1245 /* no matter which button pops up the context menu, tell the menu
1246 widget to use button 1 to drive menu selection.
1249 switch (item_type) {
1251 case FadeInHandleItem:
1252 case FadeInTrimHandleItem:
1253 case StartCrossFadeItem:
1254 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1258 case FadeOutHandleItem:
1259 case FadeOutTrimHandleItem:
1260 case EndCrossFadeItem:
1261 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1264 case LeftFrameHandle:
1265 case RightFrameHandle:
1269 popup_track_context_menu (1, event->button.time, item_type, false);
1273 case RegionViewNameHighlight:
1274 case RegionViewName:
1275 popup_track_context_menu (1, event->button.time, item_type, false);
1279 popup_track_context_menu (1, event->button.time, item_type, true);
1282 case AutomationTrackItem:
1283 popup_track_context_menu (1, event->button.time, item_type, false);
1287 case RangeMarkerBarItem:
1288 case TransportMarkerBarItem:
1289 case CdMarkerBarItem:
1293 case TimecodeRulerItem:
1294 case SamplesRulerItem:
1295 case MinsecRulerItem:
1297 popup_ruler_menu (where, item_type);
1301 marker_context_menu (&event->button, item);
1304 case TempoMarkerItem:
1305 tempo_or_meter_marker_context_menu (&event->button, item);
1308 case MeterMarkerItem:
1309 tempo_or_meter_marker_context_menu (&event->button, item);
1312 case CrossfadeViewItem:
1313 popup_track_context_menu (1, event->button.time, item_type, false);
1316 case ControlPointItem:
1317 popup_control_point_context_menu (item, event);
1321 if (internal_editing()) {
1322 popup_note_context_menu (item, event);
1334 /* delete events get handled here */
1336 Editing::MouseMode const eff = effective_mouse_mode ();
1338 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1340 switch (item_type) {
1341 case TempoMarkerItem:
1342 remove_tempo_marker (item);
1345 case MeterMarkerItem:
1346 remove_meter_marker (item);
1350 remove_marker (*item, event);
1354 if (eff == MouseObject) {
1355 remove_clicked_region ();
1359 case ControlPointItem:
1360 remove_control_point (item);
1364 remove_midi_note (item, event);
1373 switch (event->button.button) {
1376 switch (item_type) {
1377 /* see comments in button_press_handler */
1378 case PlayheadCursorItem:
1381 case AutomationLineItem:
1382 case StartSelectionTrimItem:
1383 case EndSelectionTrimItem:
1387 if (!_dragging_playhead) {
1388 snap_to_with_modifier (where, event, RoundNearest, true);
1389 mouse_add_new_marker (where);
1393 case CdMarkerBarItem:
1394 if (!_dragging_playhead) {
1395 // if we get here then a dragged range wasn't done
1396 snap_to_with_modifier (where, event, RoundNearest, true);
1397 mouse_add_new_marker (where, true);
1402 if (!_dragging_playhead) {
1403 snap_to_with_modifier (where, event);
1404 mouse_add_new_tempo_event (where);
1409 if (!_dragging_playhead) {
1410 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1415 case TimecodeRulerItem:
1416 case SamplesRulerItem:
1417 case MinsecRulerItem:
1428 switch (item_type) {
1431 /* check that we didn't drag before releasing, since
1432 its really annoying to create new control
1433 points when doing this.
1435 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1436 if (!were_dragging && arv) {
1437 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1438 arv->add_gain_point_event (item, event, with_guard_points);
1444 case AutomationTrackItem: {
1445 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1446 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1448 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1459 if (scrubbing_direction == 0) {
1460 /* no drag, just a click */
1461 switch (item_type) {
1463 play_selected_region ();
1469 /* make sure we stop */
1470 _session->request_transport_speed (0.0);
1479 /* do any (de)selection operations that should occur on button release */
1480 button_selection (item, event, item_type);
1490 switch (item_type) {
1492 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1494 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1497 // Button2 click is unused
1512 // x_style_paste (where, 1.0);
1533 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1540 /* by the time we reach here, entered_regionview and entered trackview
1541 * will have already been set as appropriate. Things are done this
1542 * way because this method isn't passed a pointer to a variable type of
1543 * thing that is entered (which may or may not be canvas item).
1544 * (e.g. the actual entered regionview)
1547 choose_canvas_cursor_on_entry (item_type);
1549 switch (item_type) {
1550 case ControlPointItem:
1551 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1552 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1555 fraction = 1.0 - (cp->get_y() / cp->line().height());
1557 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1558 _verbose_cursor->show ();
1563 if (mouse_mode == MouseDraw) {
1564 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1566 line->set_outline_color (ARDOUR_UI::config()->color ("entered gain line"));
1571 case AutomationLineItem:
1572 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1573 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1575 line->set_outline_color (ARDOUR_UI::config()->color ("entered automation line"));
1580 case AutomationTrackItem:
1581 AutomationTimeAxisView* atv;
1582 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1583 clear_entered_track = false;
1584 set_entered_track (atv);
1589 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1592 entered_marker = marker;
1593 marker->set_color_rgba (ARDOUR_UI::config()->color ("entered marker"));
1595 case MeterMarkerItem:
1596 case TempoMarkerItem:
1599 case FadeInHandleItem:
1600 case FadeInTrimHandleItem:
1601 if (mouse_mode == MouseObject) {
1602 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1604 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1605 rect->set_fill_color (rv->get_fill_color());
1610 case FadeOutHandleItem:
1611 case FadeOutTrimHandleItem:
1612 if (mouse_mode == MouseObject) {
1613 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1615 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1616 rect->set_fill_color (rv->get_fill_color ());
1621 case FeatureLineItem:
1623 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1624 line->set_outline_color (0xFF0000FF);
1635 /* third pass to handle entered track status in a comprehensible way.
1638 switch (item_type) {
1640 case AutomationLineItem:
1641 case ControlPointItem:
1642 /* these do not affect the current entered track state */
1643 clear_entered_track = false;
1646 case AutomationTrackItem:
1647 /* handled above already */
1659 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1667 if (!_enter_stack.empty()) {
1668 _enter_stack.pop_back();
1671 switch (item_type) {
1672 case ControlPointItem:
1673 _verbose_cursor->hide ();
1677 case AutomationLineItem:
1678 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1680 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1682 line->set_outline_color (al->get_line_color());
1688 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1692 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1693 location_flags_changed (loc);
1696 case MeterMarkerItem:
1697 case TempoMarkerItem:
1700 case FadeInTrimHandleItem:
1701 case FadeOutTrimHandleItem:
1702 case FadeInHandleItem:
1703 case FadeOutHandleItem:
1705 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1707 rect->set_fill_color (ARDOUR_UI::config()->color ("inactive fade handle"));
1712 case AutomationTrackItem:
1715 case FeatureLineItem:
1717 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1718 line->set_outline_color (ARDOUR_UI::config()->color ("zero line"));
1730 Editor::scrub (framepos_t frame, double current_x)
1734 if (scrubbing_direction == 0) {
1736 _session->request_locate (frame, false);
1737 _session->request_transport_speed (0.1);
1738 scrubbing_direction = 1;
1742 if (last_scrub_x > current_x) {
1744 /* pointer moved to the left */
1746 if (scrubbing_direction > 0) {
1748 /* we reversed direction to go backwards */
1751 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1755 /* still moving to the left (backwards) */
1757 scrub_reversals = 0;
1758 scrub_reverse_distance = 0;
1760 delta = 0.01 * (last_scrub_x - current_x);
1761 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1765 /* pointer moved to the right */
1767 if (scrubbing_direction < 0) {
1768 /* we reversed direction to go forward */
1771 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1774 /* still moving to the right */
1776 scrub_reversals = 0;
1777 scrub_reverse_distance = 0;
1779 delta = 0.01 * (current_x - last_scrub_x);
1780 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1784 /* if there have been more than 2 opposite motion moves detected, or one that moves
1785 back more than 10 pixels, reverse direction
1788 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1790 if (scrubbing_direction > 0) {
1791 /* was forwards, go backwards */
1792 _session->request_transport_speed (-0.1);
1793 scrubbing_direction = -1;
1795 /* was backwards, go forwards */
1796 _session->request_transport_speed (0.1);
1797 scrubbing_direction = 1;
1800 scrub_reverse_distance = 0;
1801 scrub_reversals = 0;
1805 last_scrub_x = current_x;
1809 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1811 _last_motion_y = event->motion.y;
1813 if (event->motion.is_hint) {
1816 /* We call this so that MOTION_NOTIFY events continue to be
1817 delivered to the canvas. We need to do this because we set
1818 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1819 the density of the events, at the expense of a round-trip
1820 to the server. Given that this will mostly occur on cases
1821 where DISPLAY = :0.0, and given the cost of what the motion
1822 event might do, its a good tradeoff.
1825 _track_canvas->get_pointer (x, y);
1828 if (current_stepping_trackview) {
1829 /* don't keep the persistent stepped trackview if the mouse moves */
1830 current_stepping_trackview = 0;
1831 step_timeout.disconnect ();
1834 if (_session && _session->actively_recording()) {
1835 /* Sorry. no dragging stuff around while we record */
1839 update_join_object_range_location (event->motion.y);
1841 if (_drags->active ()) {
1842 return _drags->motion_handler (event, from_autoscroll);
1849 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1851 ControlPoint* control_point;
1853 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1854 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1855 abort(); /*NOTREACHED*/
1858 AutomationLine& line = control_point->line ();
1859 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
1860 /* we shouldn't remove the first or last gain point in region gain lines */
1861 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
1870 Editor::remove_control_point (ArdourCanvas::Item* item)
1872 if (!can_remove_control_point (item)) {
1876 ControlPoint* control_point;
1878 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1879 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1880 abort(); /*NOTREACHED*/
1883 control_point->line().remove_point (*control_point);
1887 Editor::edit_control_point (ArdourCanvas::Item* item)
1889 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1892 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1893 abort(); /*NOTREACHED*/
1896 ControlPointDialog d (p);
1899 if (d.run () != RESPONSE_ACCEPT) {
1903 p->line().modify_point_y (*p, d.get_y_fraction ());
1907 Editor::edit_notes (MidiRegionView* mrv)
1909 MidiRegionView::Selection const & s = mrv->selection();
1915 EditNoteDialog* d = new EditNoteDialog (mrv, s);
1919 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
1923 Editor::note_edit_done (int r, EditNoteDialog* d)
1930 Editor::visible_order_range (int* low, int* high) const
1932 *low = TimeAxisView::max_order ();
1935 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1937 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1939 if (!rtv->hidden()) {
1941 if (*high < rtv->order()) {
1942 *high = rtv->order ();
1945 if (*low > rtv->order()) {
1946 *low = rtv->order ();
1953 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1955 /* Either add to or set the set the region selection, unless
1956 this is an alignment click (control used)
1959 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1960 TimeAxisView* tv = &rv.get_time_axis_view();
1961 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1963 if (rtv && rtv->is_track()) {
1964 speed = rtv->track()->speed();
1967 framepos_t where = get_preferred_edit_position();
1971 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1973 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
1975 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1977 align_region (rv.region(), End, (framepos_t) (where * speed));
1981 align_region (rv.region(), Start, (framepos_t) (where * speed));
1988 Editor::collect_new_region_view (RegionView* rv)
1990 latest_regionviews.push_back (rv);
1994 Editor::collect_and_select_new_region_view (RegionView* rv)
1997 latest_regionviews.push_back (rv);
2001 Editor::cancel_selection ()
2003 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2004 (*i)->hide_selection ();
2007 selection->clear ();
2008 clicked_selection = 0;
2012 Editor::cancel_time_selection ()
2014 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2015 (*i)->hide_selection ();
2017 selection->time.clear ();
2018 clicked_selection = 0;
2022 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2024 RegionView* rv = clicked_regionview;
2026 /* Choose action dependant on which button was pressed */
2027 switch (event->button.button) {
2029 begin_reversible_command (_("start point trim"));
2031 if (selection->selected (rv)) {
2032 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2033 i != selection->regions.by_layer().end(); ++i)
2035 if (!(*i)->region()->locked()) {
2036 (*i)->region()->clear_changes ();
2037 (*i)->region()->trim_front (new_bound);
2038 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2043 if (!rv->region()->locked()) {
2044 rv->region()->clear_changes ();
2045 rv->region()->trim_front (new_bound);
2046 _session->add_command(new StatefulDiffCommand (rv->region()));
2050 commit_reversible_command();
2054 begin_reversible_command (_("End point trim"));
2056 if (selection->selected (rv)) {
2058 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2060 if (!(*i)->region()->locked()) {
2061 (*i)->region()->clear_changes();
2062 (*i)->region()->trim_end (new_bound);
2063 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2069 if (!rv->region()->locked()) {
2070 rv->region()->clear_changes ();
2071 rv->region()->trim_end (new_bound);
2072 _session->add_command (new StatefulDiffCommand (rv->region()));
2076 commit_reversible_command();
2085 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2090 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2091 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2092 abort(); /*NOTREACHED*/
2095 Location* location = find_location_from_marker (marker, is_start);
2096 location->set_hidden (true, this);
2100 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2102 using namespace Gtkmm2ext;
2104 ArdourPrompter prompter (false);
2106 prompter.set_prompt (_("Name for region:"));
2107 prompter.set_initial_text (clicked_regionview->region()->name());
2108 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2109 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2110 prompter.show_all ();
2111 switch (prompter.run ()) {
2112 case Gtk::RESPONSE_ACCEPT:
2114 prompter.get_result(str);
2116 clicked_regionview->region()->set_name (str);
2125 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2127 /* no brushing without a useful snap setting */
2129 switch (_snap_mode) {
2131 return; /* can't work because it allows region to be placed anywhere */
2136 switch (_snap_type) {
2144 /* don't brush a copy over the original */
2146 if (pos == rv->region()->position()) {
2150 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2152 if (rtv == 0 || !rtv->is_track()) {
2156 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2157 double speed = rtv->track()->speed();
2159 playlist->clear_changes ();
2160 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2161 playlist->add_region (new_region, (framepos_t) (pos * speed));
2162 _session->add_command (new StatefulDiffCommand (playlist));
2164 // playlist is frozen, so we have to update manually XXX this is disgusting
2166 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2170 Editor::track_height_step_timeout ()
2172 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2173 current_stepping_trackview = 0;
2180 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2182 assert (region_view);
2184 if (!region_view->region()->playlist()) {
2188 switch (Config->get_edit_mode()) {
2190 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2193 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2196 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2203 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2205 assert (region_view);
2207 if (!region_view->region()->playlist()) {
2211 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2215 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2217 assert (region_view);
2219 if (!region_view->region()->playlist()) {
2223 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2227 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2229 begin_reversible_command (Operations::drag_region_brush);
2232 /** Start a grab where a time range is selected, track(s) are selected, and the
2233 * user clicks and drags a region with a modifier in order to create a new region containing
2234 * the section of the clicked region that lies within the time range.
2237 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2239 if (clicked_regionview == 0) {
2243 /* lets try to create new Region for the selection */
2245 vector<boost::shared_ptr<Region> > new_regions;
2246 create_region_from_selection (new_regions);
2248 if (new_regions.empty()) {
2252 /* XXX fix me one day to use all new regions */
2254 boost::shared_ptr<Region> region (new_regions.front());
2256 /* add it to the current stream/playlist.
2258 tricky: the streamview for the track will add a new regionview. we will
2259 catch the signal it sends when it creates the regionview to
2260 set the regionview we want to then drag.
2263 latest_regionviews.clear();
2264 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2266 /* A selection grab currently creates two undo/redo operations, one for
2267 creating the new region and another for moving it.
2270 begin_reversible_command (Operations::selection_grab);
2272 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2274 playlist->clear_changes ();
2275 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2276 _session->add_command(new StatefulDiffCommand (playlist));
2280 if (latest_regionviews.empty()) {
2281 /* something went wrong */
2285 /* we need to deselect all other regionviews, and select this one
2286 i'm ignoring undo stuff, because the region creation will take care of it
2289 selection->set (latest_regionviews);
2291 commit_reversible_command ();
2293 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2299 if (_drags->active ()) {
2302 selection->clear ();
2308 /** Update _join_object_range_state which indicate whether we are over the top
2309 * or bottom half of a route view, used by the `join object/range' tool
2310 * mode. Coordinates in canvas space.
2313 Editor::update_join_object_range_location (double y)
2315 if (!get_smart_mode()) {
2316 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2320 JoinObjectRangeState const old = _join_object_range_state;
2322 if (mouse_mode == MouseObject) {
2323 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2324 } else if (mouse_mode == MouseRange) {
2325 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2328 if (entered_regionview) {
2330 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2331 double const c = item_space.y / entered_regionview->height();
2333 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2335 Editor::EnterContext* ctx = get_enter_context(RegionItem);
2336 if (_join_object_range_state != old && ctx) {
2337 ctx->cursor_ctx->change(which_track_cursor());
2340 } else if (entered_track) {
2342 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2344 if (entered_route_view) {
2349 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2351 double track_height = entered_route_view->view()->child_height();
2352 if (ARDOUR_UI::config()->get_show_name_highlight()) {
2353 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2355 double const c = cy / track_height;
2359 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2361 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2365 /* Other kinds of tracks use object mode */
2366 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2369 Editor::EnterContext* ctx = get_enter_context(StreamItem);
2370 if (_join_object_range_state != old && ctx) {
2371 ctx->cursor_ctx->change(which_track_cursor());
2377 Editor::effective_mouse_mode () const
2379 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2381 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2389 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2391 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2394 e->region_view().delete_note (e->note ());
2397 /** Obtain the pointer position in canvas coordinates */
2399 Editor::get_pointer_position (double& x, double& y) const
2402 _track_canvas->get_pointer (px, py);
2403 _track_canvas->window_to_canvas (px, py, x, y);