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"
51 #include "time_axis_view.h"
52 #include "audio_time_axis.h"
53 #include "audio_region_view.h"
54 #include "midi_region_view.h"
56 #include "streamview.h"
57 #include "region_gain_line.h"
58 #include "automation_time_axis.h"
59 #include "control_point.h"
61 #include "selection.h"
64 #include "rgb_macros.h"
65 #include "control_point_dialog.h"
66 #include "editor_drag.h"
67 #include "automation_region_view.h"
68 #include "edit_note_dialog.h"
69 #include "mouse_cursors.h"
70 #include "editor_cursors.h"
71 #include "verbose_cursor.h"
77 using namespace ARDOUR;
80 using namespace Editing;
81 using Gtkmm2ext::Keyboard;
84 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
86 /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
87 pays attentions to subwindows. this means that menu windows are ignored, and
88 if the pointer is in a menu, the return window from the call will be the
89 the regular subwindow *under* the menu.
91 this matters quite a lot if the pointer is moving around in a menu that overlaps
92 the track canvas because we will believe that we are within the track canvas
93 when we are not. therefore, we track enter/leave events for the track canvas
94 and allow that to override the result of gdk_window_get_pointer().
97 if (!within_track_canvas) {
102 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
104 if (!canvas_window) {
108 Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
110 if (!pointer_window) {
114 if (pointer_window != canvas_window) {
115 in_track_canvas = false;
119 in_track_canvas = true;
122 event.type = GDK_BUTTON_RELEASE;
126 where = window_event_sample (&event, 0, 0);
132 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
134 ArdourCanvas::Duple d;
136 if (!gdk_event_get_coords (event, &d.x, &d.y)) {
140 /* event coordinates are in window units, so convert to canvas
143 d = _track_canvas->window_to_canvas (d);
153 return pixel_to_sample (d.x);
157 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
162 /* event coordinates are already in canvas units */
164 if (!gdk_event_get_coords (event, &x, &y)) {
165 cerr << "!NO c COORDS for event type " << event->type << endl;
177 /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel
178 position is negative (as can be the case with motion events in particular),
179 the frame location is always positive.
182 return pixel_to_sample_from_event (x);
186 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
188 boost::shared_ptr<Trimmable> st = _trimmable.lock();
190 if (!st || st == t) {
196 Editor::set_current_movable (boost::shared_ptr<Movable> m)
198 boost::shared_ptr<Movable> sm = _movable.lock();
200 if (!sm || sm != m) {
206 Editor::mouse_mode_object_range_toggled()
208 MouseMode m = mouse_mode;
210 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
212 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
215 set_mouse_mode(m, true); //call this so the button styles can get updated
218 static Glib::RefPtr<Action>
219 get_mouse_mode_action(MouseMode m)
223 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
225 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
227 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
229 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
231 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
233 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-content"));
235 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
237 return Glib::RefPtr<Action>();
241 Editor::set_mouse_mode (MouseMode m, bool force)
243 if (_drags->active ()) {
247 if (!force && m == mouse_mode) {
251 if (ARDOUR::Profile->get_mixbus()) {
252 if ( m == MouseCut) m = MouseObject;
255 Glib::RefPtr<Action> act = get_mouse_mode_action(m);
256 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
258 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
259 tact->set_active (false);
260 tact->set_active (true);
262 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
266 Editor::mouse_mode_toggled (MouseMode m)
268 if (ARDOUR::Profile->get_mixbus()) {
269 if ( m == MouseCut) m = MouseObject;
272 Glib::RefPtr<Action> act = get_mouse_mode_action(m);
273 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
275 if (!tact->get_active()) {
276 /* this was just the notification that the old mode has been
277 * left. we'll get called again with the new mode active in a
283 if (_session && mouse_mode == MouseAudition) {
284 /* stop transport and reset default speed to avoid oddness with
286 _session->request_transport_speed (0.0, true);
289 const bool was_internal = internal_editing();
293 /* Switch snap type/mode if we're moving to/from an internal tool. Note
294 this must toggle the actions and not call set_snap_*() directly,
295 otherwise things get out of sync and the combo box stops working. */
296 if (!was_internal && internal_editing()) {
297 snap_type_action(internal_snap_type)->set_active(true);
298 snap_mode_action(internal_snap_mode)->set_active(true);
299 } else if (was_internal && !internal_editing()) {
300 snap_type_action(pre_internal_snap_type)->set_active(true);
301 snap_mode_action(pre_internal_snap_mode)->set_active(true);
306 /* this should generate a new enter event which will
307 trigger the appropiate cursor.
311 _track_canvas->re_enter ();
314 set_gain_envelope_visibility ();
316 update_time_selection_display ();
318 update_all_enter_cursors ();
320 MouseModeChanged (); /* EMIT SIGNAL */
324 Editor::internal_editing() const
326 return mouse_mode == Editing::MouseContent || mouse_mode == Editing::MouseDraw;
330 Editor::update_time_selection_display ()
332 switch (mouse_mode) {
334 selection->clear_objects ();
335 selection->clear_midi_notes ();
338 selection->clear_time ();
339 selection->clear_tracks ();
340 selection->clear_midi_notes ();
343 /* Clear regions, but not time or tracks, since that
344 would destroy the range selection rectangle, which we need to stick
345 around for AutomationRangeDrag. */
346 selection->clear_regions ();
347 selection->clear_playlists ();
350 /* This handles internal edit.
351 Clear everything except points and notes.
353 selection->clear_regions();
354 selection->clear_lines();
355 selection->clear_playlists ();
357 selection->clear_time ();
358 selection->clear_tracks ();
362 /* We probably want to keep region selection */
363 selection->clear_points ();
364 selection->clear_lines();
365 selection->clear_playlists ();
367 selection->clear_time ();
368 selection->clear_tracks ();
372 /*Don't lose lines or points if no action in this mode */
373 selection->clear_regions ();
374 selection->clear_playlists ();
375 selection->clear_time ();
376 selection->clear_tracks ();
380 /*Clear everything */
381 selection->clear_objects();
382 selection->clear_time ();
383 selection->clear_tracks ();
389 Editor::step_mouse_mode (bool next)
391 const int n_mouse_modes = (int)MouseContent + 1;
392 int current = (int)current_mouse_mode();
394 set_mouse_mode((MouseMode)((current + 1) % n_mouse_modes));
396 set_mouse_mode((MouseMode)((current + n_mouse_modes - 1) % n_mouse_modes));
401 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
404 /* in object/audition/timefx/gain-automation mode,
405 any button press sets the selection if the object
406 can be selected. this is a bit of hack, because
407 we want to avoid this if the mouse operation is a
410 note: not dbl-click or triple-click
412 Also note that there is no region selection in internal edit mode, otherwise
413 for operations operating on the selection (e.g. cut) it is not obvious whether
414 to cut notes or regions.
417 MouseMode eff_mouse_mode = effective_mouse_mode ();
419 if (eff_mouse_mode == MouseCut) {
420 /* never change selection in cut mode */
424 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
425 /* context clicks are always about object properties, even if
426 we're in range mode within smart mode.
428 eff_mouse_mode = MouseObject;
431 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
432 if (get_smart_mode()) {
434 case FadeInHandleItem:
435 case FadeInTrimHandleItem:
436 case FadeOutHandleItem:
437 case FadeOutTrimHandleItem:
438 eff_mouse_mode = MouseObject;
445 if (((mouse_mode != MouseObject) &&
446 (mouse_mode != MouseAudition || item_type != RegionItem) &&
447 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
448 (mouse_mode != MouseDraw) &&
449 (mouse_mode != MouseContent || item_type == RegionItem)) ||
450 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
454 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
456 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
458 /* almost no selection action on modified button-2 or button-3 events */
460 if ((item_type != RegionItem && event->button.button != 2)
461 /* for selection of control points prior to delete (shift-right click) */
462 && !(item_type == ControlPointItem && event->button.button == 3 && event->type == GDK_BUTTON_PRESS)) {
468 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
469 bool press = (event->type == GDK_BUTTON_PRESS);
472 _mouse_changed_selection = false;
477 if (eff_mouse_mode == MouseDraw) {
481 if (eff_mouse_mode != MouseRange) {
482 _mouse_changed_selection = set_selected_regionview_from_click (press, op);
484 /* don't change the selection unless the
485 clicked track is not currently selected. if
486 so, "collapse" the selection to just this
489 if (!selection->selected (clicked_axisview)) {
490 set_selected_track_as_side_effect (Selection::Set);
494 if (eff_mouse_mode != MouseRange) {
495 _mouse_changed_selection |= set_selected_regionview_from_click (press, op);
500 case RegionViewNameHighlight:
502 case LeftFrameHandle:
503 case RightFrameHandle:
504 case FadeInHandleItem:
505 case FadeInTrimHandleItem:
507 case FadeOutHandleItem:
508 case FadeOutTrimHandleItem:
510 case StartCrossFadeItem:
511 case EndCrossFadeItem:
512 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
513 _mouse_changed_selection |= set_selected_regionview_from_click (press, op);
514 } else if (event->type == GDK_BUTTON_PRESS) {
515 set_selected_track_as_side_effect (op);
519 case ControlPointItem:
520 /* for object/track exclusivity, we don't call set_selected_track_as_side_effect (op); */
522 if (eff_mouse_mode != MouseRange) {
523 if (event->button.button != 3) {
524 _mouse_changed_selection |= set_selected_control_point_from_click (press, op);
526 _mouse_changed_selection |= set_selected_control_point_from_click (press, Selection::Set);
532 if (eff_mouse_mode != MouseRange) {
533 AutomationLine* argl = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
535 std::list<Selectable*> selectables;
536 uint32_t before, after;
537 framecnt_t const where = (framecnt_t) floor (event->button.x * samples_per_pixel) - clicked_regionview->region ()->position ();
539 if (!argl || !argl->control_points_adjacent (where, before, after)) {
543 selectables.push_back (argl->nth (before));
544 selectables.push_back (argl->nth (after));
549 selection->set (selectables);
550 _mouse_changed_selection = true;
555 selection->add (selectables);
556 _mouse_changed_selection = true;
559 case Selection::Toggle:
561 selection->toggle (selectables);
562 _mouse_changed_selection = true;
566 case Selection::Extend:
573 case AutomationLineItem:
574 if (eff_mouse_mode != MouseRange) {
575 AutomationLine* al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
576 std::list<Selectable*> selectables;
577 uint32_t before, after;
578 framecnt_t const where = (framecnt_t) floor (event->button.x * samples_per_pixel);
580 if (!al || !al->control_points_adjacent (where, before, after)) {
584 selectables.push_back (al->nth (before));
585 selectables.push_back (al->nth (after));
590 selection->set (selectables);
591 _mouse_changed_selection = true;
596 selection->add (selectables);
597 _mouse_changed_selection = true;
600 case Selection::Toggle:
602 selection->toggle (selectables);
603 _mouse_changed_selection = true;
607 case Selection::Extend:
615 /* for context click, select track */
616 if (event->button.button == 3) {
617 selection->clear_tracks ();
618 set_selected_track_as_side_effect (op);
620 /* We won't get a release.*/
621 begin_reversible_selection_op (X_("Button 3 Menu Select"));
622 commit_reversible_selection_op ();
626 case AutomationTrackItem:
627 if (eff_mouse_mode != MouseDraw && op == Selection::Set) {
628 set_selected_track_as_side_effect (op);
633 if (press && event->button.button == 3) {
634 NoteBase* cnote = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
636 if (cnote->region_view().selection_size() == 0 || !cnote->selected()) {
637 selection->clear_points();
638 cnote->region_view().unique_select (cnote);
639 /* we won't get the release, so store the selection change now */
640 begin_reversible_selection_op (X_("Button 3 Note Selection"));
641 commit_reversible_selection_op ();
650 if ((!press) && _mouse_changed_selection) {
651 begin_reversible_selection_op (X_("Button Selection"));
652 commit_reversible_selection_op ();
653 _mouse_changed_selection = false;
658 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
660 /* single mouse clicks on any of these item types operate
661 independent of mouse mode, mostly because they are
662 not on the main track canvas or because we want
666 NoteBase* note = NULL;
669 case PlayheadCursorItem:
670 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
674 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
675 hide_marker (item, event);
677 _drags->set (new MarkerDrag (this, item), event);
681 case TempoMarkerItem:
684 new TempoMarkerDrag (
687 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
694 case MeterMarkerItem:
697 new MeterMarkerDrag (
700 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
708 _drags->set (new VideoTimeLineDrag (this, item), event);
715 case TimecodeRulerItem:
716 case SamplesRulerItem:
717 case MinsecRulerItem:
719 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
720 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
726 case RangeMarkerBarItem:
727 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
728 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
729 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
730 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
732 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
737 case CdMarkerBarItem:
738 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
739 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
741 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
746 case TransportMarkerBarItem:
747 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
748 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
750 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
759 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
760 /* special case: allow trim of range selections in joined object mode;
761 in theory eff should equal MouseRange in this case, but it doesn't
762 because entering the range selection canvas item results in entered_regionview
763 being set to 0, so update_join_object_range_location acts as if we aren't
766 if (item_type == StartSelectionTrimItem) {
767 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
768 } else if (item_type == EndSelectionTrimItem) {
769 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
773 Editing::MouseMode eff = effective_mouse_mode ();
775 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
776 if (get_smart_mode()) {
778 case FadeInHandleItem:
779 case FadeInTrimHandleItem:
780 case FadeOutHandleItem:
781 case FadeOutTrimHandleItem:
792 case StartSelectionTrimItem:
793 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
796 case EndSelectionTrimItem:
797 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
801 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
802 start_selection_grab (item, event);
804 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
805 /* grab selection for moving */
806 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
808 /* this was debated, but decided the more common action was to
809 make a new selection */
810 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
815 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
816 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
818 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
823 case RegionViewNameHighlight:
824 if (!clicked_regionview->region()->locked()) {
825 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
831 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
832 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
834 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
843 case FadeInHandleItem:
844 case FadeOutHandleItem:
845 case LeftFrameHandle:
846 case RightFrameHandle:
847 case FeatureLineItem:
848 case RegionViewNameHighlight:
851 case AutomationTrackItem:
852 _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, get_canvas_cursor());
863 /* Existing note: allow trimming/motion */
864 if ((note = reinterpret_cast<NoteBase*> (item->get_data ("notebase")))) {
865 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
866 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
868 _drags->set (new NoteDrag (this, item), event);
874 _drags->set (new LineDrag (this, item), event);
878 case ControlPointItem:
879 _drags->set (new ControlPointDrag (this, item), event);
883 case AutomationLineItem:
884 _drags->set (new LineDrag (this, item), event);
889 //in the past, we created a new midi region here, but perhaps that is best left to the Draw mode
892 case AutomationTrackItem:
893 /* rubberband drag to select automation points */
894 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
899 if (dynamic_cast<AutomationRegionView*>(clicked_regionview)) {
900 /* rubberband drag to select automation points */
901 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
912 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
913 event->type == GDK_BUTTON_PRESS) {
915 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
917 } else if (event->type == GDK_BUTTON_PRESS) {
920 case FadeInHandleItem:
922 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
926 case FadeOutHandleItem:
928 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
932 case StartCrossFadeItem:
933 case EndCrossFadeItem:
934 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
935 // if (!clicked_regionview->region()->locked()) {
936 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
941 case FeatureLineItem:
943 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
944 remove_transient(item);
948 _drags->set (new FeatureLineDrag (this, item), event);
954 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
955 /* click on an automation region view; do nothing here and let the ARV's signal handler
961 /* click on a normal region view */
962 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
963 add_region_copy_drag (item, event, clicked_regionview);
964 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
965 add_region_brush_drag (item, event, clicked_regionview);
967 add_region_drag (item, event, clicked_regionview);
971 _drags->start_grab (event);
975 case RegionViewNameHighlight:
976 case LeftFrameHandle:
977 case RightFrameHandle:
978 if (!clicked_regionview->region()->locked()) {
979 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
984 case FadeInTrimHandleItem:
985 case FadeOutTrimHandleItem:
986 if (!clicked_regionview->region()->locked()) {
987 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
994 /* rename happens on edit clicks */
995 if (clicked_regionview->get_name_highlight()) {
996 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1002 case ControlPointItem:
1003 _drags->set (new ControlPointDrag (this, item), event);
1007 case AutomationLineItem:
1008 _drags->set (new LineDrag (this, item), event);
1013 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1016 case AutomationTrackItem:
1018 TimeAxisView* parent = clicked_axisview->get_parent ();
1019 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1021 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1023 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1025 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1026 if (pl->n_regions() == 0) {
1027 /* Parent has no regions; create one so that we have somewhere to put automation */
1028 _drags->set (new RegionCreateDrag (this, item, parent), event);
1030 /* See if there's a region before the click that we can extend, and extend it if so */
1031 framepos_t const t = canvas_event_sample (event);
1032 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1034 _drags->set (new RegionCreateDrag (this, item, parent), event);
1036 prev->set_length (t - prev->position ());
1040 /* rubberband drag to select automation points */
1041 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1063 switch (item_type) {
1065 _drags->set (new LineDrag (this, item), event);
1068 case ControlPointItem:
1069 _drags->set (new ControlPointDrag (this, item), event);
1075 if (dynamic_cast<AudioRegionView*>(clicked_regionview) ||
1076 dynamic_cast<AutomationRegionView*>(clicked_regionview)) {
1077 _drags->set (new AutomationRangeDrag (this, clicked_regionview, selection->time),
1078 event, _cursors->up_down);
1080 double const y = event->button.y;
1081 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y, false);
1083 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1085 /* smart "join" mode: drag automation */
1086 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1094 case AutomationLineItem:
1095 _drags->set (new LineDrag (this, item), event);
1099 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
1100 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
1101 /* Note is big and pointer is near the end, trim */
1102 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
1105 _drags->set (new NoteDrag (this, item), event);
1112 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1113 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1124 if (item_type == NoteItem) {
1125 /* resize-drag notes */
1126 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
1127 if (note->big_enough_to_trim()) {
1128 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
1132 } else if (clicked_regionview) {
1134 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1140 _drags->set (new ScrubDrag (this, item), event, _cursors->transparent);
1141 scrub_reversals = 0;
1142 scrub_reverse_distance = 0;
1143 last_scrub_x = event->button.x;
1144 scrubbing_direction = 0;
1156 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1158 Editing::MouseMode const eff = effective_mouse_mode ();
1161 switch (item_type) {
1163 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1164 add_region_copy_drag (item, event, clicked_regionview);
1166 add_region_drag (item, event, clicked_regionview);
1168 _drags->start_grab (event);
1171 case ControlPointItem:
1172 _drags->set (new ControlPointDrag (this, item), event);
1180 switch (item_type) {
1181 case RegionViewNameHighlight:
1182 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1186 case LeftFrameHandle:
1187 case RightFrameHandle:
1188 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1192 case RegionViewName:
1193 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1207 /* relax till release */
1219 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1221 if (event->type == GDK_2BUTTON_PRESS) {
1222 _drags->mark_double_click ();
1223 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1227 if (event->type != GDK_BUTTON_PRESS) {
1231 _track_canvas->grab_focus();
1233 if (_session && _session->actively_recording()) {
1237 button_selection (item, event, item_type);
1239 if (!_drags->active () &&
1240 (Keyboard::is_delete_event (&event->button) ||
1241 Keyboard::is_context_menu_event (&event->button) ||
1242 Keyboard::is_edit_event (&event->button))) {
1244 /* handled by button release */
1248 //not rolling, range mode click + join_play_range : locate the PH here
1249 if ( !_drags->active () && _session && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && UIConfiguration::instance().get_follow_edits() && !_session->config.get_external_sync() ) {
1250 framepos_t where = canvas_event_sample (event);
1252 _session->request_locate (where, false);
1255 switch (event->button.button) {
1257 return button_press_handler_1 (item, event, item_type);
1261 return button_press_handler_2 (item, event, item_type);
1268 return button_press_dispatch (&event->button);
1277 Editor::button_press_dispatch (GdkEventButton* ev)
1279 /* this function is intended only for buttons 4 and above.
1282 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1283 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1287 Editor::button_release_dispatch (GdkEventButton* ev)
1289 /* this function is intended only for buttons 4 and above.
1292 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1293 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1297 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1299 framepos_t where = canvas_event_sample (event);
1300 AutomationTimeAxisView* atv = 0;
1302 _press_cursor_ctx.reset();
1304 /* no action if we're recording */
1306 if (_session && _session->actively_recording()) {
1310 bool were_dragging = false;
1312 if (!Keyboard::is_context_menu_event (&event->button)) {
1314 /* see if we're finishing a drag */
1316 if (_drags->active ()) {
1317 bool const r = _drags->end_grab (event);
1319 /* grab dragged, so do nothing else */
1323 were_dragging = true;
1326 update_region_layering_order_editor ();
1329 /* edit events get handled here */
1331 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1332 switch (item_type) {
1334 show_region_properties ();
1337 case TempoMarkerItem: {
1338 ArdourMarker* marker;
1339 TempoMarker* tempo_marker;
1341 if ((marker = reinterpret_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1342 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1343 abort(); /*NOTREACHED*/
1346 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1347 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1348 abort(); /*NOTREACHED*/
1351 edit_tempo_marker (*tempo_marker);
1355 case MeterMarkerItem: {
1356 ArdourMarker* marker;
1357 MeterMarker* meter_marker;
1359 if ((marker = reinterpret_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1360 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1361 abort(); /*NOTREACHED*/
1364 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1365 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1366 abort(); /*NOTREACHED*/
1368 edit_meter_marker (*meter_marker);
1372 case RegionViewName:
1373 if (clicked_regionview->name_active()) {
1374 return mouse_rename_region (item, event);
1378 case ControlPointItem:
1379 edit_control_point (item);
1388 /* context menu events get handled here */
1389 if (Keyboard::is_context_menu_event (&event->button)) {
1391 context_click_event = *event;
1393 if (!_drags->active ()) {
1395 /* no matter which button pops up the context menu, tell the menu
1396 widget to use button 1 to drive menu selection.
1399 switch (item_type) {
1401 case FadeInHandleItem:
1402 case FadeInTrimHandleItem:
1403 case StartCrossFadeItem:
1404 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1408 case FadeOutHandleItem:
1409 case FadeOutTrimHandleItem:
1410 case EndCrossFadeItem:
1411 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1414 case LeftFrameHandle:
1415 case RightFrameHandle:
1419 popup_track_context_menu (1, event->button.time, item_type, false);
1423 case RegionViewNameHighlight:
1424 case RegionViewName:
1425 popup_track_context_menu (1, event->button.time, item_type, false);
1429 popup_track_context_menu (1, event->button.time, item_type, true);
1432 case AutomationTrackItem:
1433 popup_track_context_menu (1, event->button.time, item_type, false);
1437 case RangeMarkerBarItem:
1438 case TransportMarkerBarItem:
1439 case CdMarkerBarItem:
1443 case TimecodeRulerItem:
1444 case SamplesRulerItem:
1445 case MinsecRulerItem:
1447 popup_ruler_menu (where, item_type);
1451 marker_context_menu (&event->button, item);
1454 case TempoMarkerItem:
1455 tempo_or_meter_marker_context_menu (&event->button, item);
1458 case MeterMarkerItem:
1459 tempo_or_meter_marker_context_menu (&event->button, item);
1462 case CrossfadeViewItem:
1463 popup_track_context_menu (1, event->button.time, item_type, false);
1466 case ControlPointItem:
1467 popup_control_point_context_menu (item, event);
1471 if (internal_editing()) {
1472 popup_note_context_menu (item, event);
1484 /* delete events get handled here */
1486 Editing::MouseMode const eff = effective_mouse_mode ();
1488 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1490 switch (item_type) {
1491 case TempoMarkerItem:
1492 remove_tempo_marker (item);
1495 case MeterMarkerItem:
1496 remove_meter_marker (item);
1500 remove_marker (*item, event);
1504 if (eff == MouseObject) {
1505 remove_clicked_region ();
1509 case ControlPointItem:
1510 remove_control_point (item);
1514 remove_midi_note (item, event);
1523 switch (event->button.button) {
1526 switch (item_type) {
1527 /* see comments in button_press_handler */
1528 case PlayheadCursorItem:
1531 case AutomationLineItem:
1532 case StartSelectionTrimItem:
1533 case EndSelectionTrimItem:
1537 if (!_dragging_playhead) {
1538 snap_to_with_modifier (where, event, RoundNearest, true);
1539 mouse_add_new_marker (where);
1543 case CdMarkerBarItem:
1544 if (!_dragging_playhead) {
1545 // if we get here then a dragged range wasn't done
1546 snap_to_with_modifier (where, event, RoundNearest, true);
1547 mouse_add_new_marker (where, true);
1552 if (!_dragging_playhead) {
1553 snap_to_with_modifier (where, event);
1554 mouse_add_new_tempo_event (where);
1559 if (!_dragging_playhead) {
1560 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1565 case TimecodeRulerItem:
1566 case SamplesRulerItem:
1567 case MinsecRulerItem:
1578 switch (item_type) {
1581 /* check that we didn't drag before releasing, since
1582 its really annoying to create new control
1583 points when doing this.
1585 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1586 if (!were_dragging && arv) {
1587 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1588 arv->add_gain_point_event (item, event, with_guard_points);
1594 case AutomationTrackItem: {
1595 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1596 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1598 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1609 if (scrubbing_direction == 0) {
1610 /* no drag, just a click */
1611 switch (item_type) {
1613 play_selected_region ();
1618 } else if (_session) {
1619 /* make sure we stop */
1620 _session->request_transport_speed (0.0);
1629 /* do any (de)selection operations that should occur on button release */
1630 button_selection (item, event, item_type);
1640 switch (item_type) {
1642 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1644 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1647 // Button2 click is unused
1662 // x_style_paste (where, 1.0);
1683 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1686 ArdourMarker * marker;
1690 /* by the time we reach here, entered_regionview and entered trackview
1691 * will have already been set as appropriate. Things are done this
1692 * way because this method isn't passed a pointer to a variable type of
1693 * thing that is entered (which may or may not be canvas item).
1694 * (e.g. the actual entered regionview)
1697 choose_canvas_cursor_on_entry (item_type);
1699 switch (item_type) {
1700 case ControlPointItem:
1701 if (mouse_mode == MouseDraw || mouse_mode == MouseObject || mouse_mode == MouseContent) {
1702 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1705 fraction = 1.0 - (cp->get_y() / cp->line().height());
1707 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1708 _verbose_cursor->show ();
1713 if (mouse_mode == MouseDraw) {
1714 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1716 line->set_outline_color (UIConfiguration::instance().color ("entered gain line"));
1721 case AutomationLineItem:
1722 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1723 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1725 line->set_outline_color (UIConfiguration::instance().color ("entered automation line"));
1730 case AutomationTrackItem:
1731 AutomationTimeAxisView* atv;
1732 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1733 clear_entered_track = false;
1734 set_entered_track (atv);
1739 if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1742 entered_marker = marker;
1743 marker->set_color_rgba (UIConfiguration::instance().color ("entered marker"));
1745 case MeterMarkerItem:
1746 case TempoMarkerItem:
1749 case FadeInHandleItem:
1750 case FadeInTrimHandleItem:
1751 if (mouse_mode == MouseObject) {
1752 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1754 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1755 rect->set_fill_color (rv->get_fill_color());
1760 case FadeOutHandleItem:
1761 case FadeOutTrimHandleItem:
1762 if (mouse_mode == MouseObject) {
1763 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1765 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1766 rect->set_fill_color (rv->get_fill_color ());
1771 case FeatureLineItem:
1773 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1774 line->set_outline_color (0xFF0000FF);
1783 if (entered_regionview) {
1784 entered_regionview->entered();
1793 /* third pass to handle entered track status in a comprehensible way.
1796 switch (item_type) {
1798 case AutomationLineItem:
1799 case ControlPointItem:
1800 /* these do not affect the current entered track state */
1801 clear_entered_track = false;
1804 case AutomationTrackItem:
1805 /* handled above already */
1817 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1820 ArdourMarker *marker;
1825 if (!_enter_stack.empty()) {
1826 _enter_stack.pop_back();
1829 switch (item_type) {
1830 case ControlPointItem:
1831 _verbose_cursor->hide ();
1835 case AutomationLineItem:
1836 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1838 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1840 line->set_outline_color (al->get_line_color());
1846 if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1850 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1851 location_flags_changed (loc);
1854 case MeterMarkerItem:
1855 case TempoMarkerItem:
1858 case FadeInTrimHandleItem:
1859 case FadeOutTrimHandleItem:
1860 case FadeInHandleItem:
1861 case FadeOutHandleItem:
1863 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1865 rect->set_fill_color (UIConfiguration::instance().color ("inactive fade handle"));
1870 case AutomationTrackItem:
1873 case FeatureLineItem:
1875 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1876 line->set_outline_color (UIConfiguration::instance().color ("zero line"));
1888 Editor::scrub (framepos_t frame, double current_x)
1892 if (scrubbing_direction == 0) {
1894 _session->request_locate (frame, false);
1895 _session->request_transport_speed (0.1);
1896 scrubbing_direction = 1;
1900 if (last_scrub_x > current_x) {
1902 /* pointer moved to the left */
1904 if (scrubbing_direction > 0) {
1906 /* we reversed direction to go backwards */
1909 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1913 /* still moving to the left (backwards) */
1915 scrub_reversals = 0;
1916 scrub_reverse_distance = 0;
1918 delta = 0.01 * (last_scrub_x - current_x);
1919 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1923 /* pointer moved to the right */
1925 if (scrubbing_direction < 0) {
1926 /* we reversed direction to go forward */
1929 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1932 /* still moving to the right */
1934 scrub_reversals = 0;
1935 scrub_reverse_distance = 0;
1937 delta = 0.01 * (current_x - last_scrub_x);
1938 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1942 /* if there have been more than 2 opposite motion moves detected, or one that moves
1943 back more than 10 pixels, reverse direction
1946 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1948 if (scrubbing_direction > 0) {
1949 /* was forwards, go backwards */
1950 _session->request_transport_speed (-0.1);
1951 scrubbing_direction = -1;
1953 /* was backwards, go forwards */
1954 _session->request_transport_speed (0.1);
1955 scrubbing_direction = 1;
1958 scrub_reverse_distance = 0;
1959 scrub_reversals = 0;
1963 last_scrub_x = current_x;
1967 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1969 _last_motion_y = event->motion.y;
1971 if (event->motion.is_hint) {
1974 /* We call this so that MOTION_NOTIFY events continue to be
1975 delivered to the canvas. We need to do this because we set
1976 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1977 the density of the events, at the expense of a round-trip
1978 to the server. Given that this will mostly occur on cases
1979 where DISPLAY = :0.0, and given the cost of what the motion
1980 event might do, its a good tradeoff.
1983 _track_canvas->get_pointer (x, y);
1986 if (current_stepping_trackview) {
1987 /* don't keep the persistent stepped trackview if the mouse moves */
1988 current_stepping_trackview = 0;
1989 step_timeout.disconnect ();
1992 if (_session && _session->actively_recording()) {
1993 /* Sorry. no dragging stuff around while we record */
1997 update_join_object_range_location (event->motion.y);
1999 if (_drags->active ()) {
2000 return _drags->motion_handler (event, from_autoscroll);
2007 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2009 ControlPoint* control_point;
2011 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2012 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2013 abort(); /*NOTREACHED*/
2016 AutomationLine& line = control_point->line ();
2017 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2018 /* we shouldn't remove the first or last gain point in region gain lines */
2019 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2028 Editor::remove_control_point (ArdourCanvas::Item* item)
2030 if (!can_remove_control_point (item)) {
2034 ControlPoint* control_point;
2036 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2037 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2038 abort(); /*NOTREACHED*/
2041 control_point->line().remove_point (*control_point);
2045 Editor::edit_control_point (ArdourCanvas::Item* item)
2047 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2050 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2051 abort(); /*NOTREACHED*/
2054 ControlPointDialog d (p);
2056 if (d.run () != RESPONSE_ACCEPT) {
2060 p->line().modify_point_y (*p, d.get_y_fraction ());
2064 Editor::edit_notes (MidiRegionView* mrv)
2066 MidiRegionView::Selection const & s = mrv->selection();
2072 EditNoteDialog* d = new EditNoteDialog (mrv, s);
2075 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2079 Editor::note_edit_done (int r, EditNoteDialog* d)
2086 Editor::visible_order_range (int* low, int* high) const
2088 *low = TimeAxisView::max_order ();
2091 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2093 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2095 if (!rtv->hidden()) {
2097 if (*high < rtv->order()) {
2098 *high = rtv->order ();
2101 if (*low > rtv->order()) {
2102 *low = rtv->order ();
2109 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2111 /* Either add to or set the set the region selection, unless
2112 this is an alignment click (control used)
2115 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2116 TimeAxisView* tv = &rv.get_time_axis_view();
2117 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2119 if (rtv && rtv->is_track()) {
2120 speed = rtv->track()->speed();
2123 framepos_t where = get_preferred_edit_position();
2127 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2129 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2131 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2133 align_region (rv.region(), End, (framepos_t) (where * speed));
2137 align_region (rv.region(), Start, (framepos_t) (where * speed));
2144 Editor::collect_new_region_view (RegionView* rv)
2146 latest_regionviews.push_back (rv);
2150 Editor::collect_and_select_new_region_view (RegionView* rv)
2153 latest_regionviews.push_back (rv);
2157 Editor::cancel_selection ()
2159 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2160 (*i)->hide_selection ();
2163 selection->clear ();
2164 clicked_selection = 0;
2168 Editor::cancel_time_selection ()
2170 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2171 (*i)->hide_selection ();
2173 selection->time.clear ();
2174 clicked_selection = 0;
2178 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2180 RegionView* rv = clicked_regionview;
2182 /* Choose action dependant on which button was pressed */
2183 switch (event->button.button) {
2185 begin_reversible_command (_("start point trim"));
2187 if (selection->selected (rv)) {
2188 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2189 i != selection->regions.by_layer().end(); ++i)
2191 if (!(*i)->region()->locked()) {
2192 (*i)->region()->clear_changes ();
2193 (*i)->region()->trim_front (new_bound);
2194 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2199 if (!rv->region()->locked()) {
2200 rv->region()->clear_changes ();
2201 rv->region()->trim_front (new_bound);
2202 _session->add_command(new StatefulDiffCommand (rv->region()));
2206 commit_reversible_command();
2210 begin_reversible_command (_("end point trim"));
2212 if (selection->selected (rv)) {
2214 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2216 if (!(*i)->region()->locked()) {
2217 (*i)->region()->clear_changes();
2218 (*i)->region()->trim_end (new_bound);
2219 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2225 if (!rv->region()->locked()) {
2226 rv->region()->clear_changes ();
2227 rv->region()->trim_end (new_bound);
2228 _session->add_command (new StatefulDiffCommand (rv->region()));
2232 commit_reversible_command();
2241 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2243 ArdourMarker* marker;
2246 if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
2247 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2248 abort(); /*NOTREACHED*/
2251 Location* location = find_location_from_marker (marker, is_start);
2252 location->set_hidden (true, this);
2256 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2258 using namespace Gtkmm2ext;
2260 ArdourPrompter prompter (false);
2262 prompter.set_prompt (_("Name for region:"));
2263 prompter.set_initial_text (clicked_regionview->region()->name());
2264 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2265 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2266 prompter.show_all ();
2267 switch (prompter.run ()) {
2268 case Gtk::RESPONSE_ACCEPT:
2270 prompter.get_result(str);
2272 clicked_regionview->region()->set_name (str);
2281 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2283 /* no brushing without a useful snap setting */
2285 switch (_snap_mode) {
2287 return; /* can't work because it allows region to be placed anywhere */
2292 switch (_snap_type) {
2300 /* don't brush a copy over the original */
2302 if (pos == rv->region()->position()) {
2306 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2308 if (rtv == 0 || !rtv->is_track()) {
2312 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2313 double speed = rtv->track()->speed();
2315 playlist->clear_changes ();
2316 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2317 playlist->add_region (new_region, (framepos_t) (pos * speed));
2318 _session->add_command (new StatefulDiffCommand (playlist));
2320 // playlist is frozen, so we have to update manually XXX this is disgusting
2322 //playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2326 Editor::track_height_step_timeout ()
2328 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2329 current_stepping_trackview = 0;
2336 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2338 assert (region_view);
2340 if (!region_view->region()->playlist()) {
2344 switch (Config->get_edit_mode()) {
2346 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2349 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2352 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2359 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2361 assert (region_view);
2363 if (!region_view->region()->playlist()) {
2367 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2371 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2373 assert (region_view);
2375 if (!region_view->region()->playlist()) {
2379 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2383 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2386 /** Start a grab where a time range is selected, track(s) are selected, and the
2387 * user clicks and drags a region with a modifier in order to create a new region containing
2388 * the section of the clicked region that lies within the time range.
2391 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2393 if (clicked_regionview == 0) {
2397 /* lets try to create new Region for the selection */
2399 vector<boost::shared_ptr<Region> > new_regions;
2400 create_region_from_selection (new_regions);
2402 if (new_regions.empty()) {
2406 /* XXX fix me one day to use all new regions */
2408 boost::shared_ptr<Region> region (new_regions.front());
2410 /* add it to the current stream/playlist.
2412 tricky: the streamview for the track will add a new regionview. we will
2413 catch the signal it sends when it creates the regionview to
2414 set the regionview we want to then drag.
2417 latest_regionviews.clear();
2418 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2420 /* A selection grab currently creates two undo/redo operations, one for
2421 creating the new region and another for moving it.
2423 begin_reversible_command (Operations::selection_grab);
2425 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2427 playlist->clear_changes ();
2428 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2429 _session->add_command(new StatefulDiffCommand (playlist));
2433 if (latest_regionviews.empty()) {
2434 /* something went wrong */
2435 abort_reversible_command ();
2439 /* we need to deselect all other regionviews, and select this one
2440 i'm ignoring undo stuff, because the region creation will take care of it
2443 selection->set (latest_regionviews);
2445 commit_reversible_command ();
2447 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2453 if (_drags->active ()) {
2456 selection->clear ();
2459 reset_focus (&contents());
2462 /** Update _join_object_range_state which indicate whether we are over the top
2463 * or bottom half of a route view, used by the `join object/range' tool
2464 * mode. Coordinates in canvas space.
2467 Editor::update_join_object_range_location (double y)
2469 if (!get_smart_mode()) {
2470 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2474 JoinObjectRangeState const old = _join_object_range_state;
2476 if (mouse_mode == MouseObject) {
2477 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2478 } else if (mouse_mode == MouseRange) {
2479 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2482 if (entered_regionview) {
2484 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2485 double const c = item_space.y / entered_regionview->height();
2487 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2489 Editor::EnterContext* ctx = get_enter_context(RegionItem);
2490 if (_join_object_range_state != old && ctx) {
2491 ctx->cursor_ctx->change(which_track_cursor());
2494 } else if (entered_track) {
2496 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2498 if (entered_route_view) {
2503 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2505 double track_height = entered_route_view->view()->child_height();
2506 if (UIConfiguration::instance().get_show_name_highlight()) {
2507 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2509 double const c = cy / track_height;
2513 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2515 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2519 /* Other kinds of tracks use object mode */
2520 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2523 Editor::EnterContext* ctx = get_enter_context(StreamItem);
2524 if (_join_object_range_state != old && ctx) {
2525 ctx->cursor_ctx->change(which_track_cursor());
2531 Editor::effective_mouse_mode () const
2533 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2535 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2543 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2545 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2548 e->region_view().delete_note (e->note ());
2551 /** Obtain the pointer position in canvas coordinates */
2553 Editor::get_pointer_position (double& x, double& y) const
2556 _track_canvas->get_pointer (px, py);
2557 _track_canvas->window_to_canvas (px, py, x, y);