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"
38 #include "canvas/canvas.h"
40 #include "ardour/audioregion.h"
41 #include "ardour/operations.h"
42 #include "ardour/playlist.h"
43 #include "ardour/profile.h"
44 #include "ardour/region_factory.h"
45 #include "ardour/route.h"
46 #include "ardour/session.h"
47 #include "ardour/types.h"
49 #include "widgets/prompter.h"
52 #include "ardour_ui.h"
54 #include "time_axis_view.h"
55 #include "audio_time_axis.h"
56 #include "audio_region_view.h"
57 #include "midi_region_view.h"
59 #include "streamview.h"
60 #include "region_gain_line.h"
61 #include "rc_option_editor.h"
62 #include "automation_time_axis.h"
63 #include "control_point.h"
64 #include "selection.h"
67 #include "rgb_macros.h"
68 #include "control_point_dialog.h"
69 #include "editor_drag.h"
70 #include "automation_region_view.h"
71 #include "edit_note_dialog.h"
72 #include "mouse_cursors.h"
73 #include "editor_cursors.h"
74 #include "verbose_cursor.h"
80 using namespace ARDOUR;
83 using namespace Editing;
84 using Gtkmm2ext::Keyboard;
87 Editor::mouse_sample (samplepos_t& where, bool& in_track_canvas) const
89 /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
90 pays attentions to subwindows. this means that menu windows are ignored, and
91 if the pointer is in a menu, the return window from the call will be the
92 the regular subwindow *under* the menu.
94 this matters quite a lot if the pointer is moving around in a menu that overlaps
95 the track canvas because we will believe that we are within the track canvas
96 when we are not. therefore, we track enter/leave events for the track canvas
97 and allow that to override the result of gdk_window_get_pointer().
100 if (!within_track_canvas) {
105 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
107 if (!canvas_window) {
111 Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
113 if (!pointer_window) {
117 if (pointer_window != canvas_window) {
118 in_track_canvas = false;
122 in_track_canvas = true;
125 event.type = GDK_BUTTON_RELEASE;
129 where = window_event_sample (&event, 0, 0);
135 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
137 ArdourCanvas::Duple d;
139 if (!gdk_event_get_coords (event, &d.x, &d.y)) {
143 /* event coordinates are in window units, so convert to canvas
146 d = _track_canvas->window_to_canvas (d);
156 return pixel_to_sample (d.x);
160 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
165 /* event coordinates are already in canvas units */
167 if (!gdk_event_get_coords (event, &x, &y)) {
168 cerr << "!NO c COORDS for event type " << event->type << endl;
180 /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel
181 position is negative (as can be the case with motion events in particular),
182 the sample location is always positive.
185 return pixel_to_sample_from_event (x);
189 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
191 boost::shared_ptr<Trimmable> st = _trimmable.lock();
193 if (!st || st == t) {
199 Editor::set_current_movable (boost::shared_ptr<Movable> m)
201 boost::shared_ptr<Movable> sm = _movable.lock();
203 if (!sm || sm != m) {
209 Editor::mouse_mode_object_range_toggled()
211 MouseMode m = mouse_mode;
213 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
215 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
218 set_mouse_mode (m, true); // call this so the button styles can get updated
222 Editor::snap_mode_button_clicked (GdkEventButton* ev)
224 if (ev->button != 3) {
229 RCOptionEditor* rc_option_editor = ARDOUR_UI::instance()->get_rc_option_editor();
230 if (rc_option_editor) {
231 ARDOUR_UI::instance()->show_tabbable (rc_option_editor);
232 rc_option_editor->set_current_page (_("Editor/Snap"));
240 static Glib::RefPtr<Action>
241 get_mouse_mode_action(MouseMode m)
245 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
247 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
249 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
251 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
253 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
255 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-content"));
257 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
259 return Glib::RefPtr<Action>();
263 Editor::set_mouse_mode (MouseMode m, bool force)
265 if (_drags->active ()) {
269 if (!force && m == mouse_mode) {
273 if (ARDOUR::Profile->get_mixbus()) {
274 if (m == MouseCut) m = MouseObject;
275 if (m == MouseAudition) m = MouseRange;
278 Glib::RefPtr<Action> act = get_mouse_mode_action(m);
279 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
281 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
282 tact->set_active (false);
283 tact->set_active (true);
285 /* NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting */
289 Editor::mouse_mode_toggled (MouseMode m)
291 if (ARDOUR::Profile->get_mixbus()) {
292 if (m == MouseCut) m = MouseObject;
293 if (m == MouseAudition) m = MouseRange;
296 Glib::RefPtr<Action> act = get_mouse_mode_action(m);
297 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
299 if (!tact->get_active()) {
300 /* this was just the notification that the old mode has been
301 * left. we'll get called again with the new mode active in a
307 if (_session && mouse_mode == MouseAudition) {
308 /* stop transport and reset default speed to avoid oddness with
310 _session->request_transport_speed (0.0, true);
313 const bool was_internal = internal_editing();
317 /* Switch snap type/mode if we're moving to/from an internal tool. Note
318 this must toggle the actions and not call set_snap_*() directly,
319 otherwise things get out of sync and the combo box stops working. */
320 if (!was_internal && internal_editing()) {
321 grid_type_action(internal_grid_type)->set_active(true);
322 snap_mode_action(internal_snap_mode)->set_active(true);
323 } else if (was_internal && !internal_editing()) {
324 grid_type_action(pre_internal_grid_type)->set_active(true);
325 snap_mode_action(pre_internal_snap_mode)->set_active(true);
330 /* this should generate a new enter event which will
331 trigger the appropiate cursor.
335 _track_canvas->re_enter ();
338 set_gain_envelope_visibility ();
340 update_time_selection_display ();
342 update_all_enter_cursors ();
344 MouseModeChanged (); /* EMIT SIGNAL */
348 Editor::internal_editing() const
350 return mouse_mode == Editing::MouseContent || mouse_mode == Editing::MouseDraw;
354 Editor::update_time_selection_display ()
356 switch (mouse_mode) {
358 selection->clear_objects ();
359 selection->clear_midi_notes ();
362 selection->clear_time ();
363 selection->clear_midi_notes ();
366 /* Clear regions, but not time or tracks, since that
367 would destroy the range selection rectangle, which we need to stick
368 around for AutomationRangeDrag. */
369 selection->clear_regions ();
370 selection->clear_playlists ();
373 /* This handles internal edit.
374 Clear everything except points and notes.
376 selection->clear_regions();
377 selection->clear_lines();
378 selection->clear_playlists ();
380 selection->clear_time ();
381 selection->clear_tracks ();
385 /* We probably want to keep region selection */
386 selection->clear_points ();
387 selection->clear_lines();
388 selection->clear_playlists ();
390 selection->clear_time ();
391 selection->clear_tracks ();
395 /*Don't lose lines or points if no action in this mode */
396 selection->clear_regions ();
397 selection->clear_playlists ();
398 selection->clear_time ();
399 selection->clear_tracks ();
403 /*Clear everything */
404 selection->clear_objects();
405 selection->clear_time ();
406 selection->clear_tracks ();
412 Editor::step_mouse_mode (bool next)
414 const int n_mouse_modes = (int)MouseContent + 1;
415 int current = (int)current_mouse_mode();
417 set_mouse_mode((MouseMode)((current + 1) % n_mouse_modes));
419 set_mouse_mode((MouseMode)((current + n_mouse_modes - 1) % n_mouse_modes));
424 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
427 /* in object/audition/timefx/gain-automation mode,
428 any button press sets the selection if the object
429 can be selected. this is a bit of hack, because
430 we want to avoid this if the mouse operation is a
433 note: not dbl-click or triple-click
435 Also note that there is no region selection in internal edit mode, otherwise
436 for operations operating on the selection (e.g. cut) it is not obvious whether
437 to cut notes or regions.
440 MouseMode eff_mouse_mode = effective_mouse_mode ();
442 if (eff_mouse_mode == MouseCut) {
443 /* never change selection in cut mode */
447 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
448 /* context clicks are always about object properties, even if
449 we're in range mode within smart mode.
451 eff_mouse_mode = MouseObject;
454 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
455 if (get_smart_mode()) {
457 case FadeInHandleItem:
458 case FadeInTrimHandleItem:
459 case FadeOutHandleItem:
460 case FadeOutTrimHandleItem:
461 eff_mouse_mode = MouseObject;
468 if (((mouse_mode != MouseObject) &&
469 (mouse_mode != MouseAudition || item_type != RegionItem) &&
470 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
471 (mouse_mode != MouseDraw) &&
472 (mouse_mode != MouseContent || item_type == RegionItem)) ||
473 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
477 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
479 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
481 /* almost no selection action on modified button-2 or button-3 events */
483 if ((item_type != RegionItem && event->button.button != 2)
484 /* for selection of control points prior to delete (shift-right click) */
485 && !(item_type == ControlPointItem && event->button.button == 3 && event->type == GDK_BUTTON_PRESS)) {
491 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
492 bool press = (event->type == GDK_BUTTON_PRESS);
495 _mouse_changed_selection = false;
500 if (eff_mouse_mode == MouseDraw) {
504 if (eff_mouse_mode != MouseRange) {
505 _mouse_changed_selection = set_selected_regionview_from_click (press, op);
507 /* don't change the selection unless the
508 clicked track is not currently selected. if
509 so, "collapse" the selection to just this
512 if (!selection->selected (clicked_axisview)) {
513 set_selected_track_as_side_effect (Selection::Set);
517 if (eff_mouse_mode != MouseRange) {
518 _mouse_changed_selection |= set_selected_regionview_from_click (press, op);
523 case RegionViewNameHighlight:
525 case LeftFrameHandle:
526 case RightFrameHandle:
527 case FadeInHandleItem:
528 case FadeInTrimHandleItem:
530 case FadeOutHandleItem:
531 case FadeOutTrimHandleItem:
533 case StartCrossFadeItem:
534 case EndCrossFadeItem:
535 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
536 _mouse_changed_selection |= set_selected_regionview_from_click (press, op);
537 } else if (event->type == GDK_BUTTON_PRESS) {
538 set_selected_track_as_side_effect (op);
542 case ControlPointItem:
543 /* for object/track exclusivity, we don't call set_selected_track_as_side_effect (op); */
545 if (eff_mouse_mode != MouseRange) {
546 if (event->button.button != 3) {
547 _mouse_changed_selection |= set_selected_control_point_from_click (press, op);
549 _mouse_changed_selection |= set_selected_control_point_from_click (press, Selection::Set);
555 if (eff_mouse_mode != MouseRange) {
556 AutomationLine* argl = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
558 std::list<Selectable*> selectables;
559 uint32_t before, after;
560 samplecnt_t const where = (samplecnt_t) floor (event->button.x * samples_per_pixel) - clicked_regionview->region ()->position ();
562 if (!argl || !argl->control_points_adjacent (where, before, after)) {
566 selectables.push_back (argl->nth (before));
567 selectables.push_back (argl->nth (after));
572 selection->set (selectables);
573 _mouse_changed_selection = true;
578 selection->add (selectables);
579 _mouse_changed_selection = true;
582 case Selection::Toggle:
584 selection->toggle (selectables);
585 _mouse_changed_selection = true;
589 case Selection::Extend:
596 case AutomationLineItem:
597 if (eff_mouse_mode != MouseRange) {
598 AutomationLine* al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
599 std::list<Selectable*> selectables;
600 double mx = event->button.x;
601 double my = event->button.y;
603 al->grab_item().canvas_to_item (mx, my);
605 uint32_t before, after;
606 samplecnt_t const where = (samplecnt_t) floor (mx * samples_per_pixel);
608 if (!al || !al->control_points_adjacent (where, before, after)) {
612 selectables.push_back (al->nth (before));
613 selectables.push_back (al->nth (after));
618 selection->set (selectables);
619 _mouse_changed_selection = true;
624 selection->add (selectables);
625 _mouse_changed_selection = true;
628 case Selection::Toggle:
630 selection->toggle (selectables);
631 _mouse_changed_selection = true;
635 case Selection::Extend:
643 /* for context click, select track */
644 if (event->button.button == 3) {
645 selection->clear_tracks ();
646 set_selected_track_as_side_effect (op);
648 /* We won't get a release.*/
649 begin_reversible_selection_op (X_("Button 3 Menu Select"));
650 commit_reversible_selection_op ();
654 case AutomationTrackItem:
655 if (eff_mouse_mode != MouseDraw && op == Selection::Set) {
656 set_selected_track_as_side_effect (op);
661 if (press && event->button.button == 3) {
662 NoteBase* cnote = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
664 if (cnote->region_view().selection_size() == 0 || !cnote->selected()) {
665 selection->clear_points();
666 cnote->region_view().unique_select (cnote);
667 /* we won't get the release, so store the selection change now */
668 begin_reversible_selection_op (X_("Button 3 Note Selection"));
669 commit_reversible_selection_op ();
678 if ((!press) && _mouse_changed_selection) {
679 begin_reversible_selection_op (X_("Button Selection"));
680 commit_reversible_selection_op ();
681 _mouse_changed_selection = false;
686 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
688 /* single mouse clicks on any of these item types operate
689 independent of mouse mode, mostly because they are
690 not on the main track canvas or because we want
694 NoteBase* note = NULL;
697 case PlayheadCursorItem:
698 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
702 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
703 hide_marker (item, event);
705 _drags->set (new MarkerDrag (this, item), event);
709 case TempoMarkerItem:
711 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
721 new TempoMarkerDrag (
724 ArdourKeyboard::indicates_copy (event->button.state)
733 case MeterMarkerItem:
736 new MeterMarkerDrag (
739 ArdourKeyboard::indicates_copy (event->button.state)
747 _drags->set (new VideoTimeLineDrag (this, item), event);
755 case TimecodeRulerItem:
756 case SamplesRulerItem:
757 case MinsecRulerItem:
759 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)
760 && !ArdourKeyboard::indicates_constraint (event->button.state)) {
761 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
762 } else if (ArdourKeyboard::indicates_constraint (event->button.state)
763 && Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
764 _drags->set (new TempoTwistDrag (this, item), event);
765 } else if (ArdourKeyboard::indicates_constraint (event->button.state)) {
766 _drags->set (new BBTRulerDrag (this, item), event);
772 case RangeMarkerBarItem:
773 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
774 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
775 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
776 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
778 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
783 case CdMarkerBarItem:
784 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
785 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
787 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
792 case TransportMarkerBarItem:
793 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
794 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
796 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
805 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
806 /* special case: allow trim of range selections in joined object mode;
807 in theory eff should equal MouseRange in this case, but it doesn't
808 because entering the range selection canvas item results in entered_regionview
809 being set to 0, so update_join_object_range_location acts as if we aren't
812 if (item_type == StartSelectionTrimItem) {
813 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
814 } else if (item_type == EndSelectionTrimItem) {
815 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
819 Editing::MouseMode eff = effective_mouse_mode ();
821 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
822 if (get_smart_mode()) {
824 case FadeInHandleItem:
825 case FadeInTrimHandleItem:
826 case FadeOutHandleItem:
827 case FadeOutTrimHandleItem:
838 case StartSelectionTrimItem:
839 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
842 case EndSelectionTrimItem:
843 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
847 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
848 start_selection_grab (item, event);
850 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
851 /* grab selection for moving */
852 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
854 /* this was debated, but decided the more common action was to
855 make a new selection */
856 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
861 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
862 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
864 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
869 case RegionViewNameHighlight:
870 if (!clicked_regionview->region()->locked()) {
871 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
877 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
878 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
880 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
889 case FadeInHandleItem:
890 case FadeOutHandleItem:
891 case LeftFrameHandle:
892 case RightFrameHandle:
893 case FeatureLineItem:
894 case RegionViewNameHighlight:
897 case AutomationTrackItem:
898 _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, get_canvas_cursor());
909 /* Existing note: allow trimming/motion */
910 if ((note = reinterpret_cast<NoteBase*> (item->get_data ("notebase")))) {
911 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
912 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
914 _drags->set (new NoteDrag (this, item), event);
920 _drags->set (new LineDrag (this, item), event);
924 case ControlPointItem:
925 _drags->set (new ControlPointDrag (this, item), event);
929 case AutomationLineItem:
930 _drags->set (new LineDrag (this, item), event);
935 /* in the past, we created a new midi region here, but perhaps that is best left to the Draw mode */
938 case AutomationTrackItem:
939 /* rubberband drag to select automation points */
940 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
945 if (dynamic_cast<AutomationRegionView*>(clicked_regionview)) {
946 /* rubberband drag to select automation points */
947 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
958 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
959 event->type == GDK_BUTTON_PRESS) {
961 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
963 } else if (event->type == GDK_BUTTON_PRESS) {
966 case FadeInHandleItem:
968 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
972 case FadeOutHandleItem:
974 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
978 case StartCrossFadeItem:
979 case EndCrossFadeItem:
980 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor.
981 * For not this is not fully implemented */
983 if (!clicked_regionview->region()->locked()) {
984 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
990 case FeatureLineItem:
992 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
993 remove_transient(item);
997 _drags->set (new FeatureLineDrag (this, item), event);
1003 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1004 /* click on an automation region view; do nothing here and let the ARV's signal handler
1010 /* click on a normal region view */
1011 if (ArdourKeyboard::indicates_copy (event->button.state)) {
1012 add_region_copy_drag (item, event, clicked_regionview);
1013 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
1014 add_region_brush_drag (item, event, clicked_regionview);
1016 add_region_drag (item, event, clicked_regionview);
1020 _drags->start_grab (event);
1024 case RegionViewNameHighlight:
1025 case LeftFrameHandle:
1026 case RightFrameHandle:
1027 if (!clicked_regionview->region()->locked()) {
1028 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1033 case FadeInTrimHandleItem:
1034 case FadeOutTrimHandleItem:
1035 if (!clicked_regionview->region()->locked()) {
1036 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
1041 case RegionViewName:
1043 /* rename happens on edit clicks */
1044 if (clicked_regionview->get_name_highlight()) {
1045 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1051 case ControlPointItem:
1052 _drags->set (new ControlPointDrag (this, item), event);
1056 case AutomationLineItem:
1057 _drags->set (new LineDrag (this, item), event);
1062 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1065 case AutomationTrackItem:
1067 TimeAxisView* parent = clicked_axisview->get_parent ();
1068 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1070 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1072 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1074 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1075 if (pl->n_regions() == 0) {
1076 /* Parent has no regions; create one so that we have somewhere to put automation */
1077 _drags->set (new RegionCreateDrag (this, item, parent), event);
1079 /* See if there's a region before the click that we can extend, and extend it if so */
1080 samplepos_t const t = canvas_event_sample (event);
1081 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1083 _drags->set (new RegionCreateDrag (this, item, parent), event);
1085 prev->set_length (t - prev->position (), get_grid_music_divisions (event->button.state));
1089 /* rubberband drag to select automation points */
1090 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1112 switch (item_type) {
1114 _drags->set (new LineDrag (this, item), event);
1117 case ControlPointItem:
1118 _drags->set (new ControlPointDrag (this, item), event);
1124 if (dynamic_cast<AudioRegionView*>(clicked_regionview) ||
1125 dynamic_cast<AutomationRegionView*>(clicked_regionview)) {
1126 _drags->set (new AutomationRangeDrag (this, clicked_regionview, selection->time),
1127 event, _cursors->up_down);
1129 double const y = event->button.y;
1130 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y, false);
1132 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1134 /* smart "join" mode: drag automation */
1135 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1143 case AutomationLineItem:
1144 _drags->set (new LineDrag (this, item), event);
1148 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
1149 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
1150 /* Note is big and pointer is near the end, trim */
1151 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
1154 _drags->set (new NoteDrag (this, item), event);
1161 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1162 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1173 if (item_type == NoteItem) {
1174 /* resize-drag notes */
1175 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
1176 if (note->big_enough_to_trim()) {
1177 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
1181 } else if (clicked_regionview) {
1183 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1189 _drags->set (new ScrubDrag (this, item), event, _cursors->transparent);
1190 scrub_reversals = 0;
1191 scrub_reverse_distance = 0;
1192 last_scrub_x = event->button.x;
1193 scrubbing_direction = 0;
1205 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1207 Editing::MouseMode const eff = effective_mouse_mode ();
1210 switch (item_type) {
1212 if (ArdourKeyboard::indicates_copy (event->button.state)) {
1213 add_region_copy_drag (item, event, clicked_regionview);
1215 add_region_drag (item, event, clicked_regionview);
1217 _drags->start_grab (event);
1220 case ControlPointItem:
1221 _drags->set (new ControlPointDrag (this, item), event);
1229 switch (item_type) {
1230 case RegionViewNameHighlight:
1231 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1235 case LeftFrameHandle:
1236 case RightFrameHandle:
1237 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1241 case RegionViewName:
1242 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1256 /* relax till release */
1268 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1270 if (event->type == GDK_2BUTTON_PRESS) {
1271 _drags->mark_double_click ();
1272 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1276 if (event->type != GDK_BUTTON_PRESS) {
1280 _track_canvas->grab_focus();
1282 if (_session && _session->actively_recording()) {
1286 button_selection (item, event, item_type);
1288 if (!_drags->active () &&
1289 (Keyboard::is_delete_event (&event->button) ||
1290 Keyboard::is_context_menu_event (&event->button) ||
1291 Keyboard::is_edit_event (&event->button))) {
1293 /* handled by button release */
1297 /* not rolling, effectively in range mode, follow edits enabled (likely
1298 * to start range drag), not in a fade handle (since that means we are
1299 * not starting a range drag): locate the PH here
1302 if ((item_type != FadeInHandleItem) &&
1303 (item_type != FadeOutHandleItem) &&
1304 !_drags->active () &&
1306 !_session->transport_rolling() &&
1307 (effective_mouse_mode() == MouseRange) &&
1308 UIConfiguration::instance().get_follow_edits() &&
1309 !_session->config.get_external_sync()) {
1311 MusicSample where (canvas_event_sample (event), 0);
1313 _session->request_locate (where.sample, false);
1316 switch (event->button.button) {
1318 return button_press_handler_1 (item, event, item_type);
1322 return button_press_handler_2 (item, event, item_type);
1329 return button_press_dispatch (&event->button);
1338 Editor::button_press_dispatch (GdkEventButton* ev)
1340 /* this function is intended only for buttons 4 and above.
1343 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1344 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1348 Editor::button_release_dispatch (GdkEventButton* ev)
1350 /* this function is intended only for buttons 4 and above.
1353 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1354 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1358 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1360 MusicSample where (canvas_event_sample (event), 0);
1361 AutomationTimeAxisView* atv = 0;
1363 _press_cursor_ctx.reset();
1365 /* no action if we're recording */
1367 if (_session && _session->actively_recording()) {
1371 bool were_dragging = false;
1373 if (!Keyboard::is_context_menu_event (&event->button)) {
1375 /* see if we're finishing a drag */
1377 if (_drags->active ()) {
1378 bool const r = _drags->end_grab (event);
1380 /* grab dragged, so do nothing else */
1384 were_dragging = true;
1387 update_region_layering_order_editor ();
1390 /* edit events get handled here */
1392 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1393 switch (item_type) {
1395 show_region_properties ();
1397 case TempoMarkerItem: {
1398 ArdourMarker* marker;
1399 TempoMarker* tempo_marker;
1401 if ((marker = reinterpret_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1402 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1403 abort(); /*NOTREACHED*/
1406 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1407 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1408 abort(); /*NOTREACHED*/
1411 edit_tempo_marker (*tempo_marker);
1415 case MeterMarkerItem: {
1416 ArdourMarker* marker;
1417 MeterMarker* meter_marker;
1419 if ((marker = reinterpret_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1420 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1421 abort(); /*NOTREACHED*/
1424 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1425 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1426 abort(); /*NOTREACHED*/
1428 edit_meter_marker (*meter_marker);
1432 case RegionViewName:
1433 if (clicked_regionview->name_active()) {
1434 return mouse_rename_region (item, event);
1438 case ControlPointItem:
1439 edit_control_point (item);
1448 /* context menu events get handled here */
1449 if (Keyboard::is_context_menu_event (&event->button)) {
1451 context_click_event = *event;
1453 if (!_drags->active ()) {
1455 /* no matter which button pops up the context menu, tell the menu
1456 widget to use button 1 to drive menu selection.
1459 switch (item_type) {
1461 case FadeInHandleItem:
1462 case FadeInTrimHandleItem:
1463 case StartCrossFadeItem:
1464 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1468 case FadeOutHandleItem:
1469 case FadeOutTrimHandleItem:
1470 case EndCrossFadeItem:
1471 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1474 case LeftFrameHandle:
1475 case RightFrameHandle:
1479 popup_track_context_menu (1, event->button.time, item_type, false);
1483 case RegionViewNameHighlight:
1484 case RegionViewName:
1485 popup_track_context_menu (1, event->button.time, item_type, false);
1489 popup_track_context_menu (1, event->button.time, item_type, true);
1492 case AutomationTrackItem:
1493 popup_track_context_menu (1, event->button.time, item_type, false);
1497 case RangeMarkerBarItem:
1498 case TransportMarkerBarItem:
1499 case CdMarkerBarItem:
1501 case TempoCurveItem:
1504 case TimecodeRulerItem:
1505 case SamplesRulerItem:
1506 case MinsecRulerItem:
1508 popup_ruler_menu (where.sample, item_type);
1512 marker_context_menu (&event->button, item);
1515 case TempoMarkerItem:
1516 tempo_or_meter_marker_context_menu (&event->button, item);
1519 case MeterMarkerItem:
1520 tempo_or_meter_marker_context_menu (&event->button, item);
1523 case CrossfadeViewItem:
1524 popup_track_context_menu (1, event->button.time, item_type, false);
1527 case ControlPointItem:
1528 popup_control_point_context_menu (item, event);
1532 if (internal_editing()) {
1533 popup_note_context_menu (item, event);
1545 /* delete events get handled here */
1547 Editing::MouseMode const eff = effective_mouse_mode ();
1549 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1551 switch (item_type) {
1552 case TempoMarkerItem:
1553 remove_tempo_marker (item);
1556 case MeterMarkerItem:
1557 remove_meter_marker (item);
1561 remove_marker (*item, event);
1565 if (eff == MouseObject) {
1566 remove_clicked_region ();
1570 case ControlPointItem:
1571 remove_control_point (item);
1575 remove_midi_note (item, event);
1584 switch (event->button.button) {
1587 switch (item_type) {
1588 /* see comments in button_press_handler */
1589 case PlayheadCursorItem:
1592 case AutomationLineItem:
1593 case StartSelectionTrimItem:
1594 case EndSelectionTrimItem:
1598 if (!_dragging_playhead) {
1599 snap_to_with_modifier (where, event, RoundNearest, SnapToAny, true);
1600 mouse_add_new_marker (where.sample);
1604 case CdMarkerBarItem:
1605 if (!_dragging_playhead) {
1606 /* if we get here then a dragged range wasn't done */
1607 snap_to_with_modifier (where, event, RoundNearest, SnapToAny, true);
1608 mouse_add_new_marker (where.sample, true);
1612 case TempoCurveItem:
1613 if (!_dragging_playhead && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1614 snap_to_with_modifier (where, event);
1615 mouse_add_new_tempo_event (where.sample);
1620 if (!_dragging_playhead && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1621 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1626 case TimecodeRulerItem:
1627 case SamplesRulerItem:
1628 case MinsecRulerItem:
1639 switch (item_type) {
1642 /* check that we didn't drag before releasing, since
1643 its really annoying to create new control
1644 points when doing this.
1646 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1647 if (!were_dragging && arv) {
1648 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1649 arv->add_gain_point_event (item, event, with_guard_points);
1655 case AutomationTrackItem: {
1656 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1657 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1659 atv->add_automation_event (event, where.sample, event->button.y, with_guard_points);
1670 if (scrubbing_direction == 0) {
1671 /* no drag, just a click */
1672 switch (item_type) {
1674 play_selected_region ();
1679 } else if (_session) {
1680 /* make sure we stop */
1681 _session->request_transport_speed (0.0);
1690 /* do any (de)selection operations that should occur on button release */
1691 button_selection (item, event, item_type);
1701 switch (item_type) {
1703 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1705 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1708 /* Button2 click is unused */
1723 // x_style_paste (where, 1.0);
1744 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1747 ArdourMarker * marker;
1748 MeterMarker* m_marker = 0;
1749 TempoMarker* t_marker = 0;
1753 /* by the time we reach here, entered_regionview and entered trackview
1754 * will have already been set as appropriate. Things are done this
1755 * way because this method isn't passed a pointer to a variable type of
1756 * thing that is entered (which may or may not be canvas item).
1757 * (e.g. the actual entered regionview)
1760 choose_canvas_cursor_on_entry (item_type);
1762 switch (item_type) {
1763 case ControlPointItem:
1764 if (mouse_mode == MouseDraw || mouse_mode == MouseObject || mouse_mode == MouseContent) {
1765 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1768 fraction = 1.0 - (cp->get_y() / cp->line().height());
1770 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1771 _verbose_cursor->show ();
1776 if (mouse_mode == MouseDraw) {
1777 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1779 line->set_outline_color (UIConfiguration::instance().color ("entered gain line"));
1784 case AutomationLineItem:
1785 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1786 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1788 line->set_outline_color (UIConfiguration::instance().color ("entered automation line"));
1793 case AutomationTrackItem:
1794 AutomationTimeAxisView* atv;
1795 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1796 clear_entered_track = false;
1797 set_entered_track (atv);
1802 if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1805 entered_marker = marker;
1806 marker->set_color_rgba (UIConfiguration::instance().color ("entered marker"));
1809 case MeterMarkerItem:
1810 if ((m_marker = static_cast<MeterMarker *> (item->get_data ("marker"))) == 0) {
1813 entered_marker = m_marker;
1814 if (m_marker->meter().position_lock_style() == MusicTime) {
1815 m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker"));
1817 m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker music"));
1821 case TempoMarkerItem:
1822 if ((t_marker = static_cast<TempoMarker *> (item->get_data ("marker"))) == 0) {
1825 entered_marker = t_marker;
1826 if (t_marker->tempo().position_lock_style() == MusicTime) {
1827 t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker"));
1829 t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker music"));
1833 case FadeInHandleItem:
1834 case FadeInTrimHandleItem:
1835 if (mouse_mode == MouseObject) {
1836 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1838 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1839 rect->set_fill_color (rv->get_fill_color());
1844 case FadeOutHandleItem:
1845 case FadeOutTrimHandleItem:
1846 if (mouse_mode == MouseObject) {
1847 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1849 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1850 rect->set_fill_color (rv->get_fill_color ());
1855 case FeatureLineItem:
1857 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1858 line->set_outline_color (0xFF0000FF);
1867 if (entered_regionview) {
1868 entered_regionview->entered();
1877 /* third pass to handle entered track status in a comprehensible way.
1880 switch (item_type) {
1882 case AutomationLineItem:
1883 case ControlPointItem:
1884 /* these do not affect the current entered track state */
1885 clear_entered_track = false;
1888 case AutomationTrackItem:
1889 /* handled above already */
1901 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1904 ArdourMarker *marker;
1905 TempoMarker *t_marker;
1906 MeterMarker *m_marker;
1911 if (!_enter_stack.empty()) {
1912 _enter_stack.pop_back();
1915 switch (item_type) {
1916 case ControlPointItem:
1917 _verbose_cursor->hide ();
1921 case AutomationLineItem:
1922 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1924 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1926 line->set_outline_color (al->get_line_color());
1932 if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1936 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1937 location_flags_changed (loc);
1941 case MeterMarkerItem:
1942 if ((m_marker = static_cast<MeterMarker *> (item->get_data ("marker"))) == 0) {
1946 if (m_marker->meter().position_lock_style() == MusicTime) {
1947 m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker music"));
1949 m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker"));
1953 case TempoMarkerItem:
1954 if ((t_marker = static_cast<TempoMarker *> (item->get_data ("marker"))) == 0) {
1958 if (t_marker->tempo().position_lock_style() == MusicTime) {
1959 t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker music"));
1961 t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker"));
1965 case FadeInTrimHandleItem:
1966 case FadeOutTrimHandleItem:
1967 case FadeInHandleItem:
1968 case FadeOutHandleItem:
1970 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1972 rect->set_fill_color (UIConfiguration::instance().color ("inactive fade handle"));
1977 case AutomationTrackItem:
1980 case FeatureLineItem:
1982 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1983 line->set_outline_color (UIConfiguration::instance().color ("zero line"));
1995 Editor::scrub (samplepos_t sample, double current_x)
1999 if (scrubbing_direction == 0) {
2001 _session->request_locate (sample, false);
2002 _session->request_transport_speed (0.1);
2003 scrubbing_direction = 1;
2007 if (last_scrub_x > current_x) {
2009 /* pointer moved to the left */
2011 if (scrubbing_direction > 0) {
2013 /* we reversed direction to go backwards */
2016 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2020 /* still moving to the left (backwards) */
2022 scrub_reversals = 0;
2023 scrub_reverse_distance = 0;
2025 delta = 0.01 * (last_scrub_x - current_x);
2026 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2030 /* pointer moved to the right */
2032 if (scrubbing_direction < 0) {
2033 /* we reversed direction to go forward */
2036 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2039 /* still moving to the right */
2041 scrub_reversals = 0;
2042 scrub_reverse_distance = 0;
2044 delta = 0.01 * (current_x - last_scrub_x);
2045 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2049 /* if there have been more than 2 opposite motion moves detected, or one that moves
2050 back more than 10 pixels, reverse direction
2053 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2055 if (scrubbing_direction > 0) {
2056 /* was forwards, go backwards */
2057 _session->request_transport_speed (-0.1);
2058 scrubbing_direction = -1;
2060 /* was backwards, go forwards */
2061 _session->request_transport_speed (0.1);
2062 scrubbing_direction = 1;
2065 scrub_reverse_distance = 0;
2066 scrub_reversals = 0;
2070 last_scrub_x = current_x;
2074 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2076 _last_motion_y = event->motion.y;
2078 if (event->motion.is_hint) {
2081 /* We call this so that MOTION_NOTIFY events continue to be
2082 delivered to the canvas. We need to do this because we set
2083 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2084 the density of the events, at the expense of a round-trip
2085 to the server. Given that this will mostly occur on cases
2086 where DISPLAY = :0.0, and given the cost of what the motion
2087 event might do, its a good tradeoff.
2090 _track_canvas->get_pointer (x, y);
2093 if (current_stepping_trackview) {
2094 /* don't keep the persistent stepped trackview if the mouse moves */
2095 current_stepping_trackview = 0;
2096 step_timeout.disconnect ();
2099 if (_session && _session->actively_recording()) {
2100 /* Sorry. no dragging stuff around while we record */
2104 update_join_object_range_location (event->motion.y);
2106 /* snapped_cursor stuff (the snapped_cursor shows where an operation is going to occur) */
2108 MusicSample where (0, 0);
2109 if (mouse_sample (where.sample, ignored)) {
2110 snap_to_with_modifier (where, event);
2111 set_snapped_cursor_position (where.sample);
2114 /* drags might also change the snapped_cursor location, because we are snapping the thing being dragged, not the actual mouse cursor */
2115 if (_drags->active ()) {
2116 return _drags->motion_handler (event, from_autoscroll);
2123 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2125 ControlPoint* control_point;
2127 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2128 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2129 abort(); /*NOTREACHED*/
2132 AutomationLine& line = control_point->line ();
2133 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2134 /* we shouldn't remove the first or last gain point in region gain lines */
2135 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2144 Editor::remove_control_point (ArdourCanvas::Item* item)
2146 if (!can_remove_control_point (item)) {
2150 ControlPoint* control_point;
2152 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2153 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2154 abort(); /*NOTREACHED*/
2157 control_point->line().remove_point (*control_point);
2161 Editor::edit_control_point (ArdourCanvas::Item* item)
2163 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2166 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2167 abort(); /*NOTREACHED*/
2170 ControlPointDialog d (p);
2172 if (d.run () != RESPONSE_ACCEPT) {
2176 p->line().modify_point_y (*p, d.get_y_fraction ());
2180 Editor::edit_notes (MidiRegionView* mrv)
2182 MidiRegionView::Selection const & s = mrv->selection();
2188 EditNoteDialog* d = new EditNoteDialog (mrv, s);
2191 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2195 Editor::note_edit_done (int r, EditNoteDialog* d)
2202 Editor::edit_region (RegionView* rv)
2204 if (UIConfiguration::instance().get_use_double_click_to_zoom_to_selection()) {
2205 temporal_zoom_selection (Both);
2207 rv->show_region_editor ();
2212 Editor::visible_order_range (int* low, int* high) const
2214 *low = TimeAxisView::max_order ();
2217 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2219 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2221 if (rtv && !rtv->hidden()) {
2223 if (*high < rtv->order()) {
2224 *high = rtv->order ();
2227 if (*low > rtv->order()) {
2228 *low = rtv->order ();
2235 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2237 /* Either add to or set the set the region selection, unless
2238 this is an alignment click (control used)
2241 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2242 TimeAxisView* tv = &rv.get_time_axis_view();
2244 samplepos_t where = get_preferred_edit_position();
2248 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2250 align_region (rv.region(), SyncPoint, where);
2252 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2254 align_region (rv.region(), End, where);
2258 align_region (rv.region(), Start, where);
2265 Editor::collect_new_region_view (RegionView* rv)
2267 latest_regionviews.push_back (rv);
2271 Editor::collect_and_select_new_region_view (RegionView* rv)
2274 latest_regionviews.push_back (rv);
2278 Editor::cancel_selection ()
2280 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2281 (*i)->hide_selection ();
2284 selection->clear ();
2285 clicked_selection = 0;
2289 Editor::cancel_time_selection ()
2291 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2292 (*i)->hide_selection ();
2294 selection->time.clear ();
2295 clicked_selection = 0;
2299 Editor::point_trim (GdkEvent* event, samplepos_t new_bound)
2301 RegionView* rv = clicked_regionview;
2303 /* Choose action dependant on which button was pressed */
2304 switch (event->button.button) {
2306 begin_reversible_command (_("start point trim"));
2308 if (selection->selected (rv)) {
2309 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2310 i != selection->regions.by_layer().end(); ++i)
2312 if (!(*i)->region()->locked()) {
2313 (*i)->region()->clear_changes ();
2314 (*i)->region()->trim_front (new_bound);
2315 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2320 if (!rv->region()->locked()) {
2321 rv->region()->clear_changes ();
2322 rv->region()->trim_front (new_bound);
2323 _session->add_command(new StatefulDiffCommand (rv->region()));
2327 commit_reversible_command();
2331 begin_reversible_command (_("end point trim"));
2333 if (selection->selected (rv)) {
2335 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2337 if (!(*i)->region()->locked()) {
2338 (*i)->region()->clear_changes();
2339 (*i)->region()->trim_end (new_bound);
2340 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2346 if (!rv->region()->locked()) {
2347 rv->region()->clear_changes ();
2348 rv->region()->trim_end (new_bound);
2349 _session->add_command (new StatefulDiffCommand (rv->region()));
2353 commit_reversible_command();
2362 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2364 ArdourMarker* marker;
2367 if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
2368 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2369 abort(); /*NOTREACHED*/
2372 Location* location = find_location_from_marker (marker, is_start);
2373 location->set_hidden (true, this);
2377 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2379 using namespace Gtkmm2ext;
2381 ArdourWidgets::Prompter prompter (false);
2383 prompter.set_prompt (_("Name for region:"));
2384 prompter.set_initial_text (clicked_regionview->region()->name());
2385 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2386 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2387 prompter.show_all ();
2388 switch (prompter.run ()) {
2389 case Gtk::RESPONSE_ACCEPT:
2391 prompter.get_result(str);
2393 clicked_regionview->region()->set_name (str);
2402 Editor::mouse_brush_insert_region (RegionView* rv, samplepos_t pos)
2404 /* no brushing without a useful quantize setting */
2405 if (_grid_type == GridTypeNone)
2408 /* don't brush a copy over the original */
2410 if (pos == rv->region()->position()) {
2414 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2416 if (!rtv || !rtv->is_track()) {
2420 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2422 playlist->clear_changes ();
2423 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2424 playlist->add_region (new_region, pos);
2425 _session->add_command (new StatefulDiffCommand (playlist));
2427 /* playlist is frozen, so we have to update manually XXX this is disgusting */
2429 //playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2433 Editor::track_height_step_timeout ()
2435 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2436 current_stepping_trackview = 0;
2443 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2445 assert (region_view);
2447 if (!region_view->region()->playlist()) {
2451 switch (Config->get_edit_mode()) {
2453 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2456 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2459 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2466 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2468 assert (region_view);
2470 if (!region_view->region()->playlist()) {
2474 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2478 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2480 assert (region_view);
2482 if (!region_view->region()->playlist()) {
2486 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2490 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2493 /** Start a grab where a time range is selected, track(s) are selected, and the
2494 * user clicks and drags a region with a modifier in order to create a new region containing
2495 * the section of the clicked region that lies within the time range.
2498 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2500 if (clicked_regionview == 0) {
2504 /* lets try to create new Region for the selection */
2506 vector<boost::shared_ptr<Region> > new_regions;
2507 create_region_from_selection (new_regions);
2509 if (new_regions.empty()) {
2513 /* XXX fix me one day to use all new regions */
2515 boost::shared_ptr<Region> region (new_regions.front());
2517 /* add it to the current stream/playlist.
2519 tricky: the streamview for the track will add a new regionview. we will
2520 catch the signal it sends when it creates the regionview to
2521 set the regionview we want to then drag.
2524 latest_regionviews.clear();
2525 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2527 /* A selection grab currently creates two undo/redo operations, one for
2528 creating the new region and another for moving it.
2530 begin_reversible_command (Operations::selection_grab);
2532 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2534 playlist->clear_changes ();
2535 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2536 _session->add_command(new StatefulDiffCommand (playlist));
2540 if (latest_regionviews.empty()) {
2541 /* something went wrong */
2542 abort_reversible_command ();
2546 /* we need to deselect all other regionviews, and select this one
2547 i'm ignoring undo stuff, because the region creation will take care of it
2550 selection->set (latest_regionviews);
2552 commit_reversible_command ();
2554 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2560 if (_drags->active ()) {
2563 selection->clear ();
2565 /* if session is playing a range, cancel that */
2566 if (_session->get_play_range()) {
2567 _session->request_cancel_play_range();
2570 if (_session->solo_selection_active()) {
2572 _session->solo_selection (sl, false);
2576 ARDOUR_UI::instance()->reset_focus (&contents());
2579 /** Update _join_object_range_state which indicate whether we are over the top
2580 * or bottom half of a route view, used by the `join object/range' tool
2581 * mode. Coordinates in canvas space.
2584 Editor::update_join_object_range_location (double y)
2586 if (!get_smart_mode()) {
2587 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2591 JoinObjectRangeState const old = _join_object_range_state;
2593 if (mouse_mode == MouseObject) {
2594 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2595 } else if (mouse_mode == MouseRange) {
2596 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2599 if (entered_regionview) {
2601 /* TODO: there is currently a bug here(?)
2602 * when we are inside a region fade handle, it acts as though we are in range mode because it is in the top half of the region
2603 * can it be fixed here?
2606 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2607 double const c = item_space.y / entered_regionview->height();
2609 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2611 Editor::EnterContext* ctx = get_enter_context(RegionItem);
2612 if (_join_object_range_state != old && ctx) {
2613 ctx->cursor_ctx->change(which_track_cursor());
2616 } else if (entered_track) {
2618 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2620 if (entered_route_view) {
2625 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2627 double track_height = entered_route_view->view()->child_height();
2628 if (UIConfiguration::instance().get_show_name_highlight()) {
2629 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2631 double const c = cy / track_height;
2635 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2637 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2641 /* Other kinds of tracks use object mode */
2642 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2645 Editor::EnterContext* ctx = get_enter_context(StreamItem);
2646 if (_join_object_range_state != old && ctx) {
2647 ctx->cursor_ctx->change(which_track_cursor());
2653 Editor::effective_mouse_mode () const
2655 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2657 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2665 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2667 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2670 e->region_view().delete_note (e->note ());
2673 /** Obtain the pointer position in canvas coordinates */
2675 Editor::get_pointer_position (double& x, double& y) const
2678 _track_canvas->get_pointer (px, py);
2679 _track_canvas->window_to_canvas (px, py, x, y);