2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include "pbd/error.h"
30 #include "pbd/enumwriter.h"
31 #include "pbd/memento_command.h"
32 #include "pbd/basename.h"
33 #include "pbd/stateful_diff_command.h"
35 #include "gtkmm2ext/bindings.h"
36 #include "gtkmm2ext/utils.h"
37 #include "gtkmm2ext/tearoff.h"
39 #include "canvas/canvas.h"
41 #include "ardour/audioregion.h"
42 #include "ardour/operations.h"
43 #include "ardour/playlist.h"
44 #include "ardour/profile.h"
45 #include "ardour/region_factory.h"
46 #include "ardour/route.h"
47 #include "ardour/session.h"
48 #include "ardour/types.h"
50 #include "ardour_ui.h"
53 #include "time_axis_view.h"
54 #include "audio_time_axis.h"
55 #include "audio_region_view.h"
56 #include "midi_region_view.h"
58 #include "streamview.h"
59 #include "region_gain_line.h"
60 #include "automation_time_axis.h"
61 #include "control_point.h"
63 #include "selection.h"
66 #include "rgb_macros.h"
67 #include "control_point_dialog.h"
68 #include "editor_drag.h"
69 #include "automation_region_view.h"
70 #include "edit_note_dialog.h"
71 #include "mouse_cursors.h"
72 #include "editor_cursors.h"
73 #include "verbose_cursor.h"
79 using namespace ARDOUR;
82 using namespace Editing;
83 using Gtkmm2ext::Keyboard;
86 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
88 /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
89 pays attentions to subwindows. this means that menu windows are ignored, and
90 if the pointer is in a menu, the return window from the call will be the
91 the regular subwindow *under* the menu.
93 this matters quite a lot if the pointer is moving around in a menu that overlaps
94 the track canvas because we will believe that we are within the track canvas
95 when we are not. therefore, we track enter/leave events for the track canvas
96 and allow that to override the result of gdk_window_get_pointer().
99 if (!within_track_canvas) {
104 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
106 if (!canvas_window) {
110 Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
112 if (!pointer_window) {
116 if (pointer_window != canvas_window) {
117 in_track_canvas = false;
121 in_track_canvas = true;
124 event.type = GDK_BUTTON_RELEASE;
128 where = window_event_sample (&event, 0, 0);
134 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
136 ArdourCanvas::Duple d;
138 if (!gdk_event_get_coords (event, &d.x, &d.y)) {
142 /* event coordinates are in window units, so convert to canvas
145 d = _track_canvas->window_to_canvas (d);
155 return pixel_to_sample (d.x);
159 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
164 /* event coordinates are already in canvas units */
166 if (!gdk_event_get_coords (event, &x, &y)) {
167 cerr << "!NO c COORDS for event type " << event->type << endl;
179 /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel
180 position is negative (as can be the case with motion events in particular),
181 the frame location is always positive.
184 return pixel_to_sample_from_event (x);
188 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
190 boost::shared_ptr<Trimmable> st = _trimmable.lock();
192 if (!st || st == t) {
198 Editor::set_current_movable (boost::shared_ptr<Movable> m)
200 boost::shared_ptr<Movable> sm = _movable.lock();
202 if (!sm || sm != m) {
208 Editor::mouse_mode_object_range_toggled()
210 MouseMode m = mouse_mode;
212 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
214 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
217 set_mouse_mode(m, true); //call this so the button styles can get updated
220 static Glib::RefPtr<Action>
221 get_mouse_mode_action(MouseMode m)
225 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
227 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
229 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
231 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
233 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
235 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-content"));
237 return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
239 return Glib::RefPtr<Action>();
243 Editor::set_mouse_mode (MouseMode m, bool force)
245 if (_drags->active ()) {
249 if (!force && m == mouse_mode) {
253 if (ARDOUR::Profile->get_mixbus()) {
254 if ( m == MouseCut) m = MouseObject;
257 Glib::RefPtr<Action> act = get_mouse_mode_action(m);
258 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
260 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
261 tact->set_active (false);
262 tact->set_active (true);
264 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
268 Editor::mouse_mode_toggled (MouseMode m)
270 if (ARDOUR::Profile->get_mixbus()) {
271 if ( m == MouseCut) m = MouseObject;
274 Glib::RefPtr<Action> act = get_mouse_mode_action(m);
275 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
277 if (!tact->get_active()) {
278 /* this was just the notification that the old mode has been
279 * left. we'll get called again with the new mode active in a
285 if (_session && mouse_mode == MouseAudition) {
286 /* stop transport and reset default speed to avoid oddness with
288 _session->request_transport_speed (0.0, true);
291 const bool was_internal = internal_editing();
295 /* Switch snap type/mode if we're moving to/from an internal tool. Note
296 this must toggle the actions and not call set_snap_*() directly,
297 otherwise things get out of sync and the combo box stops working. */
298 if (!was_internal && internal_editing()) {
299 snap_type_action(internal_snap_type)->set_active(true);
300 snap_mode_action(internal_snap_mode)->set_active(true);
301 } else if (was_internal && !internal_editing()) {
302 snap_type_action(pre_internal_snap_type)->set_active(true);
303 snap_mode_action(pre_internal_snap_mode)->set_active(true);
308 /* this should generate a new enter event which will
309 trigger the appropiate cursor.
313 _track_canvas->re_enter ();
316 set_gain_envelope_visibility ();
318 update_time_selection_display ();
320 update_all_enter_cursors ();
322 MouseModeChanged (); /* EMIT SIGNAL */
326 Editor::internal_editing() const
328 return mouse_mode == Editing::MouseContent || mouse_mode == Editing::MouseDraw;
332 Editor::update_time_selection_display ()
334 switch (mouse_mode) {
336 selection->clear_objects ();
337 selection->ClearMidiNoteSelection (); /* EMIT SIGNAL */
340 selection->clear_time ();
341 selection->clear_tracks ();
342 selection->ClearMidiNoteSelection (); /* EMIT SIGNAL */
345 /* Clear regions, but not time or tracks, since that
346 would destroy the range selection rectangle, which we need to stick
347 around for AutomationRangeDrag. */
348 selection->clear_regions ();
349 selection->clear_playlists ();
352 /* This handles internal edit.
353 Clear everything except points and notes.
355 selection->clear_regions();
356 selection->clear_lines();
357 selection->clear_playlists ();
359 selection->clear_time ();
360 selection->clear_tracks ();
364 /* We probably want to keep region selection */
365 selection->clear_points ();
366 selection->clear_lines();
367 selection->clear_playlists ();
369 selection->clear_time ();
370 selection->clear_tracks ();
374 /*Don't lose lines or points if no action in this mode */
375 selection->clear_regions ();
376 selection->clear_playlists ();
377 selection->clear_time ();
378 selection->clear_tracks ();
382 /*Clear everything */
383 selection->clear_objects();
384 selection->clear_time ();
385 selection->clear_tracks ();
391 Editor::step_mouse_mode (bool next)
393 const int n_mouse_modes = (int)MouseContent + 1;
394 int current = (int)current_mouse_mode();
396 set_mouse_mode((MouseMode)((current + 1) % n_mouse_modes));
398 set_mouse_mode((MouseMode)((current + n_mouse_modes - 1) % n_mouse_modes));
403 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
405 /* in object/audition/timefx/gain-automation mode,
406 any button press sets the selection if the object
407 can be selected. this is a bit of hack, because
408 we want to avoid this if the mouse operation is a
411 note: not dbl-click or triple-click
413 Also note that there is no region selection in internal edit mode, otherwise
414 for operations operating on the selection (e.g. cut) it is not obvious whether
415 to cut notes or regions.
418 MouseMode eff_mouse_mode = effective_mouse_mode ();
420 if (eff_mouse_mode == MouseCut) {
421 /* never change selection in cut mode */
425 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
426 /* context clicks are always about object properties, even if
427 we're in range mode within smart mode.
429 eff_mouse_mode = MouseObject;
432 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
433 if (get_smart_mode()) {
435 case FadeInHandleItem:
436 case FadeInTrimHandleItem:
437 case FadeOutHandleItem:
438 case FadeOutTrimHandleItem:
439 eff_mouse_mode = MouseObject;
446 if (((mouse_mode != MouseObject) &&
447 (mouse_mode != MouseAudition || item_type != RegionItem) &&
448 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
449 (mouse_mode != MouseDraw)) ||
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) {
466 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
467 bool press = (event->type == GDK_BUTTON_PRESS);
470 _mouse_changed_selection = false;
476 if (eff_mouse_mode != MouseRange) {
477 _mouse_changed_selection = set_selected_regionview_from_click (press, op);
479 /* don't change the selection unless the
480 clicked track is not currently selected. if
481 so, "collapse" the selection to just this
484 if (!selection->selected (clicked_axisview)) {
485 set_selected_track_as_side_effect (Selection::Set);
489 if (eff_mouse_mode != MouseRange) {
490 _mouse_changed_selection |= set_selected_regionview_from_click (press, op);
495 case RegionViewNameHighlight:
497 case LeftFrameHandle:
498 case RightFrameHandle:
499 case FadeInHandleItem:
500 case FadeInTrimHandleItem:
502 case FadeOutHandleItem:
503 case FadeOutTrimHandleItem:
505 case StartCrossFadeItem:
506 case EndCrossFadeItem:
507 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
508 _mouse_changed_selection |= set_selected_regionview_from_click (press, op);
509 } else if (event->type == GDK_BUTTON_PRESS) {
510 set_selected_track_as_side_effect (op);
514 case ControlPointItem:
515 set_selected_track_as_side_effect (op);
516 if (eff_mouse_mode != MouseRange) {
517 _mouse_changed_selection |= set_selected_control_point_from_click (press, op);
522 /* for context click, select track */
523 if (event->button.button == 3) {
524 selection->clear_tracks ();
525 set_selected_track_as_side_effect (op);
527 /* We won't get a release.*/
528 begin_reversible_selection_op (X_("Button 3 Menu Select"));
529 commit_reversible_selection_op ();
533 case AutomationTrackItem:
534 set_selected_track_as_side_effect (op);
541 if ((!press) && _mouse_changed_selection) {
542 begin_reversible_selection_op (X_("Button Selection"));
543 commit_reversible_selection_op ();
544 _mouse_changed_selection = false;
549 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
551 /* single mouse clicks on any of these item types operate
552 independent of mouse mode, mostly because they are
553 not on the main track canvas or because we want
557 NoteBase* note = NULL;
560 case PlayheadCursorItem:
561 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
565 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
566 hide_marker (item, event);
568 _drags->set (new MarkerDrag (this, item), event);
572 case TempoMarkerItem:
575 new TempoMarkerDrag (
578 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
585 case MeterMarkerItem:
588 new MeterMarkerDrag (
591 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
599 _drags->set (new VideoTimeLineDrag (this, item), event);
606 case TimecodeRulerItem:
607 case SamplesRulerItem:
608 case MinsecRulerItem:
610 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
611 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
617 case RangeMarkerBarItem:
618 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
619 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
620 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
621 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
623 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
628 case CdMarkerBarItem:
629 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
630 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
632 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
637 case TransportMarkerBarItem:
638 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
639 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
641 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
650 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
651 /* special case: allow trim of range selections in joined object mode;
652 in theory eff should equal MouseRange in this case, but it doesn't
653 because entering the range selection canvas item results in entered_regionview
654 being set to 0, so update_join_object_range_location acts as if we aren't
657 if (item_type == StartSelectionTrimItem) {
658 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
659 } else if (item_type == EndSelectionTrimItem) {
660 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
664 Editing::MouseMode eff = effective_mouse_mode ();
666 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
667 if (get_smart_mode()) {
669 case FadeInHandleItem:
670 case FadeInTrimHandleItem:
671 case FadeOutHandleItem:
672 case FadeOutTrimHandleItem:
683 case StartSelectionTrimItem:
684 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
687 case EndSelectionTrimItem:
688 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
692 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
693 start_selection_grab (item, event);
695 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
696 /* grab selection for moving */
697 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
699 /* this was debated, but decided the more common action was to
700 make a new selection */
701 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
706 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
707 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
709 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
714 case RegionViewNameHighlight:
715 if (!clicked_regionview->region()->locked()) {
716 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
722 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
723 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
725 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
734 case FadeInHandleItem:
735 case FadeOutHandleItem:
736 case LeftFrameHandle:
737 case RightFrameHandle:
738 case FeatureLineItem:
739 case RegionViewNameHighlight:
742 case AutomationTrackItem:
743 _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, get_canvas_cursor());
754 /* Existing note: allow trimming/motion */
755 if ((note = reinterpret_cast<NoteBase*> (item->get_data ("notebase")))) {
756 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
757 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
759 _drags->set (new NoteDrag (this, item), event);
764 case ControlPointItem:
765 _drags->set (new ControlPointDrag (this, item), event);
769 case AutomationLineItem:
770 _drags->set (new LineDrag (this, item), event);
775 //in the past, we created a new midi region here, but perhaps that is best left to the Draw mode
778 case AutomationTrackItem:
779 /* rubberband drag to select automation points */
780 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
785 if (dynamic_cast<AutomationRegionView*>(clicked_regionview)) {
786 /* rubberband drag to select automation points */
787 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
798 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
799 event->type == GDK_BUTTON_PRESS) {
801 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
803 } else if (event->type == GDK_BUTTON_PRESS) {
806 case FadeInHandleItem:
808 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
812 case FadeOutHandleItem:
814 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
818 case StartCrossFadeItem:
819 case EndCrossFadeItem:
820 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
821 // if (!clicked_regionview->region()->locked()) {
822 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
827 case FeatureLineItem:
829 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
830 remove_transient(item);
834 _drags->set (new FeatureLineDrag (this, item), event);
840 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
841 /* click on an automation region view; do nothing here and let the ARV's signal handler
847 /* click on a normal region view */
848 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
849 add_region_copy_drag (item, event, clicked_regionview);
850 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
851 add_region_brush_drag (item, event, clicked_regionview);
853 add_region_drag (item, event, clicked_regionview);
857 _drags->start_grab (event);
861 case RegionViewNameHighlight:
862 case LeftFrameHandle:
863 case RightFrameHandle:
864 if (!clicked_regionview->region()->locked()) {
865 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
870 case FadeInTrimHandleItem:
871 case FadeOutTrimHandleItem:
872 if (!clicked_regionview->region()->locked()) {
873 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
880 /* rename happens on edit clicks */
881 if (clicked_regionview->get_name_highlight()) {
882 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
888 case ControlPointItem:
889 _drags->set (new ControlPointDrag (this, item), event);
893 case AutomationLineItem:
894 _drags->set (new LineDrag (this, item), event);
899 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
902 case AutomationTrackItem:
904 TimeAxisView* parent = clicked_axisview->get_parent ();
905 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
907 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
909 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
911 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
912 if (pl->n_regions() == 0) {
913 /* Parent has no regions; create one so that we have somewhere to put automation */
914 _drags->set (new RegionCreateDrag (this, item, parent), event);
916 /* See if there's a region before the click that we can extend, and extend it if so */
917 framepos_t const t = canvas_event_sample (event);
918 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
920 _drags->set (new RegionCreateDrag (this, item, parent), event);
922 prev->set_length (t - prev->position ());
926 /* rubberband drag to select automation points */
927 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
951 _drags->set (new LineDrag (this, item), event);
954 case ControlPointItem:
955 _drags->set (new ControlPointDrag (this, item), event);
961 if (dynamic_cast<AudioRegionView*>(clicked_regionview) ||
962 dynamic_cast<AutomationRegionView*>(clicked_regionview)) {
963 _drags->set (new AutomationRangeDrag (this, clicked_regionview, selection->time),
964 event, _cursors->up_down);
966 double const y = event->button.y;
967 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y, false);
969 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
971 /* smart "join" mode: drag automation */
972 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
980 case AutomationLineItem:
981 _drags->set (new LineDrag (this, item), event);
985 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
986 if (note->big_enough_to_trim() && note->mouse_near_ends()) {
987 /* Note is big and pointer is near the end, trim */
988 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
991 _drags->set (new NoteDrag (this, item), event);
998 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
999 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1010 if (item_type == NoteItem) {
1011 /* resize-drag notes */
1012 if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
1013 if (note->big_enough_to_trim()) {
1014 _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
1018 } else if (clicked_regionview) {
1020 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1026 _drags->set (new ScrubDrag (this, item), event, _cursors->transparent);
1027 scrub_reversals = 0;
1028 scrub_reverse_distance = 0;
1029 last_scrub_x = event->button.x;
1030 scrubbing_direction = 0;
1042 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1044 Editing::MouseMode const eff = effective_mouse_mode ();
1047 switch (item_type) {
1049 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1050 add_region_copy_drag (item, event, clicked_regionview);
1052 add_region_drag (item, event, clicked_regionview);
1054 _drags->start_grab (event);
1057 case ControlPointItem:
1058 _drags->set (new ControlPointDrag (this, item), event);
1066 switch (item_type) {
1067 case RegionViewNameHighlight:
1068 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1072 case LeftFrameHandle:
1073 case RightFrameHandle:
1074 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1078 case RegionViewName:
1079 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1093 /* relax till release */
1105 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1107 if (event->type == GDK_2BUTTON_PRESS) {
1108 _drags->mark_double_click ();
1109 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1113 if (event->type != GDK_BUTTON_PRESS) {
1117 _track_canvas->grab_focus();
1119 if (_session && _session->actively_recording()) {
1123 button_selection (item, event, item_type);
1125 if (!_drags->active () &&
1126 (Keyboard::is_delete_event (&event->button) ||
1127 Keyboard::is_context_menu_event (&event->button) ||
1128 Keyboard::is_edit_event (&event->button))) {
1130 /* handled by button release */
1134 //not rolling, range mode click + join_play_range : locate the PH here
1135 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && ARDOUR_UI::config()->get_follow_edits() ) {
1136 framepos_t where = canvas_event_sample (event);
1138 _session->request_locate (where, false);
1141 switch (event->button.button) {
1143 return button_press_handler_1 (item, event, item_type);
1147 return button_press_handler_2 (item, event, item_type);
1154 return button_press_dispatch (&event->button);
1163 Editor::button_press_dispatch (GdkEventButton* ev)
1165 /* this function is intended only for buttons 4 and above.
1168 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1169 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1173 Editor::button_release_dispatch (GdkEventButton* ev)
1175 /* this function is intended only for buttons 4 and above.
1178 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1179 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1183 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1185 framepos_t where = canvas_event_sample (event);
1186 AutomationTimeAxisView* atv = 0;
1188 _press_cursor_ctx.reset();
1190 /* no action if we're recording */
1192 if (_session && _session->actively_recording()) {
1196 bool were_dragging = false;
1198 if (!Keyboard::is_context_menu_event (&event->button)) {
1200 /* see if we're finishing a drag */
1202 if (_drags->active ()) {
1203 bool const r = _drags->end_grab (event);
1205 /* grab dragged, so do nothing else */
1209 were_dragging = true;
1212 update_region_layering_order_editor ();
1215 /* edit events get handled here */
1217 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1218 switch (item_type) {
1220 show_region_properties ();
1223 case TempoMarkerItem: {
1225 TempoMarker* tempo_marker;
1227 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1228 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1229 abort(); /*NOTREACHED*/
1232 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1233 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1234 abort(); /*NOTREACHED*/
1237 edit_tempo_marker (*tempo_marker);
1241 case MeterMarkerItem: {
1243 MeterMarker* meter_marker;
1245 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1246 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1247 abort(); /*NOTREACHED*/
1250 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1251 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1252 abort(); /*NOTREACHED*/
1254 edit_meter_marker (*meter_marker);
1258 case RegionViewName:
1259 if (clicked_regionview->name_active()) {
1260 return mouse_rename_region (item, event);
1264 case ControlPointItem:
1265 edit_control_point (item);
1274 /* context menu events get handled here */
1275 if (Keyboard::is_context_menu_event (&event->button)) {
1277 context_click_event = *event;
1279 if (!_drags->active ()) {
1281 /* no matter which button pops up the context menu, tell the menu
1282 widget to use button 1 to drive menu selection.
1285 switch (item_type) {
1287 case FadeInHandleItem:
1288 case FadeInTrimHandleItem:
1289 case StartCrossFadeItem:
1290 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1294 case FadeOutHandleItem:
1295 case FadeOutTrimHandleItem:
1296 case EndCrossFadeItem:
1297 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1300 case LeftFrameHandle:
1301 case RightFrameHandle:
1305 popup_track_context_menu (1, event->button.time, item_type, false);
1309 case RegionViewNameHighlight:
1310 case RegionViewName:
1311 popup_track_context_menu (1, event->button.time, item_type, false);
1315 popup_track_context_menu (1, event->button.time, item_type, true);
1318 case AutomationTrackItem:
1319 popup_track_context_menu (1, event->button.time, item_type, false);
1323 case RangeMarkerBarItem:
1324 case TransportMarkerBarItem:
1325 case CdMarkerBarItem:
1329 case TimecodeRulerItem:
1330 case SamplesRulerItem:
1331 case MinsecRulerItem:
1333 popup_ruler_menu (where, item_type);
1337 marker_context_menu (&event->button, item);
1340 case TempoMarkerItem:
1341 tempo_or_meter_marker_context_menu (&event->button, item);
1344 case MeterMarkerItem:
1345 tempo_or_meter_marker_context_menu (&event->button, item);
1348 case CrossfadeViewItem:
1349 popup_track_context_menu (1, event->button.time, item_type, false);
1352 case ControlPointItem:
1353 popup_control_point_context_menu (item, event);
1357 if (internal_editing()) {
1358 popup_note_context_menu (item, event);
1370 /* delete events get handled here */
1372 Editing::MouseMode const eff = effective_mouse_mode ();
1374 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1376 switch (item_type) {
1377 case TempoMarkerItem:
1378 remove_tempo_marker (item);
1381 case MeterMarkerItem:
1382 remove_meter_marker (item);
1386 remove_marker (*item, event);
1390 if (eff == MouseObject) {
1391 remove_clicked_region ();
1395 case ControlPointItem:
1396 remove_control_point (item);
1400 remove_midi_note (item, event);
1409 switch (event->button.button) {
1412 switch (item_type) {
1413 /* see comments in button_press_handler */
1414 case PlayheadCursorItem:
1417 case AutomationLineItem:
1418 case StartSelectionTrimItem:
1419 case EndSelectionTrimItem:
1423 if (!_dragging_playhead) {
1424 snap_to_with_modifier (where, event, RoundNearest, true);
1425 mouse_add_new_marker (where);
1429 case CdMarkerBarItem:
1430 if (!_dragging_playhead) {
1431 // if we get here then a dragged range wasn't done
1432 snap_to_with_modifier (where, event, RoundNearest, true);
1433 mouse_add_new_marker (where, true);
1438 if (!_dragging_playhead) {
1439 snap_to_with_modifier (where, event);
1440 mouse_add_new_tempo_event (where);
1445 if (!_dragging_playhead) {
1446 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1451 case TimecodeRulerItem:
1452 case SamplesRulerItem:
1453 case MinsecRulerItem:
1464 switch (item_type) {
1467 /* check that we didn't drag before releasing, since
1468 its really annoying to create new control
1469 points when doing this.
1471 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1472 if (!were_dragging && arv) {
1473 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1474 arv->add_gain_point_event (item, event, with_guard_points);
1480 case AutomationTrackItem: {
1481 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1482 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1484 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1495 if (scrubbing_direction == 0) {
1496 /* no drag, just a click */
1497 switch (item_type) {
1499 play_selected_region ();
1504 } else if (_session) {
1505 /* make sure we stop */
1506 _session->request_transport_speed (0.0);
1515 /* do any (de)selection operations that should occur on button release */
1516 button_selection (item, event, item_type);
1526 switch (item_type) {
1528 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1530 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1533 // Button2 click is unused
1548 // x_style_paste (where, 1.0);
1569 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1576 /* by the time we reach here, entered_regionview and entered trackview
1577 * will have already been set as appropriate. Things are done this
1578 * way because this method isn't passed a pointer to a variable type of
1579 * thing that is entered (which may or may not be canvas item).
1580 * (e.g. the actual entered regionview)
1583 choose_canvas_cursor_on_entry (item_type);
1585 switch (item_type) {
1586 case ControlPointItem:
1587 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1588 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1591 fraction = 1.0 - (cp->get_y() / cp->line().height());
1593 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1594 _verbose_cursor->show ();
1599 if (mouse_mode == MouseDraw) {
1600 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1602 line->set_outline_color (ARDOUR_UI::config()->color ("entered gain line"));
1607 case AutomationLineItem:
1608 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1609 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1611 line->set_outline_color (ARDOUR_UI::config()->color ("entered automation line"));
1616 case AutomationTrackItem:
1617 AutomationTimeAxisView* atv;
1618 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1619 clear_entered_track = false;
1620 set_entered_track (atv);
1625 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1628 entered_marker = marker;
1629 marker->set_color_rgba (ARDOUR_UI::config()->color ("entered marker"));
1631 case MeterMarkerItem:
1632 case TempoMarkerItem:
1635 case FadeInHandleItem:
1636 case FadeInTrimHandleItem:
1637 if (mouse_mode == MouseObject) {
1638 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1640 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1641 rect->set_fill_color (rv->get_fill_color());
1646 case FadeOutHandleItem:
1647 case FadeOutTrimHandleItem:
1648 if (mouse_mode == MouseObject) {
1649 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1651 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1652 rect->set_fill_color (rv->get_fill_color ());
1657 case FeatureLineItem:
1659 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1660 line->set_outline_color (0xFF0000FF);
1669 if (entered_regionview) {
1670 entered_regionview->entered();
1679 /* third pass to handle entered track status in a comprehensible way.
1682 switch (item_type) {
1684 case AutomationLineItem:
1685 case ControlPointItem:
1686 /* these do not affect the current entered track state */
1687 clear_entered_track = false;
1690 case AutomationTrackItem:
1691 /* handled above already */
1703 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1711 if (!_enter_stack.empty()) {
1712 _enter_stack.pop_back();
1715 switch (item_type) {
1716 case ControlPointItem:
1717 _verbose_cursor->hide ();
1721 case AutomationLineItem:
1722 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1724 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1726 line->set_outline_color (al->get_line_color());
1732 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1736 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1737 location_flags_changed (loc);
1740 case MeterMarkerItem:
1741 case TempoMarkerItem:
1744 case FadeInTrimHandleItem:
1745 case FadeOutTrimHandleItem:
1746 case FadeInHandleItem:
1747 case FadeOutHandleItem:
1749 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1751 rect->set_fill_color (ARDOUR_UI::config()->color ("inactive fade handle"));
1756 case AutomationTrackItem:
1759 case FeatureLineItem:
1761 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1762 line->set_outline_color (ARDOUR_UI::config()->color ("zero line"));
1774 Editor::scrub (framepos_t frame, double current_x)
1778 if (scrubbing_direction == 0) {
1780 _session->request_locate (frame, false);
1781 _session->request_transport_speed (0.1);
1782 scrubbing_direction = 1;
1786 if (last_scrub_x > current_x) {
1788 /* pointer moved to the left */
1790 if (scrubbing_direction > 0) {
1792 /* we reversed direction to go backwards */
1795 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1799 /* still moving to the left (backwards) */
1801 scrub_reversals = 0;
1802 scrub_reverse_distance = 0;
1804 delta = 0.01 * (last_scrub_x - current_x);
1805 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1809 /* pointer moved to the right */
1811 if (scrubbing_direction < 0) {
1812 /* we reversed direction to go forward */
1815 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1818 /* still moving to the right */
1820 scrub_reversals = 0;
1821 scrub_reverse_distance = 0;
1823 delta = 0.01 * (current_x - last_scrub_x);
1824 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1828 /* if there have been more than 2 opposite motion moves detected, or one that moves
1829 back more than 10 pixels, reverse direction
1832 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1834 if (scrubbing_direction > 0) {
1835 /* was forwards, go backwards */
1836 _session->request_transport_speed (-0.1);
1837 scrubbing_direction = -1;
1839 /* was backwards, go forwards */
1840 _session->request_transport_speed (0.1);
1841 scrubbing_direction = 1;
1844 scrub_reverse_distance = 0;
1845 scrub_reversals = 0;
1849 last_scrub_x = current_x;
1853 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1855 _last_motion_y = event->motion.y;
1857 if (event->motion.is_hint) {
1860 /* We call this so that MOTION_NOTIFY events continue to be
1861 delivered to the canvas. We need to do this because we set
1862 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1863 the density of the events, at the expense of a round-trip
1864 to the server. Given that this will mostly occur on cases
1865 where DISPLAY = :0.0, and given the cost of what the motion
1866 event might do, its a good tradeoff.
1869 _track_canvas->get_pointer (x, y);
1872 if (current_stepping_trackview) {
1873 /* don't keep the persistent stepped trackview if the mouse moves */
1874 current_stepping_trackview = 0;
1875 step_timeout.disconnect ();
1878 if (_session && _session->actively_recording()) {
1879 /* Sorry. no dragging stuff around while we record */
1883 update_join_object_range_location (event->motion.y);
1885 if (_drags->active ()) {
1886 return _drags->motion_handler (event, from_autoscroll);
1893 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1895 ControlPoint* control_point;
1897 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1898 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1899 abort(); /*NOTREACHED*/
1902 AutomationLine& line = control_point->line ();
1903 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
1904 /* we shouldn't remove the first or last gain point in region gain lines */
1905 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
1914 Editor::remove_control_point (ArdourCanvas::Item* item)
1916 if (!can_remove_control_point (item)) {
1920 ControlPoint* control_point;
1922 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1923 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1924 abort(); /*NOTREACHED*/
1927 control_point->line().remove_point (*control_point);
1931 Editor::edit_control_point (ArdourCanvas::Item* item)
1933 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1936 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1937 abort(); /*NOTREACHED*/
1940 ControlPointDialog d (p);
1942 if (d.run () != RESPONSE_ACCEPT) {
1946 p->line().modify_point_y (*p, d.get_y_fraction ());
1950 Editor::edit_notes (MidiRegionView* mrv)
1952 MidiRegionView::Selection const & s = mrv->selection();
1958 EditNoteDialog* d = new EditNoteDialog (mrv, s);
1961 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
1965 Editor::note_edit_done (int r, EditNoteDialog* d)
1972 Editor::visible_order_range (int* low, int* high) const
1974 *low = TimeAxisView::max_order ();
1977 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1979 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1981 if (!rtv->hidden()) {
1983 if (*high < rtv->order()) {
1984 *high = rtv->order ();
1987 if (*low > rtv->order()) {
1988 *low = rtv->order ();
1995 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1997 /* Either add to or set the set the region selection, unless
1998 this is an alignment click (control used)
2001 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2002 TimeAxisView* tv = &rv.get_time_axis_view();
2003 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2005 if (rtv && rtv->is_track()) {
2006 speed = rtv->track()->speed();
2009 framepos_t where = get_preferred_edit_position();
2013 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2015 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2017 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2019 align_region (rv.region(), End, (framepos_t) (where * speed));
2023 align_region (rv.region(), Start, (framepos_t) (where * speed));
2030 Editor::collect_new_region_view (RegionView* rv)
2032 latest_regionviews.push_back (rv);
2036 Editor::collect_and_select_new_region_view (RegionView* rv)
2039 latest_regionviews.push_back (rv);
2043 Editor::cancel_selection ()
2045 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2046 (*i)->hide_selection ();
2049 selection->clear ();
2050 clicked_selection = 0;
2054 Editor::cancel_time_selection ()
2056 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2057 (*i)->hide_selection ();
2059 selection->time.clear ();
2060 clicked_selection = 0;
2064 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2066 RegionView* rv = clicked_regionview;
2068 /* Choose action dependant on which button was pressed */
2069 switch (event->button.button) {
2071 begin_reversible_command (_("start point trim"));
2073 if (selection->selected (rv)) {
2074 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2075 i != selection->regions.by_layer().end(); ++i)
2077 if (!(*i)->region()->locked()) {
2078 (*i)->region()->clear_changes ();
2079 (*i)->region()->trim_front (new_bound);
2080 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2085 if (!rv->region()->locked()) {
2086 rv->region()->clear_changes ();
2087 rv->region()->trim_front (new_bound);
2088 _session->add_command(new StatefulDiffCommand (rv->region()));
2092 commit_reversible_command();
2096 begin_reversible_command (_("End point trim"));
2098 if (selection->selected (rv)) {
2100 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2102 if (!(*i)->region()->locked()) {
2103 (*i)->region()->clear_changes();
2104 (*i)->region()->trim_end (new_bound);
2105 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2111 if (!rv->region()->locked()) {
2112 rv->region()->clear_changes ();
2113 rv->region()->trim_end (new_bound);
2114 _session->add_command (new StatefulDiffCommand (rv->region()));
2118 commit_reversible_command();
2127 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2132 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2133 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2134 abort(); /*NOTREACHED*/
2137 Location* location = find_location_from_marker (marker, is_start);
2138 location->set_hidden (true, this);
2142 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2144 using namespace Gtkmm2ext;
2146 ArdourPrompter prompter (false);
2148 prompter.set_prompt (_("Name for region:"));
2149 prompter.set_initial_text (clicked_regionview->region()->name());
2150 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2151 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2152 prompter.show_all ();
2153 switch (prompter.run ()) {
2154 case Gtk::RESPONSE_ACCEPT:
2156 prompter.get_result(str);
2158 clicked_regionview->region()->set_name (str);
2167 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2169 /* no brushing without a useful snap setting */
2171 switch (_snap_mode) {
2173 return; /* can't work because it allows region to be placed anywhere */
2178 switch (_snap_type) {
2186 /* don't brush a copy over the original */
2188 if (pos == rv->region()->position()) {
2192 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2194 if (rtv == 0 || !rtv->is_track()) {
2198 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2199 double speed = rtv->track()->speed();
2201 playlist->clear_changes ();
2202 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2203 playlist->add_region (new_region, (framepos_t) (pos * speed));
2204 _session->add_command (new StatefulDiffCommand (playlist));
2206 // playlist is frozen, so we have to update manually XXX this is disgusting
2208 //playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2212 Editor::track_height_step_timeout ()
2214 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2215 current_stepping_trackview = 0;
2222 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2224 assert (region_view);
2226 if (!region_view->region()->playlist()) {
2230 switch (Config->get_edit_mode()) {
2232 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2235 _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2238 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2245 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2247 assert (region_view);
2249 if (!region_view->region()->playlist()) {
2253 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2257 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2259 assert (region_view);
2261 if (!region_view->region()->playlist()) {
2265 if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2269 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2272 /** Start a grab where a time range is selected, track(s) are selected, and the
2273 * user clicks and drags a region with a modifier in order to create a new region containing
2274 * the section of the clicked region that lies within the time range.
2277 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2279 if (clicked_regionview == 0) {
2283 /* lets try to create new Region for the selection */
2285 vector<boost::shared_ptr<Region> > new_regions;
2286 create_region_from_selection (new_regions);
2288 if (new_regions.empty()) {
2292 /* XXX fix me one day to use all new regions */
2294 boost::shared_ptr<Region> region (new_regions.front());
2296 /* add it to the current stream/playlist.
2298 tricky: the streamview for the track will add a new regionview. we will
2299 catch the signal it sends when it creates the regionview to
2300 set the regionview we want to then drag.
2303 latest_regionviews.clear();
2304 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2306 /* A selection grab currently creates two undo/redo operations, one for
2307 creating the new region and another for moving it.
2309 begin_reversible_command (Operations::selection_grab);
2311 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2313 playlist->clear_changes ();
2314 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2315 _session->add_command(new StatefulDiffCommand (playlist));
2319 if (latest_regionviews.empty()) {
2320 /* something went wrong */
2321 abort_reversible_command ();
2325 /* we need to deselect all other regionviews, and select this one
2326 i'm ignoring undo stuff, because the region creation will take care of it
2329 selection->set (latest_regionviews);
2331 commit_reversible_command ();
2333 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2339 if (_drags->active ()) {
2342 selection->clear ();
2348 /** Update _join_object_range_state which indicate whether we are over the top
2349 * or bottom half of a route view, used by the `join object/range' tool
2350 * mode. Coordinates in canvas space.
2353 Editor::update_join_object_range_location (double y)
2355 if (!get_smart_mode()) {
2356 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2360 JoinObjectRangeState const old = _join_object_range_state;
2362 if (mouse_mode == MouseObject) {
2363 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2364 } else if (mouse_mode == MouseRange) {
2365 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2368 if (entered_regionview) {
2370 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2371 double const c = item_space.y / entered_regionview->height();
2373 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2375 Editor::EnterContext* ctx = get_enter_context(RegionItem);
2376 if (_join_object_range_state != old && ctx) {
2377 ctx->cursor_ctx->change(which_track_cursor());
2380 } else if (entered_track) {
2382 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2384 if (entered_route_view) {
2389 entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2391 double track_height = entered_route_view->view()->child_height();
2392 if (ARDOUR_UI::config()->get_show_name_highlight()) {
2393 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2395 double const c = cy / track_height;
2399 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2401 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2405 /* Other kinds of tracks use object mode */
2406 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2409 Editor::EnterContext* ctx = get_enter_context(StreamItem);
2410 if (_join_object_range_state != old && ctx) {
2411 ctx->cursor_ctx->change(which_track_cursor());
2417 Editor::effective_mouse_mode () const
2419 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2421 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2429 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2431 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2434 e->region_view().delete_note (e->note ());
2437 /** Obtain the pointer position in canvas coordinates */
2439 Editor::get_pointer_position (double& x, double& y) const
2442 _track_canvas->get_pointer (px, py);
2443 _track_canvas->window_to_canvas (px, py, x, y);