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.
28 #include "pbd/error.h"
29 #include "pbd/enumwriter.h"
30 #include <gtkmm2ext/utils.h>
31 #include <gtkmm2ext/tearoff.h>
32 #include "pbd/memento_command.h"
33 #include "pbd/basename.h"
34 #include "pbd/stateful_diff_command.h"
36 #include "ardour_ui.h"
39 #include "time_axis_view.h"
40 #include "audio_time_axis.h"
41 #include "audio_region_view.h"
42 #include "midi_region_view.h"
44 #include "streamview.h"
45 #include "region_gain_line.h"
46 #include "automation_time_axis.h"
47 #include "control_point.h"
50 #include "selection.h"
53 #include "rgb_macros.h"
54 #include "control_point_dialog.h"
55 #include "editor_drag.h"
57 #include "ardour/types.h"
58 #include "ardour/profile.h"
59 #include "ardour/route.h"
60 #include "ardour/audio_track.h"
61 #include "ardour/audio_diskstream.h"
62 #include "ardour/midi_diskstream.h"
63 #include "ardour/playlist.h"
64 #include "ardour/audioplaylist.h"
65 #include "ardour/audioregion.h"
66 #include "ardour/midi_region.h"
67 #include "ardour/dB.h"
68 #include "ardour/utils.h"
69 #include "ardour/region_factory.h"
70 #include "ardour/source_factory.h"
71 #include "ardour/session.h"
78 using namespace ARDOUR;
81 using namespace Editing;
82 using Gtkmm2ext::Keyboard;
85 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
89 Gdk::ModifierType mask;
90 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
91 Glib::RefPtr<const Gdk::Window> pointer_window;
97 pointer_window = canvas_window->get_pointer (x, y, mask);
99 if (pointer_window == track_canvas->get_bin_window()) {
102 in_track_canvas = true;
105 in_track_canvas = false;
110 event.type = GDK_BUTTON_RELEASE;
114 where = event_frame (&event, 0, 0);
119 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
133 switch (event->type) {
134 case GDK_BUTTON_RELEASE:
135 case GDK_BUTTON_PRESS:
136 case GDK_2BUTTON_PRESS:
137 case GDK_3BUTTON_PRESS:
138 *pcx = event->button.x;
139 *pcy = event->button.y;
140 _trackview_group->w2i(*pcx, *pcy);
142 case GDK_MOTION_NOTIFY:
143 *pcx = event->motion.x;
144 *pcy = event->motion.y;
145 _trackview_group->w2i(*pcx, *pcy);
147 case GDK_ENTER_NOTIFY:
148 case GDK_LEAVE_NOTIFY:
149 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
152 case GDK_KEY_RELEASE:
153 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
156 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
160 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
161 position is negative (as can be the case with motion events in particular),
162 the frame location is always positive.
165 return pixel_to_frame (*pcx);
169 Editor::which_grabber_cursor ()
171 Gdk::Cursor* c = grabber_cursor;
173 if (_internal_editing) {
174 switch (mouse_mode) {
176 c = midi_pencil_cursor;
184 c = midi_resize_cursor;
193 switch (_edit_point) {
195 c = grabber_edit_point_cursor;
206 Editor::set_canvas_cursor ()
208 if (_internal_editing) {
210 switch (mouse_mode) {
212 current_canvas_cursor = midi_pencil_cursor;
216 current_canvas_cursor = which_grabber_cursor();
220 current_canvas_cursor = midi_resize_cursor;
229 switch (mouse_mode) {
231 current_canvas_cursor = selector_cursor;
235 current_canvas_cursor = which_grabber_cursor();
239 current_canvas_cursor = cross_hair_cursor;
243 current_canvas_cursor = zoom_cursor;
247 current_canvas_cursor = time_fx_cursor; // just use playhead
251 current_canvas_cursor = speaker_cursor;
256 switch (_join_object_range_state) {
257 case JOIN_OBJECT_RANGE_NONE:
259 case JOIN_OBJECT_RANGE_OBJECT:
260 current_canvas_cursor = which_grabber_cursor ();
262 case JOIN_OBJECT_RANGE_RANGE:
263 current_canvas_cursor = selector_cursor;
268 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
273 Editor::set_mouse_mode (MouseMode m, bool force)
275 if (_drags->active ()) {
279 if (!force && m == mouse_mode) {
283 Glib::RefPtr<Action> act;
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-gain"));
299 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
303 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
307 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
313 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
316 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
317 tact->set_active (false);
318 tact->set_active (true);
322 Editor::mouse_mode_toggled (MouseMode m)
328 if (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
330 /* in all modes except range and joined object/range, hide the range selection,
331 show the object (region) selection.
334 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
335 (*i)->set_should_show_selection (true);
337 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
338 (*i)->hide_selection ();
344 in range or object/range mode, show the range selection.
347 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
348 (*i)->show_selection (selection->time);
352 set_canvas_cursor ();
356 Editor::step_mouse_mode (bool next)
358 switch (current_mouse_mode()) {
361 if (Profile->get_sae()) {
362 set_mouse_mode (MouseZoom);
364 set_mouse_mode (MouseRange);
367 set_mouse_mode (MouseTimeFX);
372 if (next) set_mouse_mode (MouseZoom);
373 else set_mouse_mode (MouseObject);
378 if (Profile->get_sae()) {
379 set_mouse_mode (MouseTimeFX);
381 set_mouse_mode (MouseGain);
384 if (Profile->get_sae()) {
385 set_mouse_mode (MouseObject);
387 set_mouse_mode (MouseRange);
393 if (next) set_mouse_mode (MouseTimeFX);
394 else set_mouse_mode (MouseZoom);
399 set_mouse_mode (MouseAudition);
401 if (Profile->get_sae()) {
402 set_mouse_mode (MouseZoom);
404 set_mouse_mode (MouseGain);
410 if (next) set_mouse_mode (MouseObject);
411 else set_mouse_mode (MouseTimeFX);
417 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
419 /* in object/audition/timefx/gain-automation mode,
420 any button press sets the selection if the object
421 can be selected. this is a bit of hack, because
422 we want to avoid this if the mouse operation is a
425 note: not dbl-click or triple-click
428 if (((mouse_mode != MouseObject) &&
429 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
430 (mouse_mode != MouseAudition || item_type != RegionItem) &&
431 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
432 (mouse_mode != MouseGain) &&
433 (mouse_mode != MouseRange)) ||
435 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
440 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
442 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
444 /* almost no selection action on modified button-2 or button-3 events */
446 if (item_type != RegionItem && event->button.button != 2) {
452 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
453 bool press = (event->type == GDK_BUTTON_PRESS);
455 // begin_reversible_command (_("select on click"));
459 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
460 set_selected_regionview_from_click (press, op, true);
461 } else if (event->type == GDK_BUTTON_PRESS) {
462 selection->clear_tracks ();
463 set_selected_track_as_side_effect (true);
465 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
466 clicked_selection = select_range_around_region (selection->regions.front());
471 case RegionViewNameHighlight:
473 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
474 set_selected_regionview_from_click (press, op, true);
475 } else if (event->type == GDK_BUTTON_PRESS) {
476 set_selected_track_as_side_effect ();
481 case FadeInHandleItem:
483 case FadeOutHandleItem:
485 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
486 set_selected_regionview_from_click (press, op, true);
487 } else if (event->type == GDK_BUTTON_PRESS) {
488 set_selected_track_as_side_effect ();
492 case ControlPointItem:
493 set_selected_track_as_side_effect ();
494 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
495 set_selected_control_point_from_click (op, false);
500 /* for context click, select track */
501 if (event->button.button == 3) {
502 selection->clear_tracks ();
503 set_selected_track_as_side_effect (true);
507 case AutomationTrackItem:
508 set_selected_track_as_side_effect (true);
517 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
519 if (_drags->active ()) {
523 /* single mouse clicks on any of these item types operate
524 independent of mouse mode, mostly because they are
525 not on the main track canvas or because we want
530 case PlayheadCursorItem:
531 _drags->set (new CursorDrag (this, item, true), event);
535 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
536 hide_marker (item, event);
538 _drags->set (new MarkerDrag (this, item), event);
542 case TempoMarkerItem:
544 new TempoMarkerDrag (
547 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
553 case MeterMarkerItem:
555 new MeterMarkerDrag (
558 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
567 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
568 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
574 case RangeMarkerBarItem:
575 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
576 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
578 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
583 case CdMarkerBarItem:
584 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
585 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
587 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
592 case TransportMarkerBarItem:
593 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
594 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
596 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
605 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
606 /* special case: allow trim of range selections in joined object mode;
607 in theory eff should equal MouseRange in this case, but it doesn't
608 because entering the range selection canvas item results in entered_regionview
609 being set to 0, so update_join_object_range_location acts as if we aren't
612 if (item_type == StartSelectionTrimItem) {
613 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
614 } else if (item_type == EndSelectionTrimItem) {
615 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
619 Editing::MouseMode eff = effective_mouse_mode ();
621 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
622 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
629 case StartSelectionTrimItem:
630 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
633 case EndSelectionTrimItem:
634 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
638 if (Keyboard::modifier_state_contains
639 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
640 // contains and not equals because I can't use alt as a modifier alone.
641 start_selection_grab (item, event);
642 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
643 /* grab selection for moving */
644 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
646 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
647 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
649 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
650 if (join_object_range_button.get_active() && atv) {
651 /* smart "join" mode: drag automation */
652 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
654 /* this was debated, but decided the more common action was to
655 make a new selection */
656 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
663 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
671 if (internal_editing()) {
672 _drags->set (new NoteDrag (this, item), event);
681 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
682 event->type == GDK_BUTTON_PRESS) {
684 _drags->set (new RubberbandSelectDrag (this, item), event);
686 } else if (event->type == GDK_BUTTON_PRESS) {
689 case FadeInHandleItem:
691 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
692 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
696 case FadeOutHandleItem:
698 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
699 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
704 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
705 add_region_copy_drag (item, event, clicked_regionview);
706 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
707 add_region_brush_drag (item, event, clicked_regionview);
709 add_region_drag (item, event, clicked_regionview);
712 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
713 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
716 _drags->start_grab (event);
719 case RegionViewNameHighlight:
721 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
722 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
729 /* rename happens on edit clicks */
730 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
731 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
736 case ControlPointItem:
737 _drags->set (new ControlPointDrag (this, item), event);
741 case AutomationLineItem:
742 _drags->set (new LineDrag (this, item), event);
747 if (internal_editing()) {
748 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
751 _drags->set (new RubberbandSelectDrag (this, item), event);
755 case AutomationTrackItem:
756 /* rubberband drag to select automation points */
757 _drags->set (new RubberbandSelectDrag (this, item), event);
762 if (join_object_range_button.get_active()) {
763 /* we're in "smart" joined mode, and we've clicked on a Selection */
764 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
765 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
767 /* if we're over an automation track, start a drag of its data */
768 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
770 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
773 /* if we're over a track and a region, and in the `object' part of a region,
774 put a selection around the region and drag both
776 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
777 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
778 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
780 boost::shared_ptr<Playlist> pl = t->playlist ();
783 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
785 RegionView* rv = rtv->view()->find_view (r);
786 clicked_selection = select_range_around_region (rv);
787 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
788 list<RegionView*> rvs;
790 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
791 _drags->start_grab (event);
802 case ImageFrameHandleStartItem:
803 imageframe_start_handle_op(item, event) ;
806 case ImageFrameHandleEndItem:
807 imageframe_end_handle_op(item, event) ;
810 case MarkerViewHandleStartItem:
811 markerview_item_start_handle_op(item, event) ;
814 case MarkerViewHandleEndItem:
815 markerview_item_end_handle_op(item, event) ;
819 start_markerview_grab(item, event) ;
822 start_imageframe_grab(item, event) ;
840 /* start a grab so that if we finish after moving
841 we can tell what happened.
843 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
847 _drags->set (new LineDrag (this, item), event);
850 case ControlPointItem:
851 _drags->set (new ControlPointDrag (this, item), event);
862 case ControlPointItem:
863 _drags->set (new ControlPointDrag (this, item), event);
866 case AutomationLineItem:
867 _drags->set (new LineDrag (this, item), event);
871 // XXX need automation mode to identify which
873 // start_line_grab_from_regionview (item, event);
883 if (event->type == GDK_BUTTON_PRESS) {
884 _drags->set (new MouseZoomDrag (this, item), event);
891 if (internal_editing() && item_type == NoteItem) {
892 /* drag notes if we're in internal edit mode */
893 _drags->set (new NoteResizeDrag (this, item), event);
895 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
896 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
897 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
903 _drags->set (new ScrubDrag (this, item), event);
905 scrub_reverse_distance = 0;
906 last_scrub_x = event->button.x;
907 scrubbing_direction = 0;
908 track_canvas->get_window()->set_cursor (*transparent_cursor);
920 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
922 Editing::MouseMode const eff = effective_mouse_mode ();
927 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
928 add_region_copy_drag (item, event, clicked_regionview);
930 add_region_drag (item, event, clicked_regionview);
932 _drags->start_grab (event);
935 case ControlPointItem:
936 _drags->set (new ControlPointDrag (this, item), event);
945 case RegionViewNameHighlight:
946 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
951 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
962 /* relax till release */
968 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
969 temporal_zoom_session();
971 temporal_zoom_to_frame (true, event_frame(event));
984 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
986 if (event->type != GDK_BUTTON_PRESS) {
990 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
993 Glib::RefPtr<const Gdk::Window> pointer_window;
996 Gdk::ModifierType mask;
998 pointer_window = canvas_window->get_pointer (x, y, mask);
1000 if (pointer_window == track_canvas->get_bin_window()) {
1001 track_canvas->window_to_world (x, y, wx, wy);
1002 allow_vertical_scroll = true;
1004 allow_vertical_scroll = false;
1008 track_canvas->grab_focus();
1010 if (_session && _session->actively_recording()) {
1014 button_selection (item, event, item_type);
1016 if (!_drags->active () &&
1017 (Keyboard::is_delete_event (&event->button) ||
1018 Keyboard::is_context_menu_event (&event->button) ||
1019 Keyboard::is_edit_event (&event->button))) {
1021 /* handled by button release */
1025 switch (event->button.button) {
1027 return button_press_handler_1 (item, event, item_type);
1031 return button_press_handler_2 (item, event, item_type);
1046 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1048 nframes64_t where = event_frame (event, 0, 0);
1049 AutomationTimeAxisView* atv = 0;
1051 /* no action if we're recording */
1053 if (_session && _session->actively_recording()) {
1057 /* first, see if we're finishing a drag ... */
1059 bool were_dragging = false;
1060 if (_drags->active ()) {
1061 bool const r = _drags->end_grab (event);
1063 /* grab dragged, so do nothing else */
1067 were_dragging = true;
1070 button_selection (item, event, item_type);
1072 /* edit events get handled here */
1074 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1075 switch (item_type) {
1080 case TempoMarkerItem:
1081 edit_tempo_marker (item);
1084 case MeterMarkerItem:
1085 edit_meter_marker (item);
1088 case RegionViewName:
1089 if (clicked_regionview->name_active()) {
1090 return mouse_rename_region (item, event);
1094 case ControlPointItem:
1095 edit_control_point (item);
1104 /* context menu events get handled here */
1106 if (Keyboard::is_context_menu_event (&event->button)) {
1108 if (!_drags->active ()) {
1110 /* no matter which button pops up the context menu, tell the menu
1111 widget to use button 1 to drive menu selection.
1114 switch (item_type) {
1116 case FadeInHandleItem:
1118 case FadeOutHandleItem:
1119 popup_fade_context_menu (1, event->button.time, item, item_type);
1123 popup_track_context_menu (1, event->button.time, item_type, false, where);
1127 case RegionViewNameHighlight:
1128 case RegionViewName:
1129 popup_track_context_menu (1, event->button.time, item_type, false, where);
1133 popup_track_context_menu (1, event->button.time, item_type, true, where);
1136 case AutomationTrackItem:
1137 popup_track_context_menu (1, event->button.time, item_type, false, where);
1141 case RangeMarkerBarItem:
1142 case TransportMarkerBarItem:
1143 case CdMarkerBarItem:
1146 popup_ruler_menu (where, item_type);
1150 marker_context_menu (&event->button, item);
1153 case TempoMarkerItem:
1154 tempo_or_meter_marker_context_menu (&event->button, item);
1157 case MeterMarkerItem:
1158 tempo_or_meter_marker_context_menu (&event->button, item);
1161 case CrossfadeViewItem:
1162 popup_track_context_menu (1, event->button.time, item_type, false, where);
1166 case ImageFrameItem:
1167 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1169 case ImageFrameTimeAxisItem:
1170 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1172 case MarkerViewItem:
1173 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1175 case MarkerTimeAxisItem:
1176 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1188 /* delete events get handled here */
1190 Editing::MouseMode const eff = effective_mouse_mode ();
1192 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1194 switch (item_type) {
1195 case TempoMarkerItem:
1196 remove_tempo_marker (item);
1199 case MeterMarkerItem:
1200 remove_meter_marker (item);
1204 remove_marker (*item, event);
1208 if (eff == MouseObject) {
1209 remove_clicked_region ();
1213 case ControlPointItem:
1214 if (eff == MouseGain) {
1215 remove_gain_control_point (item, event);
1217 remove_control_point (item, event);
1227 switch (event->button.button) {
1230 switch (item_type) {
1231 /* see comments in button_press_handler */
1232 case PlayheadCursorItem:
1235 case AutomationLineItem:
1236 case StartSelectionTrimItem:
1237 case EndSelectionTrimItem:
1241 if (!_dragging_playhead) {
1242 snap_to_with_modifier (where, event, 0, true);
1243 mouse_add_new_marker (where);
1247 case CdMarkerBarItem:
1248 if (!_dragging_playhead) {
1249 // if we get here then a dragged range wasn't done
1250 snap_to_with_modifier (where, event, 0, true);
1251 mouse_add_new_marker (where, true);
1256 if (!_dragging_playhead) {
1257 snap_to_with_modifier (where, event);
1258 mouse_add_new_tempo_event (where);
1263 if (!_dragging_playhead) {
1264 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1275 switch (item_type) {
1276 case AutomationTrackItem:
1277 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1279 atv->add_automation_event (item, event, where, event->button.y);
1290 // Gain only makes sense for audio regions
1292 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1296 switch (item_type) {
1298 /* check that we didn't drag before releasing, since
1299 its really annoying to create new control
1300 points when doing this.
1302 if (were_dragging) {
1303 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1308 case AutomationTrackItem:
1309 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1310 add_automation_event (item, event, where, event->button.y);
1319 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1320 if (scrubbing_direction == 0) {
1321 /* no drag, just a click */
1322 switch (item_type) {
1324 play_selected_region ();
1330 /* make sure we stop */
1331 _session->request_transport_speed (0.0);
1348 switch (item_type) {
1350 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1352 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1355 // Button2 click is unused
1368 // x_style_paste (where, 1.0);
1388 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1394 if (last_item_entered != item) {
1395 last_item_entered = item;
1396 last_item_entered_n = 0;
1399 switch (item_type) {
1400 case ControlPointItem:
1401 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1402 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1403 cp->set_visible (true);
1407 at_y = cp->get_y ();
1408 cp->i2w (at_x, at_y);
1412 fraction = 1.0 - (cp->get_y() / cp->line().height());
1414 if (is_drawable() && !_drags->active ()) {
1415 track_canvas->get_window()->set_cursor (*fader_cursor);
1418 last_item_entered_n++;
1419 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1420 if (last_item_entered_n < 10) {
1421 show_verbose_canvas_cursor ();
1427 if (mouse_mode == MouseGain) {
1428 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1430 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1431 if (is_drawable()) {
1432 track_canvas->get_window()->set_cursor (*fader_cursor);
1437 case AutomationLineItem:
1438 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1440 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1442 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1444 if (is_drawable()) {
1445 track_canvas->get_window()->set_cursor (*fader_cursor);
1450 case RegionViewNameHighlight:
1451 if (is_drawable() && mouse_mode == MouseObject) {
1452 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1456 case StartSelectionTrimItem:
1457 case EndSelectionTrimItem:
1460 case ImageFrameHandleStartItem:
1461 case ImageFrameHandleEndItem:
1462 case MarkerViewHandleStartItem:
1463 case MarkerViewHandleEndItem:
1466 if (is_drawable()) {
1467 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1471 case PlayheadCursorItem:
1472 if (is_drawable()) {
1473 switch (_edit_point) {
1475 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1478 track_canvas->get_window()->set_cursor (*grabber_cursor);
1484 case RegionViewName:
1486 /* when the name is not an active item, the entire name highlight is for trimming */
1488 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1489 if (mouse_mode == MouseObject && is_drawable()) {
1490 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1496 case AutomationTrackItem:
1497 if (is_drawable()) {
1498 Gdk::Cursor *cursor;
1499 switch (mouse_mode) {
1501 cursor = selector_cursor;
1504 cursor = zoom_cursor;
1507 cursor = cross_hair_cursor;
1511 track_canvas->get_window()->set_cursor (*cursor);
1513 AutomationTimeAxisView* atv;
1514 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1515 clear_entered_track = false;
1516 set_entered_track (atv);
1522 case RangeMarkerBarItem:
1523 case TransportMarkerBarItem:
1524 case CdMarkerBarItem:
1527 if (is_drawable()) {
1528 track_canvas->get_window()->set_cursor (*timebar_cursor);
1533 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1536 entered_marker = marker;
1537 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1539 case MeterMarkerItem:
1540 case TempoMarkerItem:
1541 if (is_drawable()) {
1542 track_canvas->get_window()->set_cursor (*timebar_cursor);
1545 case FadeInHandleItem:
1546 case FadeOutHandleItem:
1547 if (mouse_mode == MouseObject) {
1548 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1550 rect->property_fill_color_rgba() = 0;
1551 rect->property_outline_pixels() = 1;
1553 track_canvas->get_window()->set_cursor (*grabber_cursor);
1561 /* second pass to handle entered track status in a comprehensible way.
1564 switch (item_type) {
1566 case AutomationLineItem:
1567 case ControlPointItem:
1568 /* these do not affect the current entered track state */
1569 clear_entered_track = false;
1572 case AutomationTrackItem:
1573 /* handled above already */
1577 set_entered_track (0);
1585 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1594 switch (item_type) {
1595 case ControlPointItem:
1596 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1597 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1598 if (cp->line().npoints() > 1 && !cp->selected()) {
1599 cp->set_visible (false);
1603 if (is_drawable()) {
1604 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1607 hide_verbose_canvas_cursor ();
1610 case RegionViewNameHighlight:
1611 case StartSelectionTrimItem:
1612 case EndSelectionTrimItem:
1613 case PlayheadCursorItem:
1616 case ImageFrameHandleStartItem:
1617 case ImageFrameHandleEndItem:
1618 case MarkerViewHandleStartItem:
1619 case MarkerViewHandleEndItem:
1622 if (is_drawable()) {
1623 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1628 case AutomationLineItem:
1629 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1631 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1633 line->property_fill_color_rgba() = al->get_line_color();
1635 if (is_drawable()) {
1636 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1640 case RegionViewName:
1641 /* see enter_handler() for notes */
1642 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1643 if (is_drawable() && mouse_mode == MouseObject) {
1644 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1649 case RangeMarkerBarItem:
1650 case TransportMarkerBarItem:
1651 case CdMarkerBarItem:
1655 if (is_drawable()) {
1656 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1661 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1665 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1666 location_flags_changed (loc, this);
1669 case MeterMarkerItem:
1670 case TempoMarkerItem:
1672 if (is_drawable()) {
1673 track_canvas->get_window()->set_cursor (*timebar_cursor);
1678 case FadeInHandleItem:
1679 case FadeOutHandleItem:
1680 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1682 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1684 rect->property_fill_color_rgba() = rv->get_fill_color();
1685 rect->property_outline_pixels() = 0;
1688 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1691 case AutomationTrackItem:
1692 if (is_drawable()) {
1693 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1694 clear_entered_track = true;
1695 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1707 Editor::left_automation_track ()
1709 if (clear_entered_track) {
1710 set_entered_track (0);
1711 clear_entered_track = false;
1717 Editor::scrub (nframes64_t frame, double current_x)
1721 if (scrubbing_direction == 0) {
1723 _session->request_locate (frame, false);
1724 _session->request_transport_speed (0.1);
1725 scrubbing_direction = 1;
1729 if (last_scrub_x > current_x) {
1731 /* pointer moved to the left */
1733 if (scrubbing_direction > 0) {
1735 /* we reversed direction to go backwards */
1738 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1742 /* still moving to the left (backwards) */
1744 scrub_reversals = 0;
1745 scrub_reverse_distance = 0;
1747 delta = 0.01 * (last_scrub_x - current_x);
1748 _session->request_transport_speed (_session->transport_speed() - delta);
1752 /* pointer moved to the right */
1754 if (scrubbing_direction < 0) {
1755 /* we reversed direction to go forward */
1758 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1761 /* still moving to the right */
1763 scrub_reversals = 0;
1764 scrub_reverse_distance = 0;
1766 delta = 0.01 * (current_x - last_scrub_x);
1767 _session->request_transport_speed (_session->transport_speed() + delta);
1771 /* if there have been more than 2 opposite motion moves detected, or one that moves
1772 back more than 10 pixels, reverse direction
1775 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1777 if (scrubbing_direction > 0) {
1778 /* was forwards, go backwards */
1779 _session->request_transport_speed (-0.1);
1780 scrubbing_direction = -1;
1782 /* was backwards, go forwards */
1783 _session->request_transport_speed (0.1);
1784 scrubbing_direction = 1;
1787 scrub_reverse_distance = 0;
1788 scrub_reversals = 0;
1792 last_scrub_x = current_x;
1796 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1798 if (event->motion.is_hint) {
1801 /* We call this so that MOTION_NOTIFY events continue to be
1802 delivered to the canvas. We need to do this because we set
1803 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1804 the density of the events, at the expense of a round-trip
1805 to the server. Given that this will mostly occur on cases
1806 where DISPLAY = :0.0, and given the cost of what the motion
1807 event might do, its a good tradeoff.
1810 track_canvas->get_pointer (x, y);
1813 if (current_stepping_trackview) {
1814 /* don't keep the persistent stepped trackview if the mouse moves */
1815 current_stepping_trackview = 0;
1816 step_timeout.disconnect ();
1819 if (_session && _session->actively_recording()) {
1820 /* Sorry. no dragging stuff around while we record */
1824 JoinObjectRangeState const old = _join_object_range_state;
1825 update_join_object_range_location (event->motion.x, event->motion.y);
1826 if (_join_object_range_state != old) {
1827 set_canvas_cursor ();
1830 bool handled = false;
1831 if (_drags->active ()) {
1832 handled = _drags->motion_handler (event, from_autoscroll);
1839 track_canvas_motion (event);
1844 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1846 ControlPoint* control_point;
1848 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1849 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1853 // We shouldn't remove the first or last gain point
1854 if (control_point->line().is_last_point(*control_point) ||
1855 control_point->line().is_first_point(*control_point)) {
1859 control_point->line().remove_point (*control_point);
1863 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1865 ControlPoint* control_point;
1867 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1868 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1872 control_point->line().remove_point (*control_point);
1876 Editor::edit_control_point (ArdourCanvas::Item* item)
1878 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1881 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1885 ControlPointDialog d (p);
1886 d.set_position (Gtk::WIN_POS_MOUSE);
1889 if (d.run () != RESPONSE_ACCEPT) {
1893 p->line().modify_point_y (*p, d.get_y_fraction ());
1898 Editor::visible_order_range (int* low, int* high) const
1900 *low = TimeAxisView::max_order ();
1903 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1905 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1907 if (!rtv->hidden()) {
1909 if (*high < rtv->order()) {
1910 *high = rtv->order ();
1913 if (*low > rtv->order()) {
1914 *low = rtv->order ();
1921 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1923 /* Either add to or set the set the region selection, unless
1924 this is an alignment click (control used)
1927 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1928 TimeAxisView* tv = &rv.get_time_axis_view();
1929 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1931 if (rtv && rtv->is_track()) {
1932 speed = rtv->track()->speed();
1935 nframes64_t where = get_preferred_edit_position();
1939 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1941 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
1943 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1945 align_region (rv.region(), End, (nframes64_t) (where * speed));
1949 align_region (rv.region(), Start, (nframes64_t) (where * speed));
1956 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
1959 Timecode::Time timecode;
1962 nframes64_t frame_rate;
1965 if (_session == 0) {
1971 if (Profile->get_sae() || Profile->get_small_screen()) {
1972 m = ARDOUR_UI::instance()->primary_clock.mode();
1974 m = ARDOUR_UI::instance()->secondary_clock.mode();
1978 case AudioClock::BBT:
1979 _session->bbt_time (frame, bbt);
1980 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
1983 case AudioClock::Timecode:
1984 _session->timecode_time (frame, timecode);
1985 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1988 case AudioClock::MinSec:
1989 /* XXX this is copied from show_verbose_duration_cursor() */
1990 frame_rate = _session->frame_rate();
1991 hours = frame / (frame_rate * 3600);
1992 frame = frame % (frame_rate * 3600);
1993 mins = frame / (frame_rate * 60);
1994 frame = frame % (frame_rate * 60);
1995 secs = (float) frame / (float) frame_rate;
1996 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2000 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2004 if (xpos >= 0 && ypos >=0) {
2005 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2007 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_position(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2009 show_verbose_canvas_cursor ();
2013 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
2016 Timecode::Time timecode;
2020 nframes64_t distance, frame_rate;
2022 Meter meter_at_start(_session->tempo_map().meter_at(start));
2024 if (_session == 0) {
2030 if (Profile->get_sae() || Profile->get_small_screen()) {
2031 m = ARDOUR_UI::instance()->primary_clock.mode ();
2033 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2037 case AudioClock::BBT:
2038 _session->bbt_time (start, sbbt);
2039 _session->bbt_time (end, ebbt);
2042 /* XXX this computation won't work well if the
2043 user makes a selection that spans any meter changes.
2046 ebbt.bars -= sbbt.bars;
2047 if (ebbt.beats >= sbbt.beats) {
2048 ebbt.beats -= sbbt.beats;
2051 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2053 if (ebbt.ticks >= sbbt.ticks) {
2054 ebbt.ticks -= sbbt.ticks;
2057 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2060 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2063 case AudioClock::Timecode:
2064 _session->timecode_duration (end - start, timecode);
2065 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2068 case AudioClock::MinSec:
2069 /* XXX this stuff should be elsewhere.. */
2070 distance = end - start;
2071 frame_rate = _session->frame_rate();
2072 hours = distance / (frame_rate * 3600);
2073 distance = distance % (frame_rate * 3600);
2074 mins = distance / (frame_rate * 60);
2075 distance = distance % (frame_rate * 60);
2076 secs = (float) distance / (float) frame_rate;
2077 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2081 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2085 if (xpos >= 0 && ypos >=0) {
2086 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2089 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2092 show_verbose_canvas_cursor ();
2096 Editor::collect_new_region_view (RegionView* rv)
2098 latest_regionviews.push_back (rv);
2102 Editor::collect_and_select_new_region_view (RegionView* rv)
2105 latest_regionviews.push_back (rv);
2109 Editor::cancel_selection ()
2111 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2112 (*i)->hide_selection ();
2115 selection->clear ();
2116 clicked_selection = 0;
2121 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction)
2123 boost::shared_ptr<Region> region (rv.region());
2125 if (region->locked()) {
2129 nframes64_t new_bound;
2132 TimeAxisView* tvp = clicked_axisview;
2133 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2135 if (tv && tv->is_track()) {
2136 speed = tv->track()->speed();
2139 if (left_direction) {
2140 if (swap_direction) {
2141 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2143 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2146 if (swap_direction) {
2147 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2149 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2153 region->trim_start ((nframes64_t) (new_bound * speed), this);
2154 rv.region_changed (PropertyChange (ARDOUR::Properties::start));
2158 Editor::single_start_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2160 boost::shared_ptr<Region> region (rv.region());
2162 if (region->locked()) {
2167 TimeAxisView* tvp = clicked_axisview;
2168 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2170 if (tv && tv->is_track()) {
2171 speed = tv->track()->speed();
2174 nframes64_t pre_trim_first_frame = region->first_frame();
2176 region->trim_front ((nframes64_t) (new_bound * speed), this);
2179 //Get the next region on the left of this region and shrink/expand it.
2180 boost::shared_ptr<Playlist> playlist (region->playlist());
2181 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2183 bool regions_touching = false;
2185 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2186 regions_touching = true;
2189 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2190 if (region_left != 0 &&
2191 (region_left->last_frame() > region->first_frame() || regions_touching))
2193 region_left->trim_end(region->first_frame() - 1, this);
2197 rv.region_changed (ARDOUR::bounds_change);
2201 Editor::single_end_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2203 boost::shared_ptr<Region> region (rv.region());
2205 if (region->locked()) {
2210 TimeAxisView* tvp = clicked_axisview;
2211 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2213 if (tv && tv->is_track()) {
2214 speed = tv->track()->speed();
2217 nframes64_t pre_trim_last_frame = region->last_frame();
2219 region->trim_end ((nframes64_t) (new_bound * speed), this);
2222 //Get the next region on the right of this region and shrink/expand it.
2223 boost::shared_ptr<Playlist> playlist (region->playlist());
2224 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2226 bool regions_touching = false;
2228 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)) {
2229 regions_touching = true;
2232 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2233 if (region_right != 0 &&
2234 (region_right->first_frame() < region->last_frame() || regions_touching))
2236 region_right->trim_front(region->last_frame() + 1, this);
2239 rv.region_changed (ARDOUR::bounds_change);
2242 rv.region_changed (PropertyChange (ARDOUR::Properties::length));
2248 Editor::point_trim (GdkEvent* event, nframes64_t new_bound)
2250 RegionView* rv = clicked_regionview;
2252 /* Choose action dependant on which button was pressed */
2253 switch (event->button.button) {
2255 begin_reversible_command (_("Start point trim"));
2257 if (selection->selected (rv)) {
2258 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2259 i != selection->regions.by_layer().end(); ++i)
2262 cerr << "region view contains null region" << endl;
2265 if (!(*i)->region()->locked()) {
2266 (*i)->region()->clear_history ();
2267 (*i)->region()->trim_front (new_bound, this);
2268 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2273 if (!rv->region()->locked()) {
2274 rv->region()->clear_history ();
2275 rv->region()->trim_front (new_bound, this);
2276 _session->add_command(new StatefulDiffCommand (rv->region()));
2280 commit_reversible_command();
2284 begin_reversible_command (_("End point trim"));
2286 if (selection->selected (rv)) {
2288 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2290 if (!(*i)->region()->locked()) {
2291 (*i)->region()->clear_history();
2292 (*i)->region()->trim_end (new_bound, this);
2293 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2299 if (!rv->region()->locked()) {
2300 rv->region()->clear_history ();
2301 rv->region()->trim_end (new_bound, this);
2302 _session->add_command (new StatefulDiffCommand (rv->region()));
2306 commit_reversible_command();
2315 Editor::thaw_region_after_trim (RegionView& rv)
2317 boost::shared_ptr<Region> region (rv.region());
2319 if (region->locked()) {
2323 region->resume_property_changes ();
2325 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2328 arv->unhide_envelope ();
2333 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2338 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2339 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2343 Location* location = find_location_from_marker (marker, is_start);
2344 location->set_hidden (true, this);
2349 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2351 double x1 = frame_to_pixel (start);
2352 double x2 = frame_to_pixel (end);
2353 double y2 = full_canvas_height - 1.0;
2355 zoom_rect->property_x1() = x1;
2356 zoom_rect->property_y1() = 1.0;
2357 zoom_rect->property_x2() = x2;
2358 zoom_rect->property_y2() = y2;
2363 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2365 using namespace Gtkmm2ext;
2367 ArdourPrompter prompter (false);
2369 prompter.set_prompt (_("Name for region:"));
2370 prompter.set_initial_text (clicked_regionview->region()->name());
2371 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2372 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2373 prompter.show_all ();
2374 switch (prompter.run ()) {
2375 case Gtk::RESPONSE_ACCEPT:
2377 prompter.get_result(str);
2379 clicked_regionview->region()->set_name (str);
2388 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2390 /* no brushing without a useful snap setting */
2392 switch (_snap_mode) {
2394 return; /* can't work because it allows region to be placed anywhere */
2399 switch (_snap_type) {
2407 /* don't brush a copy over the original */
2409 if (pos == rv->region()->position()) {
2413 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2415 if (rtv == 0 || !rtv->is_track()) {
2419 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2420 double speed = rtv->track()->speed();
2422 playlist->clear_history ();
2423 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
2424 playlist->add_region (new_region, (nframes64_t) (pos * speed));
2425 _session->add_command (new StatefulDiffCommand (playlist));
2427 // playlist is frozen, so we have to update manually XXX this is disgusting
2429 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2433 Editor::track_height_step_timeout ()
2435 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2436 current_stepping_trackview = 0;
2443 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2445 assert (region_view);
2447 _region_motion_group->raise_to_top ();
2449 if (Config->get_edit_mode() == Splice) {
2450 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2452 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2453 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2456 /* sync the canvas to what we think is its current state */
2457 update_canvas_now();
2461 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2463 assert (region_view);
2465 _region_motion_group->raise_to_top ();
2467 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2468 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2472 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2474 assert (region_view);
2476 if (Config->get_edit_mode() == Splice) {
2480 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2481 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2483 begin_reversible_command (_("Drag region brush"));
2486 /** Start a grab where a time range is selected, track(s) are selected, and the
2487 * user clicks and drags a region with a modifier in order to create a new region containing
2488 * the section of the clicked region that lies within the time range.
2491 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2493 if (clicked_regionview == 0) {
2497 /* lets try to create new Region for the selection */
2499 vector<boost::shared_ptr<Region> > new_regions;
2500 create_region_from_selection (new_regions);
2502 if (new_regions.empty()) {
2506 /* XXX fix me one day to use all new regions */
2508 boost::shared_ptr<Region> region (new_regions.front());
2510 /* add it to the current stream/playlist.
2512 tricky: the streamview for the track will add a new regionview. we will
2513 catch the signal it sends when it creates the regionview to
2514 set the regionview we want to then drag.
2517 latest_regionviews.clear();
2518 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2520 /* A selection grab currently creates two undo/redo operations, one for
2521 creating the new region and another for moving it.
2524 begin_reversible_command (_("selection grab"));
2526 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2528 playlist->clear_history ();
2529 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2530 _session->add_command(new StatefulDiffCommand (playlist));
2532 commit_reversible_command ();
2536 if (latest_regionviews.empty()) {
2537 /* something went wrong */
2541 /* we need to deselect all other regionviews, and select this one
2542 i'm ignoring undo stuff, because the region creation will take care of it
2544 selection->set (latest_regionviews);
2546 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2552 if (_drags->active ()) {
2555 selection->clear ();
2560 Editor::set_internal_edit (bool yn)
2562 _internal_editing = yn;
2565 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2566 mouse_select_button.get_image ()->show ();
2567 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2569 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2570 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2572 mtv->start_step_editing ();
2576 start_step_editing ();
2580 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2581 mouse_select_button.get_image ()->show ();
2582 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Select/Move Ranges"));
2583 stop_step_editing ();
2585 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2586 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2588 mtv->stop_step_editing ();
2593 set_canvas_cursor ();
2598 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2599 * used by the `join object/range' tool mode.
2602 Editor::update_join_object_range_location (double x, double y)
2604 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2605 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2606 that we're over requires searching the playlist.
2609 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2610 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2614 if (mouse_mode == MouseObject) {
2615 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2616 } else if (mouse_mode == MouseRange) {
2617 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2620 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2621 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2625 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2630 rtv->canvas_display()->w2i (cx, cy);
2632 bool const top_half = cy < rtv->current_height () / 2;
2634 _join_object_range_state = top_half ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2640 Editor::effective_mouse_mode () const
2642 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2644 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {