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. for not this is not fully implemented */
981 // if (!clicked_regionview->region()->locked()) {
982 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
987 case FeatureLineItem:
989 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
990 remove_transient(item);
994 _drags->set (new FeatureLineDrag (this, item), event);
1000 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1001 /* click on an automation region view; do nothing here and let the ARV's signal handler
1007 /* click on a normal region view */
1008 if (ArdourKeyboard::indicates_copy (event->button.state)) {
1009 add_region_copy_drag (item, event, clicked_regionview);
1010 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
1011 add_region_brush_drag (item, event, clicked_regionview);
1013 add_region_drag (item, event, clicked_regionview);
1017 _drags->start_grab (event);
1021 case RegionViewNameHighlight:
1022 case LeftFrameHandle:
1023 case RightFrameHandle:
1024 if (!clicked_regionview->region()->locked()) {
1025 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1030 case FadeInTrimHandleItem:
1031 case FadeOutTrimHandleItem:
1032 if (!clicked_regionview->region()->locked()) {
1033 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
1038 case RegionViewName:
1040 /* rename happens on edit clicks */
1041 if (clicked_regionview->get_name_highlight()) {
1042 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1048 case ControlPointItem:
1049 _drags->set (new ControlPointDrag (this, item), event);
1053 case AutomationLineItem:
1054 _drags->set (new LineDrag (this, item), event);
1059 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1062 case AutomationTrackItem:
1064 TimeAxisView* parent = clicked_axisview->get_parent ();
1065 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1067 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1069 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1071 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1072 if (pl->n_regions() == 0) {
1073 /* Parent has no regions; create one so that we have somewhere to put automation */
1074 _drags->set (new RegionCreateDrag (this, item, parent), event);
1076 /* See if there's a region before the click that we can extend, and extend it if so */
1077 samplepos_t const t = canvas_event_sample (event);
1078 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1080 _drags->set (new RegionCreateDrag (this, item, parent), event);
1082 prev->set_length (t - prev->position (), get_grid_music_divisions (event->button.state));
1086 /* rubberband drag to select automation points */
1087 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1109 switch (item_type) {
1111 _drags->set (new LineDrag (this, item), event);
1114 case ControlPointItem:
1115 _drags->set (new ControlPointDrag (this, item), event);
1121 if (dynamic_cast<AudioRegionView*>(clicked_regionview) ||
1122 dynamic_cast<AutomationRegionView*>(clicked_regionview)) {
1123 _drags->set (new AutomationRangeDrag (this, clicked_regionview, selection->time),
1124 event, _cursors->up_down);
1126 double const y = event->button.y;
1127 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y, false);
1129 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1131 /* smart "join" mode: drag automation */
1132 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1140 case AutomationLineItem:
1141 _drags->set (new LineDrag (this, item), event);
1145 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
1146 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
1147 /* Note is big and pointer is near the end, trim */
1148 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
1151 _drags->set (new NoteDrag (this, item), event);
1158 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1159 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1170 if (item_type == NoteItem) {
1171 /* resize-drag notes */
1172 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
1173 if (note->big_enough_to_trim()) {
1174 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
1178 } else if (clicked_regionview) {
1180 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1186 _drags->set (new ScrubDrag (this, item), event, _cursors->transparent);
1187 scrub_reversals = 0;
1188 scrub_reverse_distance = 0;
1189 last_scrub_x = event->button.x;
1190 scrubbing_direction = 0;
1202 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1204 Editing::MouseMode const eff = effective_mouse_mode ();
1207 switch (item_type) {
1209 if (ArdourKeyboard::indicates_copy (event->button.state)) {
1210 add_region_copy_drag (item, event, clicked_regionview);
1212 add_region_drag (item, event, clicked_regionview);
1214 _drags->start_grab (event);
1217 case ControlPointItem:
1218 _drags->set (new ControlPointDrag (this, item), event);
1226 switch (item_type) {
1227 case RegionViewNameHighlight:
1228 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1232 case LeftFrameHandle:
1233 case RightFrameHandle:
1234 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1238 case RegionViewName:
1239 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1253 /* relax till release */
1265 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1267 if (event->type == GDK_2BUTTON_PRESS) {
1268 _drags->mark_double_click ();
1269 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1273 if (event->type != GDK_BUTTON_PRESS) {
1277 _track_canvas->grab_focus();
1279 if (_session && _session->actively_recording()) {
1283 button_selection (item, event, item_type);
1285 if (!_drags->active () &&
1286 (Keyboard::is_delete_event (&event->button) ||
1287 Keyboard::is_context_menu_event (&event->button) ||
1288 Keyboard::is_edit_event (&event->button))) {
1290 /* handled by button release */
1294 /* not rolling, effectively in range mode, follow edits enabled (likely
1295 * to start range drag), not in a fade handle (since that means we are
1296 * not starting a range drag): locate the PH here
1299 if ((item_type != FadeInHandleItem) &&
1300 (item_type != FadeOutHandleItem) &&
1301 !_drags->active () &&
1303 !_session->transport_rolling() &&
1304 (effective_mouse_mode() == MouseRange) &&
1305 UIConfiguration::instance().get_follow_edits() &&
1306 !_session->config.get_external_sync()) {
1308 MusicSample where (canvas_event_sample (event), 0);
1310 _session->request_locate (where.sample, false);
1313 switch (event->button.button) {
1315 return button_press_handler_1 (item, event, item_type);
1319 return button_press_handler_2 (item, event, item_type);
1326 return button_press_dispatch (&event->button);
1335 Editor::button_press_dispatch (GdkEventButton* ev)
1337 /* this function is intended only for buttons 4 and above.
1340 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1341 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1345 Editor::button_release_dispatch (GdkEventButton* ev)
1347 /* this function is intended only for buttons 4 and above.
1350 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1351 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1355 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1357 MusicSample where (canvas_event_sample (event), 0);
1358 AutomationTimeAxisView* atv = 0;
1360 _press_cursor_ctx.reset();
1362 /* no action if we're recording */
1364 if (_session && _session->actively_recording()) {
1368 bool were_dragging = false;
1370 if (!Keyboard::is_context_menu_event (&event->button)) {
1372 /* see if we're finishing a drag */
1374 if (_drags->active ()) {
1375 bool const r = _drags->end_grab (event);
1377 /* grab dragged, so do nothing else */
1381 were_dragging = true;
1384 update_region_layering_order_editor ();
1387 /* edit events get handled here */
1389 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1390 switch (item_type) {
1392 show_region_properties ();
1394 case TempoMarkerItem: {
1395 ArdourMarker* marker;
1396 TempoMarker* tempo_marker;
1398 if ((marker = reinterpret_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1399 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1400 abort(); /*NOTREACHED*/
1403 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1404 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1405 abort(); /*NOTREACHED*/
1408 edit_tempo_marker (*tempo_marker);
1412 case MeterMarkerItem: {
1413 ArdourMarker* marker;
1414 MeterMarker* meter_marker;
1416 if ((marker = reinterpret_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1417 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1418 abort(); /*NOTREACHED*/
1421 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1422 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1423 abort(); /*NOTREACHED*/
1425 edit_meter_marker (*meter_marker);
1429 case RegionViewName:
1430 if (clicked_regionview->name_active()) {
1431 return mouse_rename_region (item, event);
1435 case ControlPointItem:
1436 edit_control_point (item);
1445 /* context menu events get handled here */
1446 if (Keyboard::is_context_menu_event (&event->button)) {
1448 context_click_event = *event;
1450 if (!_drags->active ()) {
1452 /* no matter which button pops up the context menu, tell the menu
1453 widget to use button 1 to drive menu selection.
1456 switch (item_type) {
1458 case FadeInHandleItem:
1459 case FadeInTrimHandleItem:
1460 case StartCrossFadeItem:
1461 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1465 case FadeOutHandleItem:
1466 case FadeOutTrimHandleItem:
1467 case EndCrossFadeItem:
1468 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1471 case LeftFrameHandle:
1472 case RightFrameHandle:
1476 popup_track_context_menu (1, event->button.time, item_type, false);
1480 case RegionViewNameHighlight:
1481 case RegionViewName:
1482 popup_track_context_menu (1, event->button.time, item_type, false);
1486 popup_track_context_menu (1, event->button.time, item_type, true);
1489 case AutomationTrackItem:
1490 popup_track_context_menu (1, event->button.time, item_type, false);
1494 case RangeMarkerBarItem:
1495 case TransportMarkerBarItem:
1496 case CdMarkerBarItem:
1498 case TempoCurveItem:
1501 case TimecodeRulerItem:
1502 case SamplesRulerItem:
1503 case MinsecRulerItem:
1505 popup_ruler_menu (where.sample, item_type);
1509 marker_context_menu (&event->button, item);
1512 case TempoMarkerItem:
1513 tempo_or_meter_marker_context_menu (&event->button, item);
1516 case MeterMarkerItem:
1517 tempo_or_meter_marker_context_menu (&event->button, item);
1520 case CrossfadeViewItem:
1521 popup_track_context_menu (1, event->button.time, item_type, false);
1524 case ControlPointItem:
1525 popup_control_point_context_menu (item, event);
1529 if (internal_editing()) {
1530 popup_note_context_menu (item, event);
1542 /* delete events get handled here */
1544 Editing::MouseMode const eff = effective_mouse_mode ();
1546 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1548 switch (item_type) {
1549 case TempoMarkerItem:
1550 remove_tempo_marker (item);
1553 case MeterMarkerItem:
1554 remove_meter_marker (item);
1558 remove_marker (*item, event);
1562 if (eff == MouseObject) {
1563 remove_clicked_region ();
1567 case ControlPointItem:
1568 remove_control_point (item);
1572 remove_midi_note (item, event);
1581 switch (event->button.button) {
1584 switch (item_type) {
1585 /* see comments in button_press_handler */
1586 case PlayheadCursorItem:
1589 case AutomationLineItem:
1590 case StartSelectionTrimItem:
1591 case EndSelectionTrimItem:
1595 if (!_dragging_playhead) {
1596 snap_to_with_modifier (where, event, RoundNearest, SnapToAny, true);
1597 mouse_add_new_marker (where.sample);
1601 case CdMarkerBarItem:
1602 if (!_dragging_playhead) {
1603 // if we get here then a dragged range wasn't done
1604 snap_to_with_modifier (where, event, RoundNearest, SnapToAny, true);
1605 mouse_add_new_marker (where.sample, true);
1609 case TempoCurveItem:
1610 if (!_dragging_playhead && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1611 snap_to_with_modifier (where, event);
1612 mouse_add_new_tempo_event (where.sample);
1617 if (!_dragging_playhead && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1618 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1623 case TimecodeRulerItem:
1624 case SamplesRulerItem:
1625 case MinsecRulerItem:
1636 switch (item_type) {
1639 /* check that we didn't drag before releasing, since
1640 its really annoying to create new control
1641 points when doing this.
1643 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1644 if (!were_dragging && arv) {
1645 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1646 arv->add_gain_point_event (item, event, with_guard_points);
1652 case AutomationTrackItem: {
1653 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1654 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1656 atv->add_automation_event (event, where.sample, event->button.y, with_guard_points);
1667 if (scrubbing_direction == 0) {
1668 /* no drag, just a click */
1669 switch (item_type) {
1671 play_selected_region ();
1676 } else if (_session) {
1677 /* make sure we stop */
1678 _session->request_transport_speed (0.0);
1687 /* do any (de)selection operations that should occur on button release */
1688 button_selection (item, event, item_type);
1698 switch (item_type) {
1700 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1702 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1705 // Button2 click is unused
1720 // x_style_paste (where, 1.0);
1741 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1744 ArdourMarker * marker;
1745 MeterMarker* m_marker = 0;
1746 TempoMarker* t_marker = 0;
1750 /* by the time we reach here, entered_regionview and entered trackview
1751 * will have already been set as appropriate. Things are done this
1752 * way because this method isn't passed a pointer to a variable type of
1753 * thing that is entered (which may or may not be canvas item).
1754 * (e.g. the actual entered regionview)
1757 choose_canvas_cursor_on_entry (item_type);
1759 switch (item_type) {
1760 case ControlPointItem:
1761 if (mouse_mode == MouseDraw || mouse_mode == MouseObject || mouse_mode == MouseContent) {
1762 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1765 fraction = 1.0 - (cp->get_y() / cp->line().height());
1767 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1768 _verbose_cursor->show ();
1773 if (mouse_mode == MouseDraw) {
1774 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1776 line->set_outline_color (UIConfiguration::instance().color ("entered gain line"));
1781 case AutomationLineItem:
1782 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1783 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1785 line->set_outline_color (UIConfiguration::instance().color ("entered automation line"));
1790 case AutomationTrackItem:
1791 AutomationTimeAxisView* atv;
1792 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1793 clear_entered_track = false;
1794 set_entered_track (atv);
1799 if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1802 entered_marker = marker;
1803 marker->set_color_rgba (UIConfiguration::instance().color ("entered marker"));
1806 case MeterMarkerItem:
1807 if ((m_marker = static_cast<MeterMarker *> (item->get_data ("marker"))) == 0) {
1810 entered_marker = m_marker;
1811 if (m_marker->meter().position_lock_style() == MusicTime) {
1812 m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker"));
1814 m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker music"));
1818 case TempoMarkerItem:
1819 if ((t_marker = static_cast<TempoMarker *> (item->get_data ("marker"))) == 0) {
1822 entered_marker = t_marker;
1823 if (t_marker->tempo().position_lock_style() == MusicTime) {
1824 t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker"));
1826 t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker music"));
1830 case FadeInHandleItem:
1831 case FadeInTrimHandleItem:
1832 if (mouse_mode == MouseObject) {
1833 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1835 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1836 rect->set_fill_color (rv->get_fill_color());
1841 case FadeOutHandleItem:
1842 case FadeOutTrimHandleItem:
1843 if (mouse_mode == MouseObject) {
1844 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1846 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1847 rect->set_fill_color (rv->get_fill_color ());
1852 case FeatureLineItem:
1854 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1855 line->set_outline_color (0xFF0000FF);
1864 if (entered_regionview) {
1865 entered_regionview->entered();
1874 /* third pass to handle entered track status in a comprehensible way.
1877 switch (item_type) {
1879 case AutomationLineItem:
1880 case ControlPointItem:
1881 /* these do not affect the current entered track state */
1882 clear_entered_track = false;
1885 case AutomationTrackItem:
1886 /* handled above already */
1898 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1901 ArdourMarker *marker;
1902 TempoMarker *t_marker;
1903 MeterMarker *m_marker;
1908 if (!_enter_stack.empty()) {
1909 _enter_stack.pop_back();
1912 switch (item_type) {
1913 case ControlPointItem:
1914 _verbose_cursor->hide ();
1918 case AutomationLineItem:
1919 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1921 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1923 line->set_outline_color (al->get_line_color());
1929 if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1933 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1934 location_flags_changed (loc);
1938 case MeterMarkerItem:
1939 if ((m_marker = static_cast<MeterMarker *> (item->get_data ("marker"))) == 0) {
1943 if (m_marker->meter().position_lock_style() == MusicTime) {
1944 m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker music"));
1946 m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker"));
1950 case TempoMarkerItem:
1951 if ((t_marker = static_cast<TempoMarker *> (item->get_data ("marker"))) == 0) {
1955 if (t_marker->tempo().position_lock_style() == MusicTime) {
1956 t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker music"));
1958 t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker"));
1962 case FadeInTrimHandleItem:
1963 case FadeOutTrimHandleItem:
1964 case FadeInHandleItem:
1965 case FadeOutHandleItem:
1967 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1969 rect->set_fill_color (UIConfiguration::instance().color ("inactive fade handle"));
1974 case AutomationTrackItem:
1977 case FeatureLineItem:
1979 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1980 line->set_outline_color (UIConfiguration::instance().color ("zero line"));
1992 Editor::scrub (samplepos_t sample, double current_x)
1996 if (scrubbing_direction == 0) {
1998 _session->request_locate (sample, false);
1999 _session->request_transport_speed (0.1);
2000 scrubbing_direction = 1;
2004 if (last_scrub_x > current_x) {
2006 /* pointer moved to the left */
2008 if (scrubbing_direction > 0) {
2010 /* we reversed direction to go backwards */
2013 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2017 /* still moving to the left (backwards) */
2019 scrub_reversals = 0;
2020 scrub_reverse_distance = 0;
2022 delta = 0.01 * (last_scrub_x - current_x);
2023 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2027 /* pointer moved to the right */
2029 if (scrubbing_direction < 0) {
2030 /* we reversed direction to go forward */
2033 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2036 /* still moving to the right */
2038 scrub_reversals = 0;
2039 scrub_reverse_distance = 0;
2041 delta = 0.01 * (current_x - last_scrub_x);
2042 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2046 /* if there have been more than 2 opposite motion moves detected, or one that moves
2047 back more than 10 pixels, reverse direction
2050 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2052 if (scrubbing_direction > 0) {
2053 /* was forwards, go backwards */
2054 _session->request_transport_speed (-0.1);
2055 scrubbing_direction = -1;
2057 /* was backwards, go forwards */
2058 _session->request_transport_speed (0.1);
2059 scrubbing_direction = 1;
2062 scrub_reverse_distance = 0;
2063 scrub_reversals = 0;
2067 last_scrub_x = current_x;
2071 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2073 _last_motion_y = event->motion.y;
2075 if (event->motion.is_hint) {
2078 /* We call this so that MOTION_NOTIFY events continue to be
2079 delivered to the canvas. We need to do this because we set
2080 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2081 the density of the events, at the expense of a round-trip
2082 to the server. Given that this will mostly occur on cases
2083 where DISPLAY = :0.0, and given the cost of what the motion
2084 event might do, its a good tradeoff.
2087 _track_canvas->get_pointer (x, y);
2090 if (current_stepping_trackview) {
2091 /* don't keep the persistent stepped trackview if the mouse moves */
2092 current_stepping_trackview = 0;
2093 step_timeout.disconnect ();
2096 if (_session && _session->actively_recording()) {
2097 /* Sorry. no dragging stuff around while we record */
2101 update_join_object_range_location (event->motion.y);
2103 //snapped_cursor stuff ( the snapped_cursor shows where an operation is going to occur )
2105 MusicSample where (0, 0);
2106 if (mouse_sample (where.sample, ignored)) {
2107 snap_to_with_modifier (where, event);
2108 set_snapped_cursor_position (where.sample);
2111 //drags might also change the snapped_cursor location, because we are snapping the thing being dragged, not the actual mouse cursor
2112 if (_drags->active ()) {
2113 return _drags->motion_handler (event, from_autoscroll);
2120 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2122 ControlPoint* control_point;
2124 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2125 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2126 abort(); /*NOTREACHED*/
2129 AutomationLine& line = control_point->line ();
2130 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2131 /* we shouldn't remove the first or last gain point in region gain lines */
2132 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2141 Editor::remove_control_point (ArdourCanvas::Item* item)
2143 if (!can_remove_control_point (item)) {
2147 ControlPoint* control_point;
2149 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2150 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2151 abort(); /*NOTREACHED*/
2154 control_point->line().remove_point (*control_point);
2158 Editor::edit_control_point (ArdourCanvas::Item* item)
2160 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2163 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2164 abort(); /*NOTREACHED*/
2167 ControlPointDialog d (p);
2169 if (d.run () != RESPONSE_ACCEPT) {
2173 p->line().modify_point_y (*p, d.get_y_fraction ());
2177 Editor::edit_notes (MidiRegionView* mrv)
2179 MidiRegionView::Selection const & s = mrv->selection();
2185 EditNoteDialog* d = new EditNoteDialog (mrv, s);
2188 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2192 Editor::note_edit_done (int r, EditNoteDialog* d)
2199 Editor::edit_region (RegionView* rv)
2201 if (UIConfiguration::instance().get_use_double_click_to_zoom_to_selection()) {
2202 temporal_zoom_selection (Both);
2204 rv->show_region_editor ();
2209 Editor::visible_order_range (int* low, int* high) const
2211 *low = TimeAxisView::max_order ();
2214 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2216 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2218 if (rtv && !rtv->hidden()) {
2220 if (*high < rtv->order()) {
2221 *high = rtv->order ();
2224 if (*low > rtv->order()) {
2225 *low = rtv->order ();
2232 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2234 /* Either add to or set the set the region selection, unless
2235 this is an alignment click (control used)
2238 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2239 TimeAxisView* tv = &rv.get_time_axis_view();
2241 samplepos_t where = get_preferred_edit_position();
2245 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2247 align_region (rv.region(), SyncPoint, where);
2249 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2251 align_region (rv.region(), End, where);
2255 align_region (rv.region(), Start, where);
2262 Editor::collect_new_region_view (RegionView* rv)
2264 latest_regionviews.push_back (rv);
2268 Editor::collect_and_select_new_region_view (RegionView* rv)
2271 latest_regionviews.push_back (rv);
2275 Editor::cancel_selection ()
2277 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2278 (*i)->hide_selection ();
2281 selection->clear ();
2282 clicked_selection = 0;
2286 Editor::cancel_time_selection ()
2288 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2289 (*i)->hide_selection ();
2291 selection->time.clear ();
2292 clicked_selection = 0;
2296 Editor::point_trim (GdkEvent* event, samplepos_t new_bound)
2298 RegionView* rv = clicked_regionview;
2300 /* Choose action dependant on which button was pressed */
2301 switch (event->button.button) {
2303 begin_reversible_command (_("start point trim"));
2305 if (selection->selected (rv)) {
2306 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2307 i != selection->regions.by_layer().end(); ++i)
2309 if (!(*i)->region()->locked()) {
2310 (*i)->region()->clear_changes ();
2311 (*i)->region()->trim_front (new_bound);
2312 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2317 if (!rv->region()->locked()) {
2318 rv->region()->clear_changes ();
2319 rv->region()->trim_front (new_bound);
2320 _session->add_command(new StatefulDiffCommand (rv->region()));
2324 commit_reversible_command();
2328 begin_reversible_command (_("end point trim"));
2330 if (selection->selected (rv)) {
2332 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2334 if (!(*i)->region()->locked()) {
2335 (*i)->region()->clear_changes();
2336 (*i)->region()->trim_end (new_bound);
2337 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2343 if (!rv->region()->locked()) {
2344 rv->region()->clear_changes ();
2345 rv->region()->trim_end (new_bound);
2346 _session->add_command (new StatefulDiffCommand (rv->region()));
2350 commit_reversible_command();
2359 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2361 ArdourMarker* marker;
2364 if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
2365 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2366 abort(); /*NOTREACHED*/
2369 Location* location = find_location_from_marker (marker, is_start);
2370 location->set_hidden (true, this);
2374 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2376 using namespace Gtkmm2ext;
2378 ArdourWidgets::Prompter prompter (false);
2380 prompter.set_prompt (_("Name for region:"));
2381 prompter.set_initial_text (clicked_regionview->region()->name());
2382 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2383 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2384 prompter.show_all ();
2385 switch (prompter.run ()) {
2386 case Gtk::RESPONSE_ACCEPT:
2388 prompter.get_result(str);
2390 clicked_regionview->region()->set_name (str);
2399 Editor::mouse_brush_insert_region (RegionView* rv, samplepos_t pos)
2401 /* no brushing without a useful quantize setting */
2402 if (_grid_type == GridTypeNone)
2405 /* don't brush a copy over the original */
2407 if (pos == rv->region()->position()) {
2411 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2413 if (!rtv || !rtv->is_track()) {
2417 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2419 playlist->clear_changes ();
2420 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2421 playlist->add_region (new_region, pos);
2422 _session->add_command (new StatefulDiffCommand (playlist));
2424 // playlist is frozen, so we have to update manually XXX this is disgusting
2426 //playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2430 Editor::track_height_step_timeout ()
2432 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2433 current_stepping_trackview = 0;
2440 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2442 assert (region_view);
2444 if (!region_view->region()->playlist()) {
2448 switch (Config->get_edit_mode()) {
2450 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2453 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2456 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2463 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2465 assert (region_view);
2467 if (!region_view->region()->playlist()) {
2471 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2475 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2477 assert (region_view);
2479 if (!region_view->region()->playlist()) {
2483 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2487 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2490 /** Start a grab where a time range is selected, track(s) are selected, and the
2491 * user clicks and drags a region with a modifier in order to create a new region containing
2492 * the section of the clicked region that lies within the time range.
2495 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2497 if (clicked_regionview == 0) {
2501 /* lets try to create new Region for the selection */
2503 vector<boost::shared_ptr<Region> > new_regions;
2504 create_region_from_selection (new_regions);
2506 if (new_regions.empty()) {
2510 /* XXX fix me one day to use all new regions */
2512 boost::shared_ptr<Region> region (new_regions.front());
2514 /* add it to the current stream/playlist.
2516 tricky: the streamview for the track will add a new regionview. we will
2517 catch the signal it sends when it creates the regionview to
2518 set the regionview we want to then drag.
2521 latest_regionviews.clear();
2522 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2524 /* A selection grab currently creates two undo/redo operations, one for
2525 creating the new region and another for moving it.
2527 begin_reversible_command (Operations::selection_grab);
2529 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2531 playlist->clear_changes ();
2532 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2533 _session->add_command(new StatefulDiffCommand (playlist));
2537 if (latest_regionviews.empty()) {
2538 /* something went wrong */
2539 abort_reversible_command ();
2543 /* we need to deselect all other regionviews, and select this one
2544 i'm ignoring undo stuff, because the region creation will take care of it
2547 selection->set (latest_regionviews);
2549 commit_reversible_command ();
2551 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2557 if (_drags->active ()) {
2560 selection->clear ();
2562 //if session is playing a range, cancel that
2563 if (_session->get_play_range()) {
2564 _session->request_cancel_play_range();
2567 if ( _session->solo_selection_active() ) {
2569 _session->solo_selection( sl, false );
2574 ARDOUR_UI::instance()->reset_focus (&contents());
2577 /** Update _join_object_range_state which indicate whether we are over the top
2578 * or bottom half of a route view, used by the `join object/range' tool
2579 * mode. Coordinates in canvas space.
2582 Editor::update_join_object_range_location (double y)
2584 if (!get_smart_mode()) {
2585 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2589 JoinObjectRangeState const old = _join_object_range_state;
2591 if (mouse_mode == MouseObject) {
2592 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2593 } else if (mouse_mode == MouseRange) {
2594 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2597 if (entered_regionview) {
2599 //ToDo: there is currently a bug here(?)
2600 //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
2601 //can it be fixed here?
2603 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2604 double const c = item_space.y / entered_regionview->height();
2606 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2608 Editor::EnterContext* ctx = get_enter_context(RegionItem);
2609 if (_join_object_range_state != old && ctx) {
2610 ctx->cursor_ctx->change(which_track_cursor());
2613 } else if (entered_track) {
2615 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2617 if (entered_route_view) {
2622 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2624 double track_height = entered_route_view->view()->child_height();
2625 if (UIConfiguration::instance().get_show_name_highlight()) {
2626 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2628 double const c = cy / track_height;
2632 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2634 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2638 /* Other kinds of tracks use object mode */
2639 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2642 Editor::EnterContext* ctx = get_enter_context(StreamItem);
2643 if (_join_object_range_state != old && ctx) {
2644 ctx->cursor_ctx->change(which_track_cursor());
2650 Editor::effective_mouse_mode () const
2652 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2654 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2662 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2664 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2667 e->region_view().delete_note (e->note ());
2670 /** Obtain the pointer position in canvas coordinates */
2672 Editor::get_pointer_position (double& x, double& y) const
2675 _track_canvas->get_pointer (px, py);
2676 _track_canvas->window_to_canvas (px, py, x, y);