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 if (mouse_mode == m) {
296 /* switch to the same mode, act like a toggle and switch back to previous mode */
297 Glib::RefPtr<Action> pact = get_mouse_mode_action(previous_mouse_mode);
298 Glib::RefPtr<ToggleAction> ptact = Glib::RefPtr<ToggleAction>::cast_dynamic(pact);
299 ptact->set_active(true);
303 const bool was_internal = internal_editing();
305 previous_mouse_mode = mouse_mode;
308 if (!was_internal && internal_editing()) {
309 /* switched to internal, switch to internal snap settings */
310 set_snap_to(internal_snap_type);
311 set_snap_mode(internal_snap_mode);
312 } else if (was_internal && !internal_editing()) {
313 /* switched out of internal, switch to non-internal snap settings */
314 set_snap_to(pre_internal_snap_type);
315 set_snap_mode(pre_internal_snap_mode);
320 /* this should generate a new enter event which will
321 trigger the appropiate cursor.
325 _track_canvas->re_enter ();
328 set_gain_envelope_visibility ();
330 update_time_selection_display ();
332 MouseModeChanged (); /* EMIT SIGNAL */
336 Editor::internal_editing() const
338 return mouse_mode == Editing::MouseContent || mouse_mode == Editing::MouseDraw;
342 Editor::update_time_selection_display ()
344 if (smart_mode_action->get_active()) {
345 /* not sure what to do here */
346 if (mouse_mode == MouseObject) {
350 switch (mouse_mode) {
352 selection->clear_objects ();
353 selection->ClearMidiNoteSelection(); //signal
356 selection->clear_objects ();
357 selection->clear_time ();
358 selection->clear_tracks ();
359 selection->ClearMidiNoteSelection(); //signal
363 //if we go into internal editing, clear everything in the outside world
364 selection->clear_objects ();
365 selection->clear_time ();
366 selection->clear_tracks ();
370 selection->clear_objects ();
371 selection->clear_time ();
372 selection->clear_tracks ();
379 Editor::step_mouse_mode (bool next)
381 const int n_mouse_modes = (int)MouseContent + 1;
382 int current = (int)current_mouse_mode();
384 set_mouse_mode((MouseMode)((current + 1) % n_mouse_modes));
386 set_mouse_mode((MouseMode)((current + n_mouse_modes - 1) % n_mouse_modes));
391 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
393 /* in object/audition/timefx/gain-automation mode,
394 any button press sets the selection if the object
395 can be selected. this is a bit of hack, because
396 we want to avoid this if the mouse operation is a
399 note: not dbl-click or triple-click
401 Also note that there is no region selection in internal edit mode, otherwise
402 for operations operating on the selection (e.g. cut) it is not obvious whether
403 to cut notes or regions.
406 MouseMode eff_mouse_mode = effective_mouse_mode ();
408 if (eff_mouse_mode == MouseCut) {
409 /* never change selection in cut mode */
413 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
414 /* context clicks are always about object properties, even if
415 we're in range mode within smart mode.
417 eff_mouse_mode = MouseObject;
420 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
421 if (get_smart_mode()) {
423 case FadeInHandleItem:
424 case FadeInTrimHandleItem:
425 case FadeOutHandleItem:
426 case FadeOutTrimHandleItem:
427 eff_mouse_mode = MouseObject;
434 if (((mouse_mode != MouseObject) &&
435 (mouse_mode != MouseAudition || item_type != RegionItem) &&
436 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
437 (mouse_mode != MouseDraw)) ||
438 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
442 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
444 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
446 /* almost no selection action on modified button-2 or button-3 events */
448 if (item_type != RegionItem && event->button.button != 2) {
454 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
455 bool press = (event->type == GDK_BUTTON_PRESS);
460 if (eff_mouse_mode != MouseRange) {
461 set_selected_regionview_from_click (press, op);
463 /* don't change the selection unless the
464 clicked track is not currently selected. if
465 so, "collapse" the selection to just this
468 if (!selection->selected (clicked_axisview)) {
469 set_selected_track_as_side_effect (Selection::Set);
473 if (eff_mouse_mode != MouseRange) {
474 set_selected_regionview_from_click (press, op);
479 case RegionViewNameHighlight:
481 case LeftFrameHandle:
482 case RightFrameHandle:
483 case FadeInHandleItem:
484 case FadeInTrimHandleItem:
486 case FadeOutHandleItem:
487 case FadeOutTrimHandleItem:
489 case StartCrossFadeItem:
490 case EndCrossFadeItem:
491 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
492 set_selected_regionview_from_click (press, op);
493 } else if (event->type == GDK_BUTTON_PRESS) {
494 set_selected_track_as_side_effect (op);
498 case ControlPointItem:
499 set_selected_track_as_side_effect (op);
500 if (eff_mouse_mode != MouseRange) {
501 set_selected_control_point_from_click (press, op);
506 /* for context click, select track */
507 if (event->button.button == 3) {
508 selection->clear_tracks ();
509 set_selected_track_as_side_effect (op);
513 case AutomationTrackItem:
514 set_selected_track_as_side_effect (op);
523 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
525 /* single mouse clicks on any of these item types operate
526 independent of mouse mode, mostly because they are
527 not on the main track canvas or because we want
531 NoteBase* note = NULL;
534 case PlayheadCursorItem:
535 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
539 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
540 hide_marker (item, event);
542 _drags->set (new MarkerDrag (this, item), event);
546 case TempoMarkerItem:
549 new TempoMarkerDrag (
552 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
559 case MeterMarkerItem:
562 new MeterMarkerDrag (
565 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
573 _drags->set (new VideoTimeLineDrag (this, item), event);
580 case TimecodeRulerItem:
581 case SamplesRulerItem:
582 case MinsecRulerItem:
584 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
585 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
591 case RangeMarkerBarItem:
592 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
593 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
594 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
595 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
597 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
602 case CdMarkerBarItem:
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::CreateCDMarker), event);
611 case TransportMarkerBarItem:
612 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
613 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
615 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
624 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
625 /* special case: allow trim of range selections in joined object mode;
626 in theory eff should equal MouseRange in this case, but it doesn't
627 because entering the range selection canvas item results in entered_regionview
628 being set to 0, so update_join_object_range_location acts as if we aren't
631 if (item_type == StartSelectionTrimItem) {
632 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
633 } else if (item_type == EndSelectionTrimItem) {
634 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
638 Editing::MouseMode eff = effective_mouse_mode ();
640 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
641 if (get_smart_mode()) {
643 case FadeInHandleItem:
644 case FadeInTrimHandleItem:
645 case FadeOutHandleItem:
646 case FadeOutTrimHandleItem:
657 case StartSelectionTrimItem:
658 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
661 case EndSelectionTrimItem:
662 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
666 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
667 start_selection_grab (item, event);
669 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
670 /* grab selection for moving */
671 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
673 /* this was debated, but decided the more common action was to
674 make a new selection */
675 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
680 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
681 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
683 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
688 case RegionViewNameHighlight:
689 if (!clicked_regionview->region()->locked()) {
690 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
696 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
697 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
699 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
708 case FadeInHandleItem:
709 case FadeOutHandleItem:
710 case LeftFrameHandle:
711 case RightFrameHandle:
712 case FeatureLineItem:
713 case RegionViewNameHighlight:
716 case AutomationTrackItem:
717 _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, current_canvas_cursor);
728 /* Existing note: allow trimming/motion */
729 if ((note = reinterpret_cast<NoteBase*> (item->get_data ("notebase")))) {
730 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
731 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
733 _drags->set (new NoteDrag (this, item), event);
738 case ControlPointItem:
739 _drags->set (new ControlPointDrag (this, item), event);
744 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
745 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
750 case AutomationTrackItem:
751 /* rubberband drag to select automation points */
752 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
761 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
762 event->type == GDK_BUTTON_PRESS) {
764 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
766 } else if (event->type == GDK_BUTTON_PRESS) {
769 case FadeInHandleItem:
771 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
775 case FadeOutHandleItem:
777 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
781 case StartCrossFadeItem:
782 case EndCrossFadeItem:
783 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
784 // if (!clicked_regionview->region()->locked()) {
785 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
790 case FeatureLineItem:
792 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
793 remove_transient(item);
797 _drags->set (new FeatureLineDrag (this, item), event);
803 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
804 /* click on an automation region view; do nothing here and let the ARV's signal handler
810 /* click on a normal region view */
811 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
812 add_region_copy_drag (item, event, clicked_regionview);
813 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
814 add_region_brush_drag (item, event, clicked_regionview);
816 add_region_drag (item, event, clicked_regionview);
820 _drags->start_grab (event);
824 case RegionViewNameHighlight:
825 case LeftFrameHandle:
826 case RightFrameHandle:
827 if (!clicked_regionview->region()->locked()) {
828 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
833 case FadeInTrimHandleItem:
834 case FadeOutTrimHandleItem:
835 if (!clicked_regionview->region()->locked()) {
836 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
843 /* rename happens on edit clicks */
844 if (clicked_regionview->get_name_highlight()) {
845 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
851 case ControlPointItem:
852 _drags->set (new ControlPointDrag (this, item), event);
856 case AutomationLineItem:
857 _drags->set (new LineDrag (this, item), event);
862 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
865 case AutomationTrackItem:
867 TimeAxisView* parent = clicked_axisview->get_parent ();
868 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
870 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
872 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
874 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
875 if (pl->n_regions() == 0) {
876 /* Parent has no regions; create one so that we have somewhere to put automation */
877 _drags->set (new RegionCreateDrag (this, item, parent), event);
879 /* See if there's a region before the click that we can extend, and extend it if so */
880 framepos_t const t = canvas_event_sample (event);
881 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
883 _drags->set (new RegionCreateDrag (this, item, parent), event);
885 prev->set_length (t - prev->position ());
889 /* rubberband drag to select automation points */
890 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
914 _drags->set (new LineDrag (this, item), event);
917 case ControlPointItem:
918 _drags->set (new ControlPointDrag (this, item), event);
924 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
926 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
928 double const y = event->button.y;
929 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
931 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
933 /* smart "join" mode: drag automation */
934 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
942 case AutomationLineItem:
943 _drags->set (new LineDrag (this, item), event);
947 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
948 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
949 /* Note is big and pointer is near the end, trim */
950 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
953 _drags->set (new NoteDrag (this, item), event);
960 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
961 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
972 if (item_type == NoteItem) {
973 /* resize-drag notes */
974 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
975 if (note->big_enough_to_trim()) {
976 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
980 } else if (clicked_regionview) {
982 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
988 _drags->set (new ScrubDrag (this, item), event);
990 scrub_reverse_distance = 0;
991 last_scrub_x = event->button.x;
992 scrubbing_direction = 0;
993 push_canvas_cursor (_cursors->transparent);
1005 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1007 Editing::MouseMode const eff = effective_mouse_mode ();
1010 switch (item_type) {
1012 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1013 add_region_copy_drag (item, event, clicked_regionview);
1015 add_region_drag (item, event, clicked_regionview);
1017 _drags->start_grab (event);
1020 case ControlPointItem:
1021 _drags->set (new ControlPointDrag (this, item), event);
1029 switch (item_type) {
1030 case RegionViewNameHighlight:
1031 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1035 case LeftFrameHandle:
1036 case RightFrameHandle:
1037 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1041 case RegionViewName:
1042 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1056 /* relax till release */
1068 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1070 if (event->type == GDK_2BUTTON_PRESS) {
1071 _drags->mark_double_click ();
1072 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1076 if (event->type != GDK_BUTTON_PRESS) {
1080 pre_press_cursor = current_canvas_cursor;
1082 _track_canvas->grab_focus();
1084 if (_session && _session->actively_recording()) {
1088 button_selection (item, event, item_type);
1090 if (!_drags->active () &&
1091 (Keyboard::is_delete_event (&event->button) ||
1092 Keyboard::is_context_menu_event (&event->button) ||
1093 Keyboard::is_edit_event (&event->button))) {
1095 /* handled by button release */
1099 //not rolling, range mode click + join_play_range : locate the PH here
1100 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_follow_edits() ) {
1101 framepos_t where = canvas_event_sample (event);
1103 _session->request_locate (where, false);
1106 switch (event->button.button) {
1108 return button_press_handler_1 (item, event, item_type);
1112 return button_press_handler_2 (item, event, item_type);
1119 return button_press_dispatch (&event->button);
1128 Editor::button_press_dispatch (GdkEventButton* ev)
1130 /* this function is intended only for buttons 4 and above.
1133 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1134 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1138 Editor::button_release_dispatch (GdkEventButton* ev)
1140 /* this function is intended only for buttons 4 and above.
1143 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1144 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1148 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1150 framepos_t where = canvas_event_sample (event);
1151 AutomationTimeAxisView* atv = 0;
1153 if (pre_press_cursor) {
1154 set_canvas_cursor (pre_press_cursor);
1155 pre_press_cursor = 0;
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);
1328 /* delete events get handled here */
1330 Editing::MouseMode const eff = effective_mouse_mode ();
1332 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1334 switch (item_type) {
1335 case TempoMarkerItem:
1336 remove_tempo_marker (item);
1339 case MeterMarkerItem:
1340 remove_meter_marker (item);
1344 remove_marker (*item, event);
1348 if (eff == MouseObject) {
1349 remove_clicked_region ();
1353 case ControlPointItem:
1354 remove_control_point (item);
1358 remove_midi_note (item, event);
1367 switch (event->button.button) {
1370 switch (item_type) {
1371 /* see comments in button_press_handler */
1372 case PlayheadCursorItem:
1375 case AutomationLineItem:
1376 case StartSelectionTrimItem:
1377 case EndSelectionTrimItem:
1381 if (!_dragging_playhead) {
1382 snap_to_with_modifier (where, event, RoundNearest, true);
1383 mouse_add_new_marker (where);
1387 case CdMarkerBarItem:
1388 if (!_dragging_playhead) {
1389 // if we get here then a dragged range wasn't done
1390 snap_to_with_modifier (where, event, RoundNearest, true);
1391 mouse_add_new_marker (where, true);
1396 if (!_dragging_playhead) {
1397 snap_to_with_modifier (where, event);
1398 mouse_add_new_tempo_event (where);
1403 if (!_dragging_playhead) {
1404 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1409 case TimecodeRulerItem:
1410 case SamplesRulerItem:
1411 case MinsecRulerItem:
1422 switch (item_type) {
1425 /* check that we didn't drag before releasing, since
1426 its really annoying to create new control
1427 points when doing this.
1429 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1430 if (!were_dragging && arv) {
1431 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1432 arv->add_gain_point_event (item, event, with_guard_points);
1438 case AutomationTrackItem: {
1439 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1440 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1442 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1453 pop_canvas_cursor ();
1454 if (scrubbing_direction == 0) {
1455 /* no drag, just a click */
1456 switch (item_type) {
1458 play_selected_region ();
1464 /* make sure we stop */
1465 _session->request_transport_speed (0.0);
1474 /* do any (de)selection operations that should occur on button release */
1475 button_selection (item, event, item_type);
1484 switch (item_type) {
1486 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1488 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1491 // Button2 click is unused
1506 // x_style_paste (where, 1.0);
1527 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1534 /* by the time we reach here, entered_regionview and entered trackview
1535 * will have already been set as appropriate. Things are done this
1536 * way because this method isn't passed a pointer to a variable type of
1537 * thing that is entered (which may or may not be canvas item).
1538 * (e.g. the actual entered regionview)
1541 choose_canvas_cursor_on_entry (&event->crossing, item_type);
1543 switch (item_type) {
1544 case ControlPointItem:
1545 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1546 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1549 fraction = 1.0 - (cp->get_y() / cp->line().height());
1551 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1552 _verbose_cursor->show ();
1557 if (mouse_mode == MouseDraw) {
1558 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1560 line->set_outline_color (ARDOUR_UI::config()->color ("entered gain line"));
1565 case AutomationLineItem:
1566 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1567 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1569 line->set_outline_color (ARDOUR_UI::config()->color ("entered automation line"));
1574 case AutomationTrackItem:
1575 AutomationTimeAxisView* atv;
1576 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1577 clear_entered_track = false;
1578 set_entered_track (atv);
1583 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1586 entered_marker = marker;
1587 marker->set_color_rgba (ARDOUR_UI::config()->color ("entered marker"));
1589 case MeterMarkerItem:
1590 case TempoMarkerItem:
1593 case FadeInHandleItem:
1594 case FadeInTrimHandleItem:
1595 if (mouse_mode == MouseObject) {
1596 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1598 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1599 rect->set_fill_color (rv->get_fill_color());
1604 case FadeOutHandleItem:
1605 case FadeOutTrimHandleItem:
1606 if (mouse_mode == MouseObject) {
1607 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1609 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1610 rect->set_fill_color (rv->get_fill_color ());
1615 case FeatureLineItem:
1617 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1618 line->set_outline_color (0xFF0000FF);
1629 /* third pass to handle entered track status in a comprehensible way.
1632 switch (item_type) {
1634 case AutomationLineItem:
1635 case ControlPointItem:
1636 /* these do not affect the current entered track state */
1637 clear_entered_track = false;
1640 case AutomationTrackItem:
1641 /* handled above already */
1653 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1661 reset_canvas_cursor ();
1663 switch (item_type) {
1664 case ControlPointItem:
1665 _verbose_cursor->hide ();
1669 case AutomationLineItem:
1670 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1672 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1674 line->set_outline_color (al->get_line_color());
1680 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1684 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1685 location_flags_changed (loc);
1688 case MeterMarkerItem:
1689 case TempoMarkerItem:
1692 case FadeInTrimHandleItem:
1693 case FadeOutTrimHandleItem:
1694 case FadeInHandleItem:
1695 case FadeOutHandleItem:
1697 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1699 rect->set_fill_color (ARDOUR_UI::config()->color ("inactive fade handle"));
1704 case AutomationTrackItem:
1707 case FeatureLineItem:
1709 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1710 line->set_outline_color (ARDOUR_UI::config()->color ("zero line"));
1722 Editor::scrub (framepos_t frame, double current_x)
1726 if (scrubbing_direction == 0) {
1728 _session->request_locate (frame, false);
1729 _session->request_transport_speed (0.1);
1730 scrubbing_direction = 1;
1734 if (last_scrub_x > current_x) {
1736 /* pointer moved to the left */
1738 if (scrubbing_direction > 0) {
1740 /* we reversed direction to go backwards */
1743 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1747 /* still moving to the left (backwards) */
1749 scrub_reversals = 0;
1750 scrub_reverse_distance = 0;
1752 delta = 0.01 * (last_scrub_x - current_x);
1753 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1757 /* pointer moved to the right */
1759 if (scrubbing_direction < 0) {
1760 /* we reversed direction to go forward */
1763 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1766 /* still moving to the right */
1768 scrub_reversals = 0;
1769 scrub_reverse_distance = 0;
1771 delta = 0.01 * (current_x - last_scrub_x);
1772 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1776 /* if there have been more than 2 opposite motion moves detected, or one that moves
1777 back more than 10 pixels, reverse direction
1780 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1782 if (scrubbing_direction > 0) {
1783 /* was forwards, go backwards */
1784 _session->request_transport_speed (-0.1);
1785 scrubbing_direction = -1;
1787 /* was backwards, go forwards */
1788 _session->request_transport_speed (0.1);
1789 scrubbing_direction = 1;
1792 scrub_reverse_distance = 0;
1793 scrub_reversals = 0;
1797 last_scrub_x = current_x;
1801 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1803 _last_motion_y = event->motion.y;
1805 if (event->motion.is_hint) {
1808 /* We call this so that MOTION_NOTIFY events continue to be
1809 delivered to the canvas. We need to do this because we set
1810 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1811 the density of the events, at the expense of a round-trip
1812 to the server. Given that this will mostly occur on cases
1813 where DISPLAY = :0.0, and given the cost of what the motion
1814 event might do, its a good tradeoff.
1817 _track_canvas->get_pointer (x, y);
1820 if (current_stepping_trackview) {
1821 /* don't keep the persistent stepped trackview if the mouse moves */
1822 current_stepping_trackview = 0;
1823 step_timeout.disconnect ();
1826 if (_session && _session->actively_recording()) {
1827 /* Sorry. no dragging stuff around while we record */
1831 update_join_object_range_location (event->motion.y);
1833 if (_drags->active ()) {
1834 return _drags->motion_handler (event, from_autoscroll);
1841 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1843 ControlPoint* control_point;
1845 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1846 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1847 abort(); /*NOTREACHED*/
1850 AutomationLine& line = control_point->line ();
1851 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
1852 /* we shouldn't remove the first or last gain point in region gain lines */
1853 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
1862 Editor::remove_control_point (ArdourCanvas::Item* item)
1864 if (!can_remove_control_point (item)) {
1868 ControlPoint* control_point;
1870 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1871 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1872 abort(); /*NOTREACHED*/
1875 control_point->line().remove_point (*control_point);
1879 Editor::edit_control_point (ArdourCanvas::Item* item)
1881 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1884 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1885 abort(); /*NOTREACHED*/
1888 ControlPointDialog d (p);
1891 if (d.run () != RESPONSE_ACCEPT) {
1895 p->line().modify_point_y (*p, d.get_y_fraction ());
1899 Editor::edit_notes (TimeAxisViewItem& tavi)
1901 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
1907 MidiRegionView::Selection const & s = mrv->selection();
1913 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
1917 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
1921 Editor::note_edit_done (int r, EditNoteDialog* d)
1928 Editor::visible_order_range (int* low, int* high) const
1930 *low = TimeAxisView::max_order ();
1933 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1935 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1937 if (!rtv->hidden()) {
1939 if (*high < rtv->order()) {
1940 *high = rtv->order ();
1943 if (*low > rtv->order()) {
1944 *low = rtv->order ();
1951 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1953 /* Either add to or set the set the region selection, unless
1954 this is an alignment click (control used)
1957 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1958 TimeAxisView* tv = &rv.get_time_axis_view();
1959 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1961 if (rtv && rtv->is_track()) {
1962 speed = rtv->track()->speed();
1965 framepos_t where = get_preferred_edit_position();
1969 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1971 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
1973 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1975 align_region (rv.region(), End, (framepos_t) (where * speed));
1979 align_region (rv.region(), Start, (framepos_t) (where * speed));
1986 Editor::collect_new_region_view (RegionView* rv)
1988 latest_regionviews.push_back (rv);
1992 Editor::collect_and_select_new_region_view (RegionView* rv)
1995 latest_regionviews.push_back (rv);
1999 Editor::cancel_selection ()
2001 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2002 (*i)->hide_selection ();
2005 selection->clear ();
2006 clicked_selection = 0;
2010 Editor::cancel_time_selection ()
2012 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2013 (*i)->hide_selection ();
2015 selection->time.clear ();
2016 clicked_selection = 0;
2020 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2022 RegionView* rv = clicked_regionview;
2024 /* Choose action dependant on which button was pressed */
2025 switch (event->button.button) {
2027 begin_reversible_command (_("start point trim"));
2029 if (selection->selected (rv)) {
2030 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2031 i != selection->regions.by_layer().end(); ++i)
2033 if (!(*i)->region()->locked()) {
2034 (*i)->region()->clear_changes ();
2035 (*i)->region()->trim_front (new_bound);
2036 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2041 if (!rv->region()->locked()) {
2042 rv->region()->clear_changes ();
2043 rv->region()->trim_front (new_bound);
2044 _session->add_command(new StatefulDiffCommand (rv->region()));
2048 commit_reversible_command();
2052 begin_reversible_command (_("End point trim"));
2054 if (selection->selected (rv)) {
2056 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2058 if (!(*i)->region()->locked()) {
2059 (*i)->region()->clear_changes();
2060 (*i)->region()->trim_end (new_bound);
2061 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2067 if (!rv->region()->locked()) {
2068 rv->region()->clear_changes ();
2069 rv->region()->trim_end (new_bound);
2070 _session->add_command (new StatefulDiffCommand (rv->region()));
2074 commit_reversible_command();
2083 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2088 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2089 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2090 abort(); /*NOTREACHED*/
2093 Location* location = find_location_from_marker (marker, is_start);
2094 location->set_hidden (true, this);
2098 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2100 using namespace Gtkmm2ext;
2102 ArdourPrompter prompter (false);
2104 prompter.set_prompt (_("Name for region:"));
2105 prompter.set_initial_text (clicked_regionview->region()->name());
2106 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2107 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2108 prompter.show_all ();
2109 switch (prompter.run ()) {
2110 case Gtk::RESPONSE_ACCEPT:
2112 prompter.get_result(str);
2114 clicked_regionview->region()->set_name (str);
2123 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2125 /* no brushing without a useful snap setting */
2127 switch (_snap_mode) {
2129 return; /* can't work because it allows region to be placed anywhere */
2134 switch (_snap_type) {
2142 /* don't brush a copy over the original */
2144 if (pos == rv->region()->position()) {
2148 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2150 if (rtv == 0 || !rtv->is_track()) {
2154 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2155 double speed = rtv->track()->speed();
2157 playlist->clear_changes ();
2158 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2159 playlist->add_region (new_region, (framepos_t) (pos * speed));
2160 _session->add_command (new StatefulDiffCommand (playlist));
2162 // playlist is frozen, so we have to update manually XXX this is disgusting
2164 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2168 Editor::track_height_step_timeout ()
2170 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2171 current_stepping_trackview = 0;
2178 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2180 assert (region_view);
2182 if (!region_view->region()->playlist()) {
2186 switch (Config->get_edit_mode()) {
2188 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2191 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2194 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2201 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2203 assert (region_view);
2205 if (!region_view->region()->playlist()) {
2209 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2213 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2215 assert (region_view);
2217 if (!region_view->region()->playlist()) {
2221 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2225 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2227 begin_reversible_command (Operations::drag_region_brush);
2230 /** Start a grab where a time range is selected, track(s) are selected, and the
2231 * user clicks and drags a region with a modifier in order to create a new region containing
2232 * the section of the clicked region that lies within the time range.
2235 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2237 if (clicked_regionview == 0) {
2241 /* lets try to create new Region for the selection */
2243 vector<boost::shared_ptr<Region> > new_regions;
2244 create_region_from_selection (new_regions);
2246 if (new_regions.empty()) {
2250 /* XXX fix me one day to use all new regions */
2252 boost::shared_ptr<Region> region (new_regions.front());
2254 /* add it to the current stream/playlist.
2256 tricky: the streamview for the track will add a new regionview. we will
2257 catch the signal it sends when it creates the regionview to
2258 set the regionview we want to then drag.
2261 latest_regionviews.clear();
2262 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2264 /* A selection grab currently creates two undo/redo operations, one for
2265 creating the new region and another for moving it.
2268 begin_reversible_command (Operations::selection_grab);
2270 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2272 playlist->clear_changes ();
2273 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2274 _session->add_command(new StatefulDiffCommand (playlist));
2278 if (latest_regionviews.empty()) {
2279 /* something went wrong */
2283 /* we need to deselect all other regionviews, and select this one
2284 i'm ignoring undo stuff, because the region creation will take care of it
2287 selection->set (latest_regionviews);
2289 commit_reversible_command ();
2291 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2297 if (_drags->active ()) {
2300 selection->clear ();
2306 /** Update _join_object_range_state which indicate whether we are over the top
2307 * or bottom half of a route view, used by the `join object/range' tool
2308 * mode. Coordinates in canvas space.
2311 Editor::update_join_object_range_location (double y)
2313 if (!get_smart_mode()) {
2314 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2318 JoinObjectRangeState const old = _join_object_range_state;
2320 if (mouse_mode == MouseObject) {
2321 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2322 } else if (mouse_mode == MouseRange) {
2323 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2326 if (entered_regionview) {
2328 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2329 double const c = item_space.y / entered_regionview->height();
2331 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2333 if (_join_object_range_state != old) {
2334 set_canvas_cursor (which_track_cursor ());
2337 } else if (entered_track) {
2339 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2341 if (entered_route_view) {
2346 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2348 double track_height = entered_route_view->view()->child_height();
2349 if (Config->get_show_name_highlight()) {
2350 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2352 double const c = cy / track_height;
2356 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2358 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2362 /* Other kinds of tracks use object mode */
2363 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2366 if (_join_object_range_state != old) {
2367 set_canvas_cursor (which_track_cursor ());
2373 Editor::effective_mouse_mode () const
2375 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2377 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2385 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2387 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2390 e->region_view().delete_note (e->note ());
2393 /** Obtain the pointer position in canvas coordinates */
2395 Editor::get_pointer_position (double& x, double& y) const
2398 _track_canvas->get_pointer (px, py);
2399 _track_canvas->window_to_canvas (px, py, x, y);