2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include "pbd/error.h"
30 #include "pbd/enumwriter.h"
31 #include "pbd/memento_command.h"
32 #include "pbd/basename.h"
33 #include "pbd/stateful_diff_command.h"
35 #include "gtkmm2ext/bindings.h"
36 #include "gtkmm2ext/utils.h"
37 #include "gtkmm2ext/tearoff.h"
39 #include "canvas/canvas.h"
41 #include "ardour/audioregion.h"
42 #include "ardour/operations.h"
43 #include "ardour/playlist.h"
44 #include "ardour/profile.h"
45 #include "ardour/region_factory.h"
46 #include "ardour/route.h"
47 #include "ardour/session.h"
48 #include "ardour/types.h"
50 #include "ardour_ui.h"
53 #include "time_axis_view.h"
54 #include "audio_time_axis.h"
55 #include "audio_region_view.h"
56 #include "midi_region_view.h"
58 #include "streamview.h"
59 #include "region_gain_line.h"
60 #include "automation_time_axis.h"
61 #include "control_point.h"
63 #include "selection.h"
66 #include "rgb_macros.h"
67 #include "control_point_dialog.h"
68 #include "editor_drag.h"
69 #include "automation_region_view.h"
70 #include "edit_note_dialog.h"
71 #include "mouse_cursors.h"
72 #include "editor_cursors.h"
73 #include "verbose_cursor.h"
79 using namespace ARDOUR;
82 using namespace Editing;
83 using Gtkmm2ext::Keyboard;
86 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
88 /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
89 pays attentions to subwindows. this means that menu windows are ignored, and
90 if the pointer is in a menu, the return window from the call will be the
91 the regular subwindow *under* the menu.
93 this matters quite a lot if the pointer is moving around in a menu that overlaps
94 the track canvas because we will believe that we are within the track canvas
95 when we are not. therefore, we track enter/leave events for the track canvas
96 and allow that to override the result of gdk_window_get_pointer().
99 if (!within_track_canvas) {
104 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
106 if (!canvas_window) {
110 Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
112 if (!pointer_window) {
116 if (pointer_window != canvas_window) {
117 in_track_canvas = false;
121 in_track_canvas = true;
124 event.type = GDK_BUTTON_RELEASE;
128 where = window_event_sample (&event, 0, 0);
134 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
136 ArdourCanvas::Duple d;
138 if (!gdk_event_get_coords (event, &d.x, &d.y)) {
142 /* event coordinates are in window units, so convert to canvas
145 d = _track_canvas->window_to_canvas (d);
155 return pixel_to_sample (d.x);
159 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
164 /* event coordinates are already in canvas units */
166 if (!gdk_event_get_coords (event, &x, &y)) {
167 cerr << "!NO c COORDS for event type " << event->type << endl;
179 /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel
180 position is negative (as can be the case with motion events in particular),
181 the frame location is always positive.
184 return pixel_to_sample_from_event (x);
188 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
190 boost::shared_ptr<Trimmable> st = _trimmable.lock();
192 if (!st || st == t) {
198 Editor::set_current_movable (boost::shared_ptr<Movable> m)
200 boost::shared_ptr<Movable> sm = _movable.lock();
202 if (!sm || sm != m) {
208 Editor::mouse_mode_object_range_toggled()
210 MouseMode m = mouse_mode;
212 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
214 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
217 if (tact->get_active()) {
218 m = MouseObject; //Smart mode turned to ON, force editing to Object mode
221 set_mouse_mode(m, true); //call this so the button styles can get updated
225 Editor::set_mouse_mode (MouseMode m, bool force)
227 if (_drags->active ()) {
231 if (!force && m == mouse_mode) {
235 Glib::RefPtr<Action> act;
239 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
243 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
247 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
251 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
255 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
259 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
263 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
269 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
272 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
273 tact->set_active (false);
274 tact->set_active (true);
276 //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
280 Editor::mouse_mode_toggled (MouseMode m)
282 Glib::RefPtr<Action> act;
283 Glib::RefPtr<ToggleAction> tact;
287 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
291 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
295 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
299 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
303 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
307 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
311 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
317 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
320 if (!tact->get_active()) {
321 /* this was just the notification that the old mode has been
322 * left. we'll get called again with the new mode active in a
330 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
331 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
332 tact->set_active (true);
338 if (_session && mouse_mode == MouseAudition) {
339 /* stop transport and reset default speed to avoid oddness with
341 _session->request_transport_speed (0.0, true);
348 //TODO: set button styles for smart buttons
350 if ( smart_mode_action->get_active() ) {
351 if( mouse_mode == MouseObject ) { //smart active and object active
352 smart_mode_button.set_active(1);
353 smart_mode_button.set_name("smart mode button");
354 mouse_move_button.set_name("smart mode button");
355 } else { //smart active but object inactive
356 smart_mode_button.set_active(0);
357 smart_mode_button.set_name("smart mode button");
358 mouse_move_button.set_name("mouse mode button");
361 smart_mode_button.set_active(0);
362 smart_mode_button.set_name("mouse mode button");
363 mouse_move_button.set_name("mouse mode button");
367 reset_canvas_cursor ();
368 set_gain_envelope_visibility ();
370 update_time_selection_display ();
372 MouseModeChanged (); /* EMIT SIGNAL */
376 Editor::update_time_selection_display ()
378 if (smart_mode_action->get_active()) {
379 /* not sure what to do here */
380 if (mouse_mode == MouseObject) {
384 switch (mouse_mode) {
386 selection->clear_objects ();
389 selection->clear_time ();
396 Editor::step_mouse_mode (bool next)
398 switch (current_mouse_mode()) {
401 if (Profile->get_sae()) {
402 set_mouse_mode (MouseZoom);
404 set_mouse_mode (MouseRange);
407 set_mouse_mode (MouseTimeFX);
412 if (next) set_mouse_mode (MouseDraw);
413 else set_mouse_mode (MouseObject);
417 if (next) set_mouse_mode (MouseZoom);
418 else set_mouse_mode (MouseRange);
423 if (Profile->get_sae()) {
424 set_mouse_mode (MouseTimeFX);
426 set_mouse_mode (MouseGain);
429 if (Profile->get_sae()) {
430 set_mouse_mode (MouseObject);
432 set_mouse_mode (MouseDraw);
438 if (next) set_mouse_mode (MouseTimeFX);
439 else set_mouse_mode (MouseZoom);
444 set_mouse_mode (MouseAudition);
446 if (Profile->get_sae()) {
447 set_mouse_mode (MouseZoom);
449 set_mouse_mode (MouseGain);
455 if (next) set_mouse_mode (MouseObject);
456 else set_mouse_mode (MouseTimeFX);
462 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
464 if (_drags->active()) {
465 _drags->end_grab (event);
468 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
470 /* prevent reversion of edit cursor on button release */
472 pre_press_cursor = 0;
478 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
480 /* in object/audition/timefx/gain-automation mode,
481 any button press sets the selection if the object
482 can be selected. this is a bit of hack, because
483 we want to avoid this if the mouse operation is a
486 note: not dbl-click or triple-click
488 Also note that there is no region selection in internal edit mode, otherwise
489 for operations operating on the selection (e.g. cut) it is not obvious whether
490 to cut notes or regions.
493 MouseMode eff_mouse_mode = mouse_mode;
495 if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
496 /* context clicks are always about object properties, even if
497 we're in range mode within smart mode.
499 eff_mouse_mode = MouseObject;
502 if (((mouse_mode != MouseObject) &&
503 (mouse_mode != MouseAudition || item_type != RegionItem) &&
504 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
505 (mouse_mode != MouseGain) &&
506 (mouse_mode != MouseDraw)) ||
507 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
508 (internal_editing() && mouse_mode != MouseTimeFX)) {
513 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
515 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
517 /* almost no selection action on modified button-2 or button-3 events */
519 if (item_type != RegionItem && event->button.button != 2) {
525 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
526 bool press = (event->type == GDK_BUTTON_PRESS);
531 if (eff_mouse_mode != MouseRange) {
532 set_selected_regionview_from_click (press, op);
534 /* don't change the selection unless the
535 clicked track is not currently selected. if
536 so, "collapse" the selection to just this
539 if (!selection->selected (clicked_axisview)) {
540 set_selected_track_as_side_effect (Selection::Set);
544 if (eff_mouse_mode != MouseRange) {
545 set_selected_regionview_from_click (press, op);
550 case RegionViewNameHighlight:
552 case LeftFrameHandle:
553 case RightFrameHandle:
554 if (eff_mouse_mode != MouseRange) {
555 set_selected_regionview_from_click (press, op);
556 } else if (event->type == GDK_BUTTON_PRESS) {
557 set_selected_track_as_side_effect (op);
561 case FadeInHandleItem:
562 case FadeInTrimHandleItem:
564 case FadeOutHandleItem:
565 case FadeOutTrimHandleItem:
567 case StartCrossFadeItem:
568 case EndCrossFadeItem:
569 if (eff_mouse_mode != MouseRange) {
570 cerr << "Should be setting selected regionview\n";
571 set_selected_regionview_from_click (press, op);
572 } else if (event->type == GDK_BUTTON_PRESS) {
573 set_selected_track_as_side_effect (op);
577 case ControlPointItem:
578 set_selected_track_as_side_effect (op);
579 if (eff_mouse_mode != MouseRange) {
580 set_selected_control_point_from_click (press, op);
585 /* for context click, select track */
586 if (event->button.button == 3) {
587 selection->clear_tracks ();
588 set_selected_track_as_side_effect (op);
592 case AutomationTrackItem:
593 set_selected_track_as_side_effect (op);
602 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
604 /* single mouse clicks on any of these item types operate
605 independent of mouse mode, mostly because they are
606 not on the main track canvas or because we want
611 case PlayheadCursorItem:
612 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
616 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
617 hide_marker (item, event);
619 _drags->set (new MarkerDrag (this, item), event);
623 case TempoMarkerItem:
625 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
628 new TempoMarkerDrag (
631 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
638 case MeterMarkerItem:
640 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
643 new MeterMarkerDrag (
646 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
654 _drags->set (new VideoTimeLineDrag (this, item), event);
661 case TimecodeRulerItem:
662 case SamplesRulerItem:
663 case MinsecRulerItem:
665 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
666 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
672 case RangeMarkerBarItem:
673 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
674 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
676 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
681 case CdMarkerBarItem:
682 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
683 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
685 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
690 case TransportMarkerBarItem:
691 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
692 _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
694 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
703 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
704 /* special case: allow trim of range selections in joined object mode;
705 in theory eff should equal MouseRange in this case, but it doesn't
706 because entering the range selection canvas item results in entered_regionview
707 being set to 0, so update_join_object_range_location acts as if we aren't
710 if (item_type == StartSelectionTrimItem) {
711 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
712 } else if (item_type == EndSelectionTrimItem) {
713 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
717 Editing::MouseMode eff = effective_mouse_mode ();
719 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
720 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
724 /* there is no Range mode when in internal edit mode */
725 if (eff == MouseRange && internal_editing()) {
732 case StartSelectionTrimItem:
733 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
736 case EndSelectionTrimItem:
737 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
741 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
742 start_selection_grab (item, event);
744 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
745 /* grab selection for moving */
746 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
748 double const y = event->button.y;
749 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
751 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
752 if ( get_smart_mode() && atv) {
753 /* smart "join" mode: drag automation */
754 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
756 /* this was debated, but decided the more common action was to
757 make a new selection */
758 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
765 if (internal_editing()) {
766 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
767 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
771 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
772 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
774 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
780 case RegionViewNameHighlight:
781 if (!clicked_regionview->region()->locked()) {
782 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
788 if (!internal_editing()) {
789 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
790 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
792 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
802 /* Existing note: allow trimming/motion */
803 if (internal_editing()) {
804 /* trim notes if we're in internal edit mode and near the ends of the note */
805 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
807 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
808 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
810 _drags->set (new NoteDrag (this, item), event);
816 if (internal_editing()) {
817 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
818 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
832 /* Existing note: allow trimming/motion */
833 if (internal_editing()) {
834 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
836 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
837 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
839 _drags->set (new NoteDrag (this, item), event);
849 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
850 event->type == GDK_BUTTON_PRESS) {
852 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
854 } else if (event->type == GDK_BUTTON_PRESS) {
857 case FadeInHandleItem:
859 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
863 case FadeOutHandleItem:
865 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
869 case StartCrossFadeItem:
870 case EndCrossFadeItem:
871 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
872 // if (!clicked_regionview->region()->locked()) {
873 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
878 case FeatureLineItem:
880 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
881 remove_transient(item);
885 _drags->set (new FeatureLineDrag (this, item), event);
891 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
892 /* click on an automation region view; do nothing here and let the ARV's signal handler
898 if (internal_editing ()) {
902 /* click on a normal region view */
903 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
904 add_region_copy_drag (item, event, clicked_regionview);
905 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
906 add_region_brush_drag (item, event, clicked_regionview);
908 add_region_drag (item, event, clicked_regionview);
912 // if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
913 // _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
916 _drags->start_grab (event);
920 case RegionViewNameHighlight:
921 case LeftFrameHandle:
922 case RightFrameHandle:
923 if (!clicked_regionview->region()->locked()) {
924 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
929 case FadeInTrimHandleItem:
930 case FadeOutTrimHandleItem:
931 if (!clicked_regionview->region()->locked()) {
932 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
939 /* rename happens on edit clicks */
940 if (clicked_regionview->get_name_highlight()) {
941 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
947 case ControlPointItem:
948 _drags->set (new ControlPointDrag (this, item), event);
952 case AutomationLineItem:
953 _drags->set (new LineDrag (this, item), event);
958 if (internal_editing()) {
959 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
960 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
964 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
968 case AutomationTrackItem:
970 TimeAxisView* parent = clicked_axisview->get_parent ();
971 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
973 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
975 RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
977 boost::shared_ptr<Playlist> pl = p->track()->playlist ();
978 if (pl->n_regions() == 0) {
979 /* Parent has no regions; create one so that we have somewhere to put automation */
980 _drags->set (new RegionCreateDrag (this, item, parent), event);
982 /* See if there's a region before the click that we can extend, and extend it if so */
983 framepos_t const t = canvas_event_sample (event);
984 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
986 _drags->set (new RegionCreateDrag (this, item, parent), event);
988 prev->set_length (t - prev->position ());
992 /* rubberband drag to select automation points */
993 _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1000 if ( get_smart_mode() ) {
1001 /* we're in "smart" joined mode, and we've clicked on a Selection */
1002 double const y = event->button.y;
1003 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1005 /* if we're over an automation track, start a drag of its data */
1006 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1008 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1011 /* if we're over a track and a region, and in the `object' part of a region,
1012 put a selection around the region and drag both
1014 /* RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1015 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1016 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1018 boost::shared_ptr<Playlist> pl = t->playlist ();
1021 boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_sample (event));
1023 RegionView* rv = rtv->view()->find_view (r);
1024 clicked_selection = select_range (rv->region()->position(),
1025 rv->region()->last_frame()+1);
1026 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1027 list<RegionView*> rvs;
1029 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1030 _drags->start_grab (event);
1054 switch (item_type) {
1056 _drags->set (new LineDrag (this, item), event);
1059 case ControlPointItem:
1060 _drags->set (new ControlPointDrag (this, item), event);
1066 AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1068 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1069 _drags->start_grab (event);
1075 case AutomationLineItem:
1076 _drags->set (new LineDrag (this, item), event);
1086 if (event->type == GDK_BUTTON_PRESS) {
1087 _drags->set (new MouseZoomDrag (this, item), event);
1094 if (internal_editing() && item_type == NoteItem ) {
1095 /* drag notes if we're in internal edit mode */
1096 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1098 if (cn->big_enough_to_trim()) {
1099 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1102 } else if (clicked_regionview) {
1104 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1110 _drags->set (new ScrubDrag (this, item), event);
1111 scrub_reversals = 0;
1112 scrub_reverse_distance = 0;
1113 last_scrub_x = event->button.x;
1114 scrubbing_direction = 0;
1115 push_canvas_cursor (_cursors->transparent);
1127 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1129 Editing::MouseMode const eff = effective_mouse_mode ();
1132 switch (item_type) {
1134 if (internal_editing ()) {
1135 /* no region drags in internal edit mode */
1139 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1140 add_region_copy_drag (item, event, clicked_regionview);
1142 add_region_drag (item, event, clicked_regionview);
1144 _drags->start_grab (event);
1147 case ControlPointItem:
1148 _drags->set (new ControlPointDrag (this, item), event);
1156 switch (item_type) {
1157 case RegionViewNameHighlight:
1158 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1162 case LeftFrameHandle:
1163 case RightFrameHandle:
1164 if (!internal_editing ()) {
1165 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1170 case RegionViewName:
1171 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1185 /* relax till release */
1191 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1192 temporal_zoom_to_frame (false, canvas_event_sample (event));
1194 temporal_zoom_to_frame (true, canvas_event_sample(event));
1207 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1209 if (event->type == GDK_2BUTTON_PRESS) {
1210 _drags->mark_double_click ();
1211 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1215 if (event->type != GDK_BUTTON_PRESS) {
1219 pre_press_cursor = current_canvas_cursor;
1221 _track_canvas->grab_focus();
1223 if (_session && _session->actively_recording()) {
1227 if (internal_editing()) {
1228 bool leave_internal_edit_mode = false;
1230 switch (item_type) {
1235 if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1236 leave_internal_edit_mode = true;
1240 case PlayheadCursorItem:
1242 case TempoMarkerItem:
1243 case MeterMarkerItem:
1247 case RangeMarkerBarItem:
1248 case CdMarkerBarItem:
1249 case TransportMarkerBarItem:
1251 case TimecodeRulerItem:
1252 case SamplesRulerItem:
1253 case MinsecRulerItem:
1255 /* button press on these items never does anything to
1256 change the editing mode.
1264 if (leave_internal_edit_mode) {
1265 ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1269 button_selection (item, event, item_type);
1271 if (!_drags->active () &&
1272 (Keyboard::is_delete_event (&event->button) ||
1273 Keyboard::is_context_menu_event (&event->button) ||
1274 Keyboard::is_edit_event (&event->button))) {
1276 /* handled by button release */
1280 //not rolling, range mode click + join_play_range : locate the PH here
1281 if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
1282 framepos_t where = canvas_event_sample (event);
1284 _session->request_locate (where, false);
1287 switch (event->button.button) {
1289 return button_press_handler_1 (item, event, item_type);
1293 return button_press_handler_2 (item, event, item_type);
1300 return button_press_dispatch (&event->button);
1309 Editor::button_press_dispatch (GdkEventButton* ev)
1311 /* this function is intended only for buttons 4 and above.
1314 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1315 return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1319 Editor::button_release_dispatch (GdkEventButton* ev)
1321 /* this function is intended only for buttons 4 and above.
1324 Gtkmm2ext::MouseButton b (ev->state, ev->button);
1325 return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1329 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1331 framepos_t where = canvas_event_sample (event);
1332 AutomationTimeAxisView* atv = 0;
1334 if (pre_press_cursor) {
1335 set_canvas_cursor (pre_press_cursor);
1336 pre_press_cursor = 0;
1339 /* no action if we're recording */
1341 if (_session && _session->actively_recording()) {
1345 /* see if we're finishing a drag */
1347 bool were_dragging = false;
1348 if (_drags->active ()) {
1349 bool const r = _drags->end_grab (event);
1351 /* grab dragged, so do nothing else */
1355 were_dragging = true;
1358 update_region_layering_order_editor ();
1360 /* edit events get handled here */
1362 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1363 switch (item_type) {
1365 show_region_properties ();
1368 case TempoMarkerItem: {
1370 TempoMarker* tempo_marker;
1372 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1373 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1377 if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1378 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1382 edit_tempo_marker (*tempo_marker);
1386 case MeterMarkerItem: {
1388 MeterMarker* meter_marker;
1390 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1391 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1395 if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1396 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1399 edit_meter_marker (*meter_marker);
1403 case RegionViewName:
1404 if (clicked_regionview->name_active()) {
1405 return mouse_rename_region (item, event);
1409 case ControlPointItem:
1410 edit_control_point (item);
1419 /* context menu events get handled here */
1420 if (Keyboard::is_context_menu_event (&event->button)) {
1422 context_click_event = *event;
1424 if (!_drags->active ()) {
1426 /* no matter which button pops up the context menu, tell the menu
1427 widget to use button 1 to drive menu selection.
1430 switch (item_type) {
1432 case FadeInHandleItem:
1433 case FadeInTrimHandleItem:
1434 case StartCrossFadeItem:
1435 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1439 case FadeOutHandleItem:
1440 case FadeOutTrimHandleItem:
1441 case EndCrossFadeItem:
1442 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1446 popup_track_context_menu (1, event->button.time, item_type, false);
1450 case RegionViewNameHighlight:
1451 case LeftFrameHandle:
1452 case RightFrameHandle:
1453 case RegionViewName:
1454 popup_track_context_menu (1, event->button.time, item_type, false);
1458 popup_track_context_menu (1, event->button.time, item_type, true);
1461 case AutomationTrackItem:
1462 popup_track_context_menu (1, event->button.time, item_type, false);
1466 case RangeMarkerBarItem:
1467 case TransportMarkerBarItem:
1468 case CdMarkerBarItem:
1472 case TimecodeRulerItem:
1473 case SamplesRulerItem:
1474 case MinsecRulerItem:
1476 popup_ruler_menu (where, item_type);
1480 marker_context_menu (&event->button, item);
1483 case TempoMarkerItem:
1484 tempo_or_meter_marker_context_menu (&event->button, item);
1487 case MeterMarkerItem:
1488 tempo_or_meter_marker_context_menu (&event->button, item);
1491 case CrossfadeViewItem:
1492 popup_track_context_menu (1, event->button.time, item_type, false);
1495 case ControlPointItem:
1496 popup_control_point_context_menu (item, event);
1507 /* delete events get handled here */
1509 Editing::MouseMode const eff = effective_mouse_mode ();
1511 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1513 switch (item_type) {
1514 case TempoMarkerItem:
1515 remove_tempo_marker (item);
1518 case MeterMarkerItem:
1519 remove_meter_marker (item);
1523 remove_marker (*item, event);
1527 if (eff == MouseObject) {
1528 remove_clicked_region ();
1532 case ControlPointItem:
1533 remove_control_point (item);
1537 remove_midi_note (item, event);
1546 switch (event->button.button) {
1549 switch (item_type) {
1550 /* see comments in button_press_handler */
1551 case PlayheadCursorItem:
1554 case AutomationLineItem:
1555 case StartSelectionTrimItem:
1556 case EndSelectionTrimItem:
1560 if (!_dragging_playhead) {
1561 snap_to_with_modifier (where, event, 0, true);
1562 mouse_add_new_marker (where);
1566 case CdMarkerBarItem:
1567 if (!_dragging_playhead) {
1568 // if we get here then a dragged range wasn't done
1569 snap_to_with_modifier (where, event, 0, true);
1570 mouse_add_new_marker (where, true);
1575 if (!_dragging_playhead) {
1576 snap_to_with_modifier (where, event);
1577 mouse_add_new_tempo_event (where);
1582 if (!_dragging_playhead) {
1583 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1588 case TimecodeRulerItem:
1589 case SamplesRulerItem:
1590 case MinsecRulerItem:
1601 switch (item_type) {
1602 case AutomationTrackItem:
1603 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1605 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1606 atv->add_automation_event (event, where, event->button.y, with_guard_points);
1616 switch (item_type) {
1619 /* check that we didn't drag before releasing, since
1620 its really annoying to create new control
1621 points when doing this.
1623 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1624 if (!were_dragging && arv) {
1625 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1626 arv->add_gain_point_event (item, event, with_guard_points);
1632 case AutomationTrackItem: {
1633 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1634 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1635 add_automation_event (event, where, event->button.y, with_guard_points);
1645 pop_canvas_cursor ();
1646 if (scrubbing_direction == 0) {
1647 /* no drag, just a click */
1648 switch (item_type) {
1650 play_selected_region ();
1656 /* make sure we stop */
1657 _session->request_transport_speed (0.0);
1666 /* do any (de)selection operations that should occur on button release */
1667 button_selection (item, event, item_type);
1676 switch (item_type) {
1678 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1680 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1683 // Button2 click is unused
1698 // x_style_paste (where, 1.0);
1719 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1726 /* by the time we reach here, entered_regionview and entered trackview
1727 * will have already been set as appropriate. Things are done this
1728 * way because this method isn't passed a pointer to a variable type of
1729 * thing that is entered (which may or may not be canvas item).
1730 * (e.g. the actual entered regionview)
1733 choose_canvas_cursor_on_entry (&event->crossing, item_type);
1735 switch (item_type) {
1736 case ControlPointItem:
1737 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1738 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1741 fraction = 1.0 - (cp->get_y() / cp->line().height());
1743 _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1744 _verbose_cursor->show ();
1749 if (mouse_mode == MouseGain) {
1750 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1752 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1757 case AutomationLineItem:
1758 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1759 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1761 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1766 case AutomationTrackItem:
1767 AutomationTimeAxisView* atv;
1768 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1769 clear_entered_track = false;
1770 set_entered_track (atv);
1775 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1778 entered_marker = marker;
1779 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
1781 case MeterMarkerItem:
1782 case TempoMarkerItem:
1785 case FadeInHandleItem:
1786 case FadeInTrimHandleItem:
1787 if (mouse_mode == MouseObject && !internal_editing()) {
1788 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1790 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1791 rect->set_fill_color (rv->get_fill_color());
1796 case FadeOutHandleItem:
1797 case FadeOutTrimHandleItem:
1798 if (mouse_mode == MouseObject && !internal_editing()) {
1799 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1801 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1802 rect->set_fill_color (rv->get_fill_color ());
1807 case FeatureLineItem:
1809 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1810 line->set_outline_color (0xFF0000FF);
1821 /* third pass to handle entered track status in a comprehensible way.
1824 switch (item_type) {
1826 case AutomationLineItem:
1827 case ControlPointItem:
1828 /* these do not affect the current entered track state */
1829 clear_entered_track = false;
1832 case AutomationTrackItem:
1833 /* handled above already */
1845 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1853 switch (item_type) {
1854 case ControlPointItem:
1855 _verbose_cursor->hide ();
1859 case AutomationLineItem:
1860 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1862 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1864 line->set_outline_color (al->get_line_color());
1870 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1874 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1875 location_flags_changed (loc, this);
1878 case MeterMarkerItem:
1879 case TempoMarkerItem:
1882 case FadeInTrimHandleItem:
1883 case FadeOutTrimHandleItem:
1884 case FadeInHandleItem:
1885 case FadeOutHandleItem:
1887 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1889 rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
1894 case AutomationTrackItem:
1897 case FeatureLineItem:
1899 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1900 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
1912 Editor::scrub (framepos_t frame, double current_x)
1916 if (scrubbing_direction == 0) {
1918 _session->request_locate (frame, false);
1919 _session->request_transport_speed (0.1);
1920 scrubbing_direction = 1;
1924 if (last_scrub_x > current_x) {
1926 /* pointer moved to the left */
1928 if (scrubbing_direction > 0) {
1930 /* we reversed direction to go backwards */
1933 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1937 /* still moving to the left (backwards) */
1939 scrub_reversals = 0;
1940 scrub_reverse_distance = 0;
1942 delta = 0.01 * (last_scrub_x - current_x);
1943 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1947 /* pointer moved to the right */
1949 if (scrubbing_direction < 0) {
1950 /* we reversed direction to go forward */
1953 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1956 /* still moving to the right */
1958 scrub_reversals = 0;
1959 scrub_reverse_distance = 0;
1961 delta = 0.01 * (current_x - last_scrub_x);
1962 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1966 /* if there have been more than 2 opposite motion moves detected, or one that moves
1967 back more than 10 pixels, reverse direction
1970 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1972 if (scrubbing_direction > 0) {
1973 /* was forwards, go backwards */
1974 _session->request_transport_speed (-0.1);
1975 scrubbing_direction = -1;
1977 /* was backwards, go forwards */
1978 _session->request_transport_speed (0.1);
1979 scrubbing_direction = 1;
1982 scrub_reverse_distance = 0;
1983 scrub_reversals = 0;
1987 last_scrub_x = current_x;
1991 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1993 _last_motion_y = event->motion.y;
1995 if (event->motion.is_hint) {
1998 /* We call this so that MOTION_NOTIFY events continue to be
1999 delivered to the canvas. We need to do this because we set
2000 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2001 the density of the events, at the expense of a round-trip
2002 to the server. Given that this will mostly occur on cases
2003 where DISPLAY = :0.0, and given the cost of what the motion
2004 event might do, its a good tradeoff.
2007 _track_canvas->get_pointer (x, y);
2010 if (current_stepping_trackview) {
2011 /* don't keep the persistent stepped trackview if the mouse moves */
2012 current_stepping_trackview = 0;
2013 step_timeout.disconnect ();
2016 if (_session && _session->actively_recording()) {
2017 /* Sorry. no dragging stuff around while we record */
2021 update_join_object_range_location (event->motion.y);
2023 if (_drags->active ()) {
2024 return _drags->motion_handler (event, from_autoscroll);
2031 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2033 ControlPoint* control_point;
2035 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2036 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2040 AutomationLine& line = control_point->line ();
2041 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2042 /* we shouldn't remove the first or last gain point in region gain lines */
2043 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2052 Editor::remove_control_point (ArdourCanvas::Item* item)
2054 if (!can_remove_control_point (item)) {
2058 ControlPoint* control_point;
2060 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2061 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2065 control_point->line().remove_point (*control_point);
2069 Editor::edit_control_point (ArdourCanvas::Item* item)
2071 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2074 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2078 ControlPointDialog d (p);
2081 if (d.run () != RESPONSE_ACCEPT) {
2085 p->line().modify_point_y (*p, d.get_y_fraction ());
2089 Editor::edit_notes (TimeAxisViewItem& tavi)
2091 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2097 MidiRegionView::Selection const & s = mrv->selection();
2103 EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2107 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2111 Editor::note_edit_done (int r, EditNoteDialog* d)
2118 Editor::visible_order_range (int* low, int* high) const
2120 *low = TimeAxisView::max_order ();
2123 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2125 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2127 if (!rtv->hidden()) {
2129 if (*high < rtv->order()) {
2130 *high = rtv->order ();
2133 if (*low > rtv->order()) {
2134 *low = rtv->order ();
2141 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2143 /* Either add to or set the set the region selection, unless
2144 this is an alignment click (control used)
2147 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2148 TimeAxisView* tv = &rv.get_time_axis_view();
2149 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2151 if (rtv && rtv->is_track()) {
2152 speed = rtv->track()->speed();
2155 framepos_t where = get_preferred_edit_position();
2159 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2161 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2163 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2165 align_region (rv.region(), End, (framepos_t) (where * speed));
2169 align_region (rv.region(), Start, (framepos_t) (where * speed));
2176 Editor::collect_new_region_view (RegionView* rv)
2178 latest_regionviews.push_back (rv);
2182 Editor::collect_and_select_new_region_view (RegionView* rv)
2185 latest_regionviews.push_back (rv);
2189 Editor::cancel_selection ()
2191 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2192 (*i)->hide_selection ();
2195 selection->clear ();
2196 clicked_selection = 0;
2200 Editor::cancel_time_selection ()
2202 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2203 (*i)->hide_selection ();
2205 selection->time.clear ();
2206 clicked_selection = 0;
2210 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2212 RegionView* rv = clicked_regionview;
2214 /* Choose action dependant on which button was pressed */
2215 switch (event->button.button) {
2217 begin_reversible_command (_("start point trim"));
2219 if (selection->selected (rv)) {
2220 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2221 i != selection->regions.by_layer().end(); ++i)
2223 if (!(*i)->region()->locked()) {
2224 (*i)->region()->clear_changes ();
2225 (*i)->region()->trim_front (new_bound);
2226 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2231 if (!rv->region()->locked()) {
2232 rv->region()->clear_changes ();
2233 rv->region()->trim_front (new_bound);
2234 _session->add_command(new StatefulDiffCommand (rv->region()));
2238 commit_reversible_command();
2242 begin_reversible_command (_("End point trim"));
2244 if (selection->selected (rv)) {
2246 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2248 if (!(*i)->region()->locked()) {
2249 (*i)->region()->clear_changes();
2250 (*i)->region()->trim_end (new_bound);
2251 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2257 if (!rv->region()->locked()) {
2258 rv->region()->clear_changes ();
2259 rv->region()->trim_end (new_bound);
2260 _session->add_command (new StatefulDiffCommand (rv->region()));
2264 commit_reversible_command();
2273 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2278 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2279 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2283 Location* location = find_location_from_marker (marker, is_start);
2284 location->set_hidden (true, this);
2289 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2291 double x1 = sample_to_pixel (start);
2292 double x2 = sample_to_pixel (end);
2293 double y2 = _full_canvas_height - 1.0;
2295 zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2300 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2302 using namespace Gtkmm2ext;
2304 ArdourPrompter prompter (false);
2306 prompter.set_prompt (_("Name for region:"));
2307 prompter.set_initial_text (clicked_regionview->region()->name());
2308 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2309 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2310 prompter.show_all ();
2311 switch (prompter.run ()) {
2312 case Gtk::RESPONSE_ACCEPT:
2314 prompter.get_result(str);
2316 clicked_regionview->region()->set_name (str);
2325 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2327 /* no brushing without a useful snap setting */
2329 switch (_snap_mode) {
2331 return; /* can't work because it allows region to be placed anywhere */
2336 switch (_snap_type) {
2344 /* don't brush a copy over the original */
2346 if (pos == rv->region()->position()) {
2350 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2352 if (rtv == 0 || !rtv->is_track()) {
2356 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2357 double speed = rtv->track()->speed();
2359 playlist->clear_changes ();
2360 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2361 playlist->add_region (new_region, (framepos_t) (pos * speed));
2362 _session->add_command (new StatefulDiffCommand (playlist));
2364 // playlist is frozen, so we have to update manually XXX this is disgusting
2366 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2370 Editor::track_height_step_timeout ()
2372 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2373 current_stepping_trackview = 0;
2380 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2382 assert (region_view);
2384 if (!region_view->region()->playlist()) {
2388 if (Config->get_edit_mode() == Splice) {
2389 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2391 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2396 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2398 assert (region_view);
2400 if (!region_view->region()->playlist()) {
2404 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2408 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2410 assert (region_view);
2412 if (!region_view->region()->playlist()) {
2416 if (Config->get_edit_mode() == Splice) {
2420 _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2422 begin_reversible_command (Operations::drag_region_brush);
2425 /** Start a grab where a time range is selected, track(s) are selected, and the
2426 * user clicks and drags a region with a modifier in order to create a new region containing
2427 * the section of the clicked region that lies within the time range.
2430 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2432 if (clicked_regionview == 0) {
2436 /* lets try to create new Region for the selection */
2438 vector<boost::shared_ptr<Region> > new_regions;
2439 create_region_from_selection (new_regions);
2441 if (new_regions.empty()) {
2445 /* XXX fix me one day to use all new regions */
2447 boost::shared_ptr<Region> region (new_regions.front());
2449 /* add it to the current stream/playlist.
2451 tricky: the streamview for the track will add a new regionview. we will
2452 catch the signal it sends when it creates the regionview to
2453 set the regionview we want to then drag.
2456 latest_regionviews.clear();
2457 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2459 /* A selection grab currently creates two undo/redo operations, one for
2460 creating the new region and another for moving it.
2463 begin_reversible_command (Operations::selection_grab);
2465 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2467 playlist->clear_changes ();
2468 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2469 _session->add_command(new StatefulDiffCommand (playlist));
2471 commit_reversible_command ();
2475 if (latest_regionviews.empty()) {
2476 /* something went wrong */
2480 /* we need to deselect all other regionviews, and select this one
2481 i'm ignoring undo stuff, because the region creation will take care of it
2483 selection->set (latest_regionviews);
2485 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2491 if (_drags->active ()) {
2494 selection->clear ();
2499 Editor::set_internal_edit (bool yn)
2501 if (_internal_editing == yn) {
2505 _internal_editing = yn;
2508 pre_internal_mouse_mode = mouse_mode;
2509 pre_internal_snap_type = _snap_type;
2510 pre_internal_snap_mode = _snap_mode;
2512 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2513 (*i)->enter_internal_edit_mode ();
2516 set_snap_to (internal_snap_type);
2517 set_snap_mode (internal_snap_mode);
2521 internal_snap_mode = _snap_mode;
2522 internal_snap_type = _snap_type;
2524 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2525 (*i)->leave_internal_edit_mode ();
2528 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2529 /* we were drawing .. flip back to something sensible */
2530 set_mouse_mode (pre_internal_mouse_mode);
2533 set_snap_to (pre_internal_snap_type);
2534 set_snap_mode (pre_internal_snap_mode);
2537 reset_canvas_cursor ();
2540 /** Update _join_object_range_state which indicate whether we are over the top
2541 * or bottom half of a route view, used by the `join object/range' tool
2542 * mode. Coordinates in canvas space.
2545 Editor::update_join_object_range_location (double y)
2547 if (_internal_editing || !get_smart_mode()) {
2548 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2552 JoinObjectRangeState const old = _join_object_range_state;
2554 if (mouse_mode == MouseObject) {
2555 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2556 } else if (mouse_mode == MouseRange) {
2557 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2561 if (entered_regionview) {
2563 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2564 double const c = item_space.y / entered_regionview->height();
2566 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2568 if (_join_object_range_state != old) {
2569 set_canvas_cursor (which_track_cursor ());
2572 } else if (entered_track) {
2574 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2576 if (entered_route_view) {
2577 /* track/bus ... but not in a region ... use range mode */
2578 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2579 if (_join_object_range_state != old) {
2580 set_canvas_cursor (which_track_cursor ());
2583 /* Other kinds of tracks use object mode */
2584 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2585 if (_join_object_range_state != old) {
2586 set_canvas_cursor (which_track_cursor ());
2593 Editor::effective_mouse_mode () const
2595 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2597 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2605 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2607 NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2610 e->region_view().delete_note (e->note ());
2613 /** Obtain the pointer position in canvas coordinates */
2615 Editor::get_pointer_position (double& x, double& y) const
2618 _track_canvas->get_pointer (px, py);
2619 _track_canvas->window_to_canvas (px, py, x, y);