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"
56 #include "automation_region_view.h"
57 #include "edit_note_dialog.h"
59 #include "ardour/types.h"
60 #include "ardour/profile.h"
61 #include "ardour/route.h"
62 #include "ardour/audio_track.h"
63 #include "ardour/audio_diskstream.h"
64 #include "ardour/midi_diskstream.h"
65 #include "ardour/playlist.h"
66 #include "ardour/audioplaylist.h"
67 #include "ardour/audioregion.h"
68 #include "ardour/midi_region.h"
69 #include "ardour/dB.h"
70 #include "ardour/utils.h"
71 #include "ardour/region_factory.h"
72 #include "ardour/source_factory.h"
73 #include "ardour/session.h"
80 using namespace ARDOUR;
83 using namespace Editing;
84 using Gtkmm2ext::Keyboard;
87 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
91 Gdk::ModifierType mask;
92 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
93 Glib::RefPtr<const Gdk::Window> pointer_window;
99 pointer_window = canvas_window->get_pointer (x, y, mask);
101 if (pointer_window == track_canvas->get_bin_window()) {
104 in_track_canvas = true;
107 in_track_canvas = false;
112 event.type = GDK_BUTTON_RELEASE;
116 where = event_frame (&event, 0, 0);
121 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
135 switch (event->type) {
136 case GDK_BUTTON_RELEASE:
137 case GDK_BUTTON_PRESS:
138 case GDK_2BUTTON_PRESS:
139 case GDK_3BUTTON_PRESS:
140 *pcx = event->button.x;
141 *pcy = event->button.y;
142 _trackview_group->w2i(*pcx, *pcy);
144 case GDK_MOTION_NOTIFY:
145 *pcx = event->motion.x;
146 *pcy = event->motion.y;
147 _trackview_group->w2i(*pcx, *pcy);
149 case GDK_ENTER_NOTIFY:
150 case GDK_LEAVE_NOTIFY:
151 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
154 case GDK_KEY_RELEASE:
155 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
158 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
162 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
163 position is negative (as can be the case with motion events in particular),
164 the frame location is always positive.
167 return pixel_to_frame (*pcx);
171 Editor::which_grabber_cursor ()
173 Gdk::Cursor* c = grabber_cursor;
175 if (_internal_editing) {
176 switch (mouse_mode) {
178 c = midi_pencil_cursor;
182 c = grabber_note_cursor;
186 c = midi_resize_cursor;
195 switch (_edit_point) {
197 c = grabber_edit_point_cursor;
208 Editor::set_canvas_cursor ()
210 if (_internal_editing) {
212 switch (mouse_mode) {
214 current_canvas_cursor = midi_pencil_cursor;
218 current_canvas_cursor = which_grabber_cursor();
222 current_canvas_cursor = midi_resize_cursor;
231 switch (mouse_mode) {
233 current_canvas_cursor = selector_cursor;
237 current_canvas_cursor = which_grabber_cursor();
241 current_canvas_cursor = cross_hair_cursor;
245 current_canvas_cursor = zoom_in_cursor;
249 current_canvas_cursor = time_fx_cursor; // just use playhead
253 current_canvas_cursor = speaker_cursor;
258 switch (_join_object_range_state) {
259 case JOIN_OBJECT_RANGE_NONE:
261 case JOIN_OBJECT_RANGE_OBJECT:
262 current_canvas_cursor = which_grabber_cursor ();
264 case JOIN_OBJECT_RANGE_RANGE:
265 current_canvas_cursor = selector_cursor;
269 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
270 if (join_object_range_button.get_active() && last_item_entered) {
271 if (last_item_entered->property_parent() && (*last_item_entered->property_parent()).get_data (X_("timeselection"))) {
272 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value() - canvas_timebars_vsize);
273 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
274 current_canvas_cursor = up_down_cursor;
280 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
285 Editor::set_mouse_mode (MouseMode m, bool force)
287 if (_drags->active ()) {
291 if (!force && m == mouse_mode) {
295 Glib::RefPtr<Action> act;
299 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
303 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
307 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
311 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
315 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
319 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
325 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
328 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
329 tact->set_active (false);
330 tact->set_active (true);
334 Editor::mouse_mode_toggled (MouseMode m)
340 if (!internal_editing()) {
341 if (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
343 /* in all modes except range and joined object/range, hide the range selection,
344 show the object (region) selection.
347 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
348 (*i)->set_should_show_selection (true);
350 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
351 (*i)->hide_selection ();
357 in range or object/range mode, show the range selection.
360 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
361 (*i)->show_selection (selection->time);
366 set_canvas_cursor ();
370 Editor::step_mouse_mode (bool next)
372 switch (current_mouse_mode()) {
375 if (Profile->get_sae()) {
376 set_mouse_mode (MouseZoom);
378 set_mouse_mode (MouseRange);
381 set_mouse_mode (MouseTimeFX);
386 if (next) set_mouse_mode (MouseZoom);
387 else set_mouse_mode (MouseObject);
392 if (Profile->get_sae()) {
393 set_mouse_mode (MouseTimeFX);
395 set_mouse_mode (MouseGain);
398 if (Profile->get_sae()) {
399 set_mouse_mode (MouseObject);
401 set_mouse_mode (MouseRange);
407 if (next) set_mouse_mode (MouseTimeFX);
408 else set_mouse_mode (MouseZoom);
413 set_mouse_mode (MouseAudition);
415 if (Profile->get_sae()) {
416 set_mouse_mode (MouseZoom);
418 set_mouse_mode (MouseGain);
424 if (next) set_mouse_mode (MouseObject);
425 else set_mouse_mode (MouseTimeFX);
431 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
433 /* in object/audition/timefx/gain-automation mode,
434 any button press sets the selection if the object
435 can be selected. this is a bit of hack, because
436 we want to avoid this if the mouse operation is a
439 note: not dbl-click or triple-click
441 Also note that there is no region selection in internal edit mode, otherwise
442 for operations operating on the selection (e.g. cut) it is not obvious whether
443 to cut notes or regions.
446 if (((mouse_mode != MouseObject) &&
447 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
448 (mouse_mode != MouseAudition || item_type != RegionItem) &&
449 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
450 (mouse_mode != MouseGain) &&
451 (mouse_mode != MouseRange)) ||
452 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
453 internal_editing()) {
458 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
460 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
462 /* almost no selection action on modified button-2 or button-3 events */
464 if (item_type != RegionItem && event->button.button != 2) {
470 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
471 bool press = (event->type == GDK_BUTTON_PRESS);
473 // begin_reversible_command (_("select on click"));
477 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
478 set_selected_regionview_from_click (press, op, true);
479 } else if (event->type == GDK_BUTTON_PRESS) {
480 selection->clear_tracks ();
481 set_selected_track_as_side_effect (op, true);
483 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
484 clicked_selection = select_range_around_region (selection->regions.front());
488 case RegionViewNameHighlight:
490 case LeftFrameHandle:
491 case RightFrameHandle:
492 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
493 set_selected_regionview_from_click (press, op, true);
494 } else if (event->type == GDK_BUTTON_PRESS) {
495 set_selected_track_as_side_effect (op);
500 case FadeInHandleItem:
502 case FadeOutHandleItem:
504 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
505 set_selected_regionview_from_click (press, op, true);
506 } else if (event->type == GDK_BUTTON_PRESS) {
507 set_selected_track_as_side_effect (op);
511 case ControlPointItem:
512 set_selected_track_as_side_effect (op, true);
513 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
514 set_selected_control_point_from_click (op, false);
519 /* for context click, select track */
520 if (event->button.button == 3) {
521 selection->clear_tracks ();
522 set_selected_track_as_side_effect (op, true);
526 case AutomationTrackItem:
527 set_selected_track_as_side_effect (op, true);
536 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
538 /* single mouse clicks on any of these item types operate
539 independent of mouse mode, mostly because they are
540 not on the main track canvas or because we want
545 case PlayheadCursorItem:
546 _drags->set (new CursorDrag (this, item, true), event);
550 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
551 hide_marker (item, event);
553 _drags->set (new MarkerDrag (this, item), event);
557 case TempoMarkerItem:
559 new TempoMarkerDrag (
562 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
568 case MeterMarkerItem:
570 new MeterMarkerDrag (
573 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
582 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
583 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
589 case RangeMarkerBarItem:
590 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
591 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
593 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
598 case CdMarkerBarItem:
599 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
600 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
602 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
607 case TransportMarkerBarItem:
608 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
609 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
611 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
620 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
621 /* special case: allow trim of range selections in joined object mode;
622 in theory eff should equal MouseRange in this case, but it doesn't
623 because entering the range selection canvas item results in entered_regionview
624 being set to 0, so update_join_object_range_location acts as if we aren't
627 if (item_type == StartSelectionTrimItem) {
628 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
629 } else if (item_type == EndSelectionTrimItem) {
630 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
634 Editing::MouseMode eff = effective_mouse_mode ();
636 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
637 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
644 case StartSelectionTrimItem:
645 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
648 case EndSelectionTrimItem:
649 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
653 if (Keyboard::modifier_state_contains
654 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
655 // contains and not equals because I can't use alt as a modifier alone.
656 start_selection_grab (item, event);
657 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
658 /* grab selection for moving */
659 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
661 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
662 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
664 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
665 if (join_object_range_button.get_active() && atv) {
666 /* smart "join" mode: drag automation */
667 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, up_down_cursor);
669 /* this was debated, but decided the more common action was to
670 make a new selection */
671 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
678 if (internal_editing()) {
679 /* trim notes if we're in internal edit mode and near the ends of the note */
680 _drags->set (new NoteResizeDrag (this, item), event);
685 cerr << "press on stream item, internal? " << internal_editing() << " MIDI ? "
686 << dynamic_cast<MidiTimeAxisView*>(clicked_axisview)
688 if (internal_editing()) {
689 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
690 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
694 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
699 case RegionViewNameHighlight:
700 case LeftFrameHandle:
701 case RightFrameHandle:
703 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
704 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
710 if (!internal_editing()) {
711 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
720 if (internal_editing()) {
721 _drags->set (new NoteDrag (this, item), event);
730 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
731 event->type == GDK_BUTTON_PRESS) {
733 _drags->set (new RubberbandSelectDrag (this, item), event);
735 } else if (event->type == GDK_BUTTON_PRESS) {
738 case FadeInHandleItem:
740 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
741 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
745 case FadeOutHandleItem:
747 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
748 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
752 case FeatureLineItem:
754 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
755 remove_transient(item);
759 _drags->set (new FeatureLineDrag (this, item), event);
765 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
766 /* click on an automation region view; do nothing here and let the ARV's signal handler
772 if (internal_editing ()) {
773 /* no region drags in internal edit mode */
777 /* click on a normal region view */
778 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
779 add_region_copy_drag (item, event, clicked_regionview);
781 else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
782 add_region_brush_drag (item, event, clicked_regionview);
784 add_region_drag (item, event, clicked_regionview);
787 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
788 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
791 _drags->start_grab (event);
794 case RegionViewNameHighlight:
795 case LeftFrameHandle:
796 case RightFrameHandle:
797 if (!internal_editing ()) {
798 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
799 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
806 /* rename happens on edit clicks */
807 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
808 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
813 case ControlPointItem:
814 _drags->set (new ControlPointDrag (this, item), event);
818 case AutomationLineItem:
819 _drags->set (new LineDrag (this, item), event);
824 if (internal_editing()) {
825 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
826 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
830 _drags->set (new RubberbandSelectDrag (this, item), event);
834 case AutomationTrackItem:
835 /* rubberband drag to select automation points */
836 _drags->set (new RubberbandSelectDrag (this, item), event);
841 if (join_object_range_button.get_active()) {
842 /* we're in "smart" joined mode, and we've clicked on a Selection */
843 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
844 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
846 /* if we're over an automation track, start a drag of its data */
847 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
849 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, up_down_cursor);
852 /* if we're over a track and a region, and in the `object' part of a region,
853 put a selection around the region and drag both
855 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
856 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
857 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
859 boost::shared_ptr<Playlist> pl = t->playlist ();
862 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
864 RegionView* rv = rtv->view()->find_view (r);
865 clicked_selection = select_range_around_region (rv);
866 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
867 list<RegionView*> rvs;
869 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
870 _drags->start_grab (event);
881 case ImageFrameHandleStartItem:
882 imageframe_start_handle_op(item, event) ;
885 case ImageFrameHandleEndItem:
886 imageframe_end_handle_op(item, event) ;
889 case MarkerViewHandleStartItem:
890 markerview_item_start_handle_op(item, event) ;
893 case MarkerViewHandleEndItem:
894 markerview_item_end_handle_op(item, event) ;
898 start_markerview_grab(item, event) ;
901 start_imageframe_grab(item, event) ;
919 /* start a grab so that if we finish after moving
920 we can tell what happened.
922 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
926 _drags->set (new LineDrag (this, item), event);
929 case ControlPointItem:
930 _drags->set (new ControlPointDrag (this, item), event);
941 case ControlPointItem:
942 _drags->set (new ControlPointDrag (this, item), event);
945 case AutomationLineItem:
946 _drags->set (new LineDrag (this, item), event);
950 // XXX need automation mode to identify which
952 // start_line_grab_from_regionview (item, event);
962 if (event->type == GDK_BUTTON_PRESS) {
963 _drags->set (new MouseZoomDrag (this, item), event);
970 if (internal_editing() && item_type == NoteItem) {
971 /* drag notes if we're in internal edit mode */
972 _drags->set (new NoteResizeDrag (this, item), event);
974 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
975 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
976 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
982 _drags->set (new ScrubDrag (this, item), event);
984 scrub_reverse_distance = 0;
985 last_scrub_x = event->button.x;
986 scrubbing_direction = 0;
987 track_canvas->get_window()->set_cursor (*transparent_cursor);
999 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1001 Editing::MouseMode const eff = effective_mouse_mode ();
1004 switch (item_type) {
1006 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1007 add_region_copy_drag (item, event, clicked_regionview);
1009 add_region_drag (item, event, clicked_regionview);
1011 _drags->start_grab (event);
1014 case ControlPointItem:
1015 _drags->set (new ControlPointDrag (this, item), event);
1023 switch (item_type) {
1024 case RegionViewNameHighlight:
1025 case LeftFrameHandle:
1026 case RightFrameHandle:
1027 if (!internal_editing ()) {
1028 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1033 case RegionViewName:
1034 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1045 /* relax till release */
1051 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1052 temporal_zoom_session();
1054 temporal_zoom_to_frame (true, event_frame(event));
1067 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1069 if (event->type != GDK_BUTTON_PRESS) {
1073 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1075 if (canvas_window) {
1076 Glib::RefPtr<const Gdk::Window> pointer_window;
1079 Gdk::ModifierType mask;
1081 pointer_window = canvas_window->get_pointer (x, y, mask);
1083 if (pointer_window == track_canvas->get_bin_window()) {
1084 track_canvas->window_to_world (x, y, wx, wy);
1088 track_canvas->grab_focus();
1090 if (_session && _session->actively_recording()) {
1094 button_selection (item, event, item_type);
1096 if (!_drags->active () &&
1097 (Keyboard::is_delete_event (&event->button) ||
1098 Keyboard::is_context_menu_event (&event->button) ||
1099 Keyboard::is_edit_event (&event->button))) {
1101 /* handled by button release */
1105 switch (event->button.button) {
1107 return button_press_handler_1 (item, event, item_type);
1111 return button_press_handler_2 (item, event, item_type);
1126 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1128 framepos_t where = event_frame (event, 0, 0);
1129 AutomationTimeAxisView* atv = 0;
1131 /* no action if we're recording */
1133 if (_session && _session->actively_recording()) {
1137 /* see if we're finishing a drag */
1139 bool were_dragging = false;
1140 if (_drags->active ()) {
1141 bool const r = _drags->end_grab (event);
1143 /* grab dragged, so do nothing else */
1147 were_dragging = true;
1150 update_region_layering_order_editor (where);
1152 /* edit events get handled here */
1154 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1155 switch (item_type) {
1160 case TempoMarkerItem:
1161 edit_tempo_marker (item);
1164 case MeterMarkerItem:
1165 edit_meter_marker (item);
1168 case RegionViewName:
1169 if (clicked_regionview->name_active()) {
1170 return mouse_rename_region (item, event);
1174 case ControlPointItem:
1175 edit_control_point (item);
1188 /* context menu events get handled here */
1190 if (Keyboard::is_context_menu_event (&event->button)) {
1192 if (!_drags->active ()) {
1194 /* no matter which button pops up the context menu, tell the menu
1195 widget to use button 1 to drive menu selection.
1198 switch (item_type) {
1200 case FadeInHandleItem:
1202 case FadeOutHandleItem:
1203 popup_fade_context_menu (1, event->button.time, item, item_type);
1207 popup_track_context_menu (1, event->button.time, item_type, false, where);
1211 case RegionViewNameHighlight:
1212 case LeftFrameHandle:
1213 case RightFrameHandle:
1214 case RegionViewName:
1215 popup_track_context_menu (1, event->button.time, item_type, false, where);
1219 popup_track_context_menu (1, event->button.time, item_type, true, where);
1222 case AutomationTrackItem:
1223 popup_track_context_menu (1, event->button.time, item_type, false, where);
1227 case RangeMarkerBarItem:
1228 case TransportMarkerBarItem:
1229 case CdMarkerBarItem:
1232 popup_ruler_menu (where, item_type);
1236 marker_context_menu (&event->button, item);
1239 case TempoMarkerItem:
1240 tempo_or_meter_marker_context_menu (&event->button, item);
1243 case MeterMarkerItem:
1244 tempo_or_meter_marker_context_menu (&event->button, item);
1247 case CrossfadeViewItem:
1248 popup_track_context_menu (1, event->button.time, item_type, false, where);
1252 case ImageFrameItem:
1253 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1255 case ImageFrameTimeAxisItem:
1256 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1258 case MarkerViewItem:
1259 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1261 case MarkerTimeAxisItem:
1262 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1274 /* delete events get handled here */
1276 Editing::MouseMode const eff = effective_mouse_mode ();
1278 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1280 switch (item_type) {
1281 case TempoMarkerItem:
1282 remove_tempo_marker (item);
1285 case MeterMarkerItem:
1286 remove_meter_marker (item);
1290 remove_marker (*item, event);
1294 if (eff == MouseObject) {
1295 remove_clicked_region ();
1299 case ControlPointItem:
1300 if (eff == MouseGain) {
1301 remove_gain_control_point (item, event);
1303 remove_control_point (item, event);
1308 remove_midi_note (item, event);
1317 switch (event->button.button) {
1320 switch (item_type) {
1321 /* see comments in button_press_handler */
1322 case PlayheadCursorItem:
1325 case AutomationLineItem:
1326 case StartSelectionTrimItem:
1327 case EndSelectionTrimItem:
1331 if (!_dragging_playhead) {
1332 snap_to_with_modifier (where, event, 0, true);
1333 mouse_add_new_marker (where);
1337 case CdMarkerBarItem:
1338 if (!_dragging_playhead) {
1339 // if we get here then a dragged range wasn't done
1340 snap_to_with_modifier (where, event, 0, true);
1341 mouse_add_new_marker (where, true);
1346 if (!_dragging_playhead) {
1347 snap_to_with_modifier (where, event);
1348 mouse_add_new_tempo_event (where);
1353 if (!_dragging_playhead) {
1354 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1365 switch (item_type) {
1366 case AutomationTrackItem:
1367 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1369 atv->add_automation_event (item, event, where, event->button.y);
1380 switch (item_type) {
1383 /* check that we didn't drag before releasing, since
1384 its really annoying to create new control
1385 points when doing this.
1387 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1388 if (were_dragging && arv) {
1389 arv->add_gain_point_event (item, event);
1395 case AutomationTrackItem:
1396 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1397 add_automation_event (item, event, where, event->button.y);
1406 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1407 if (scrubbing_direction == 0) {
1408 /* no drag, just a click */
1409 switch (item_type) {
1411 play_selected_region ();
1417 /* make sure we stop */
1418 _session->request_transport_speed (0.0);
1435 switch (item_type) {
1437 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1439 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1442 // Button2 click is unused
1455 // x_style_paste (where, 1.0);
1475 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1482 last_item_entered = item;
1484 switch (item_type) {
1485 case ControlPointItem:
1486 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1487 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1488 cp->set_visible (true);
1492 at_y = cp->get_y ();
1493 cp->i2w (at_x, at_y);
1497 fraction = 1.0 - (cp->get_y() / cp->line().height());
1499 if (is_drawable() && !_drags->active ()) {
1500 track_canvas->get_window()->set_cursor (*fader_cursor);
1503 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1504 show_verbose_canvas_cursor ();
1509 if (mouse_mode == MouseGain) {
1510 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1512 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1513 if (is_drawable()) {
1514 track_canvas->get_window()->set_cursor (*fader_cursor);
1519 case AutomationLineItem:
1520 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1522 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1524 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1526 if (is_drawable()) {
1527 track_canvas->get_window()->set_cursor (*fader_cursor);
1532 case RegionViewNameHighlight:
1533 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1534 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1538 case LeftFrameHandle:
1539 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1540 track_canvas->get_window()->set_cursor (*left_side_trim_cursor);
1544 case RightFrameHandle:
1545 if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
1546 track_canvas->get_window()->set_cursor (*right_side_trim_cursor);
1550 case StartSelectionTrimItem:
1551 case EndSelectionTrimItem:
1554 case ImageFrameHandleStartItem:
1555 case ImageFrameHandleEndItem:
1556 case MarkerViewHandleStartItem:
1557 case MarkerViewHandleEndItem:
1560 if (is_drawable()) {
1561 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1565 case PlayheadCursorItem:
1566 if (is_drawable()) {
1567 switch (_edit_point) {
1569 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1572 track_canvas->get_window()->set_cursor (*grabber_cursor);
1578 case RegionViewName:
1580 /* when the name is not an active item, the entire name highlight is for trimming */
1582 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1583 if (mouse_mode == MouseObject && is_drawable()) {
1584 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1590 case AutomationTrackItem:
1591 if (is_drawable()) {
1592 Gdk::Cursor *cursor;
1593 switch (mouse_mode) {
1595 cursor = selector_cursor;
1598 cursor = zoom_in_cursor;
1601 cursor = cross_hair_cursor;
1605 track_canvas->get_window()->set_cursor (*cursor);
1607 AutomationTimeAxisView* atv;
1608 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1609 clear_entered_track = false;
1610 set_entered_track (atv);
1616 case RangeMarkerBarItem:
1617 case TransportMarkerBarItem:
1618 case CdMarkerBarItem:
1621 if (is_drawable()) {
1622 track_canvas->get_window()->set_cursor (*timebar_cursor);
1627 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1630 entered_marker = marker;
1631 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1633 case MeterMarkerItem:
1634 case TempoMarkerItem:
1635 if (is_drawable()) {
1636 track_canvas->get_window()->set_cursor (*timebar_cursor);
1640 case FadeInHandleItem:
1641 if (mouse_mode == MouseObject && !internal_editing()) {
1642 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1644 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1646 track_canvas->get_window()->set_cursor (*fade_in_cursor);
1650 case FadeOutHandleItem:
1651 if (mouse_mode == MouseObject && !internal_editing()) {
1652 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1654 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1656 track_canvas->get_window()->set_cursor (*fade_out_cursor);
1659 case FeatureLineItem:
1661 ArdourCanvas::SimpleLine *line = dynamic_cast<ArdourCanvas::SimpleLine *> (item);
1662 line->property_color_rgba() = 0xFF0000FF;
1666 if (join_object_range_button.get_active()) {
1667 set_canvas_cursor ();
1675 /* second pass to handle entered track status in a comprehensible way.
1678 switch (item_type) {
1680 case AutomationLineItem:
1681 case ControlPointItem:
1682 /* these do not affect the current entered track state */
1683 clear_entered_track = false;
1686 case AutomationTrackItem:
1687 /* handled above already */
1691 set_entered_track (0);
1699 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1709 switch (item_type) {
1710 case ControlPointItem:
1711 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1712 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1713 if (cp->line().npoints() > 1 && !cp->get_selected()) {
1714 cp->set_visible (false);
1718 if (is_drawable()) {
1719 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1722 hide_verbose_canvas_cursor ();
1725 case RegionViewNameHighlight:
1726 case LeftFrameHandle:
1727 case RightFrameHandle:
1728 case StartSelectionTrimItem:
1729 case EndSelectionTrimItem:
1730 case PlayheadCursorItem:
1733 case ImageFrameHandleStartItem:
1734 case ImageFrameHandleEndItem:
1735 case MarkerViewHandleStartItem:
1736 case MarkerViewHandleEndItem:
1739 if (is_drawable()) {
1740 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1745 case AutomationLineItem:
1746 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1748 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1750 line->property_fill_color_rgba() = al->get_line_color();
1752 if (is_drawable()) {
1753 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1757 case RegionViewName:
1758 /* see enter_handler() for notes */
1759 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1760 if (is_drawable() && mouse_mode == MouseObject) {
1761 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1766 case RangeMarkerBarItem:
1767 case TransportMarkerBarItem:
1768 case CdMarkerBarItem:
1772 if (is_drawable()) {
1773 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1778 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1782 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1783 location_flags_changed (loc, this);
1786 case MeterMarkerItem:
1787 case TempoMarkerItem:
1789 if (is_drawable()) {
1790 track_canvas->get_window()->set_cursor (*timebar_cursor);
1795 case FadeInHandleItem:
1796 case FadeOutHandleItem:
1797 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1799 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1801 rect->property_fill_color_rgba() = rv->get_fill_color();
1802 rect->property_outline_pixels() = 0;
1805 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1808 case AutomationTrackItem:
1809 if (is_drawable()) {
1810 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1811 clear_entered_track = true;
1812 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1815 case FeatureLineItem:
1817 ArdourCanvas::SimpleLine *line = dynamic_cast<ArdourCanvas::SimpleLine *> (item);
1818 line->property_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
1830 Editor::left_automation_track ()
1832 if (clear_entered_track) {
1833 set_entered_track (0);
1834 clear_entered_track = false;
1840 Editor::scrub (framepos_t frame, double current_x)
1844 if (scrubbing_direction == 0) {
1846 _session->request_locate (frame, false);
1847 _session->request_transport_speed (0.1);
1848 scrubbing_direction = 1;
1852 if (last_scrub_x > current_x) {
1854 /* pointer moved to the left */
1856 if (scrubbing_direction > 0) {
1858 /* we reversed direction to go backwards */
1861 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1865 /* still moving to the left (backwards) */
1867 scrub_reversals = 0;
1868 scrub_reverse_distance = 0;
1870 delta = 0.01 * (last_scrub_x - current_x);
1871 _session->request_transport_speed (_session->transport_speed() - delta);
1875 /* pointer moved to the right */
1877 if (scrubbing_direction < 0) {
1878 /* we reversed direction to go forward */
1881 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1884 /* still moving to the right */
1886 scrub_reversals = 0;
1887 scrub_reverse_distance = 0;
1889 delta = 0.01 * (current_x - last_scrub_x);
1890 _session->request_transport_speed (_session->transport_speed() + delta);
1894 /* if there have been more than 2 opposite motion moves detected, or one that moves
1895 back more than 10 pixels, reverse direction
1898 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1900 if (scrubbing_direction > 0) {
1901 /* was forwards, go backwards */
1902 _session->request_transport_speed (-0.1);
1903 scrubbing_direction = -1;
1905 /* was backwards, go forwards */
1906 _session->request_transport_speed (0.1);
1907 scrubbing_direction = 1;
1910 scrub_reverse_distance = 0;
1911 scrub_reversals = 0;
1915 last_scrub_x = current_x;
1919 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1921 _last_motion_y = event->motion.y;
1923 if (event->motion.is_hint) {
1926 /* We call this so that MOTION_NOTIFY events continue to be
1927 delivered to the canvas. We need to do this because we set
1928 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1929 the density of the events, at the expense of a round-trip
1930 to the server. Given that this will mostly occur on cases
1931 where DISPLAY = :0.0, and given the cost of what the motion
1932 event might do, its a good tradeoff.
1935 track_canvas->get_pointer (x, y);
1938 if (current_stepping_trackview) {
1939 /* don't keep the persistent stepped trackview if the mouse moves */
1940 current_stepping_trackview = 0;
1941 step_timeout.disconnect ();
1944 if (_session && _session->actively_recording()) {
1945 /* Sorry. no dragging stuff around while we record */
1949 JoinObjectRangeState const old = _join_object_range_state;
1950 update_join_object_range_location (event->motion.x, event->motion.y);
1951 if (_join_object_range_state != old) {
1952 set_canvas_cursor ();
1955 bool handled = false;
1956 if (_drags->active ()) {
1957 handled = _drags->motion_handler (event, from_autoscroll);
1964 track_canvas_motion (event);
1969 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1971 ControlPoint* control_point;
1973 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1974 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1978 // We shouldn't remove the first or last gain point
1979 if (control_point->line().is_last_point(*control_point) ||
1980 control_point->line().is_first_point(*control_point)) {
1984 control_point->line().remove_point (*control_point);
1988 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1990 ControlPoint* control_point;
1992 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1993 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1997 control_point->line().remove_point (*control_point);
2001 Editor::edit_control_point (ArdourCanvas::Item* item)
2003 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2006 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2010 ControlPointDialog d (p);
2011 d.set_position (Gtk::WIN_POS_MOUSE);
2014 if (d.run () != RESPONSE_ACCEPT) {
2018 p->line().modify_point_y (*p, d.get_y_fraction ());
2022 Editor::edit_note (ArdourCanvas::Item* item)
2024 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2027 EditNoteDialog d (&e->region_view(), e);
2028 d.set_position (Gtk::WIN_POS_MOUSE);
2036 Editor::visible_order_range (int* low, int* high) const
2038 *low = TimeAxisView::max_order ();
2041 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2043 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2045 if (!rtv->hidden()) {
2047 if (*high < rtv->order()) {
2048 *high = rtv->order ();
2051 if (*low > rtv->order()) {
2052 *low = rtv->order ();
2059 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2061 /* Either add to or set the set the region selection, unless
2062 this is an alignment click (control used)
2065 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2066 TimeAxisView* tv = &rv.get_time_axis_view();
2067 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2069 if (rtv && rtv->is_track()) {
2070 speed = rtv->track()->speed();
2073 framepos_t where = get_preferred_edit_position();
2077 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2079 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2081 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2083 align_region (rv.region(), End, (framepos_t) (where * speed));
2087 align_region (rv.region(), Start, (framepos_t) (where * speed));
2094 Editor::show_verbose_time_cursor (framepos_t frame, double offset, double xpos, double ypos)
2097 Timecode::Time timecode;
2100 framepos_t frame_rate;
2103 if (_session == 0) {
2109 if (Profile->get_sae() || Profile->get_small_screen()) {
2110 m = ARDOUR_UI::instance()->primary_clock.mode();
2112 m = ARDOUR_UI::instance()->secondary_clock.mode();
2116 case AudioClock::BBT:
2117 _session->bbt_time (frame, bbt);
2118 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2121 case AudioClock::Timecode:
2122 _session->timecode_time (frame, timecode);
2123 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2126 case AudioClock::MinSec:
2127 /* XXX this is copied from show_verbose_duration_cursor() */
2128 frame_rate = _session->frame_rate();
2129 hours = frame / (frame_rate * 3600);
2130 frame = frame % (frame_rate * 3600);
2131 mins = frame / (frame_rate * 60);
2132 frame = frame % (frame_rate * 60);
2133 secs = (float) frame / (float) frame_rate;
2134 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2138 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2142 if (xpos >= 0 && ypos >=0) {
2143 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2145 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_position(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2147 show_verbose_canvas_cursor ();
2151 Editor::show_verbose_duration_cursor (framepos_t start, framepos_t end, double offset, double xpos, double ypos)
2154 Timecode::Time timecode;
2158 framepos_t distance, frame_rate;
2160 Meter meter_at_start(_session->tempo_map().meter_at(start));
2162 if (_session == 0) {
2168 if (Profile->get_sae() || Profile->get_small_screen()) {
2169 m = ARDOUR_UI::instance()->primary_clock.mode ();
2171 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2175 case AudioClock::BBT:
2176 _session->bbt_time (start, sbbt);
2177 _session->bbt_time (end, ebbt);
2180 /* XXX this computation won't work well if the
2181 user makes a selection that spans any meter changes.
2184 ebbt.bars -= sbbt.bars;
2185 if (ebbt.beats >= sbbt.beats) {
2186 ebbt.beats -= sbbt.beats;
2189 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2191 if (ebbt.ticks >= sbbt.ticks) {
2192 ebbt.ticks -= sbbt.ticks;
2195 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2198 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2201 case AudioClock::Timecode:
2202 _session->timecode_duration (end - start, timecode);
2203 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2206 case AudioClock::MinSec:
2207 /* XXX this stuff should be elsewhere.. */
2208 distance = end - start;
2209 frame_rate = _session->frame_rate();
2210 hours = distance / (frame_rate * 3600);
2211 distance = distance % (frame_rate * 3600);
2212 mins = distance / (frame_rate * 60);
2213 distance = distance % (frame_rate * 60);
2214 secs = (float) distance / (float) frame_rate;
2215 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2219 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2223 if (xpos >= 0 && ypos >=0) {
2224 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2227 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2230 show_verbose_canvas_cursor ();
2234 Editor::collect_new_region_view (RegionView* rv)
2236 latest_regionviews.push_back (rv);
2240 Editor::collect_and_select_new_region_view (RegionView* rv)
2243 latest_regionviews.push_back (rv);
2247 Editor::cancel_selection ()
2249 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2250 (*i)->hide_selection ();
2253 selection->clear ();
2254 clicked_selection = 0;
2259 Editor::single_contents_trim (RegionView& rv, framepos_t frame_delta, bool left_direction, bool swap_direction)
2261 boost::shared_ptr<Region> region (rv.region());
2263 if (region->locked()) {
2267 framepos_t new_bound;
2270 TimeAxisView* tvp = clicked_axisview;
2271 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2273 if (tv && tv->is_track()) {
2274 speed = tv->track()->speed();
2277 if (left_direction) {
2278 if (swap_direction) {
2279 new_bound = (framepos_t) (region->position()/speed) + frame_delta;
2281 new_bound = (framepos_t) (region->position()/speed) - frame_delta;
2284 if (swap_direction) {
2285 new_bound = (framepos_t) (region->position()/speed) - frame_delta;
2287 new_bound = (framepos_t) (region->position()/speed) + frame_delta;
2291 region->trim_start ((framepos_t) (new_bound * speed), this);
2292 rv.region_changed (PropertyChange (ARDOUR::Properties::start));
2296 Editor::single_start_trim (RegionView& rv, framepos_t new_bound, bool no_overlap)
2298 boost::shared_ptr<Region> region (rv.region());
2300 if (region->locked()) {
2305 TimeAxisView* tvp = clicked_axisview;
2306 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2308 if (tv && tv->is_track()) {
2309 speed = tv->track()->speed();
2312 framepos_t pre_trim_first_frame = region->first_frame();
2314 region->trim_front ((framepos_t) (new_bound * speed), this);
2317 //Get the next region on the left of this region and shrink/expand it.
2318 boost::shared_ptr<Playlist> playlist (region->playlist());
2319 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2321 bool regions_touching = false;
2323 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2324 regions_touching = true;
2327 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2328 if (region_left != 0 &&
2329 (region_left->last_frame() > region->first_frame() || regions_touching))
2331 region_left->trim_end(region->first_frame() - 1, this);
2335 rv.region_changed (ARDOUR::bounds_change);
2339 Editor::single_end_trim (RegionView& rv, framepos_t new_bound, bool no_overlap)
2341 boost::shared_ptr<Region> region (rv.region());
2343 if (region->locked()) {
2348 TimeAxisView* tvp = clicked_axisview;
2349 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2351 if (tv && tv->is_track()) {
2352 speed = tv->track()->speed();
2355 framepos_t pre_trim_last_frame = region->last_frame();
2357 region->trim_end ((framepos_t) (new_bound * speed), this);
2360 //Get the next region on the right of this region and shrink/expand it.
2361 boost::shared_ptr<Playlist> playlist (region->playlist());
2362 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2364 bool regions_touching = false;
2366 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)) {
2367 regions_touching = true;
2370 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2371 if (region_right != 0 &&
2372 (region_right->first_frame() < region->last_frame() || regions_touching))
2374 region_right->trim_front(region->last_frame() + 1, this);
2377 rv.region_changed (ARDOUR::bounds_change);
2380 rv.region_changed (PropertyChange (ARDOUR::Properties::length));
2386 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2388 RegionView* rv = clicked_regionview;
2390 /* Choose action dependant on which button was pressed */
2391 switch (event->button.button) {
2393 begin_reversible_command (_("start point trim"));
2395 if (selection->selected (rv)) {
2396 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2397 i != selection->regions.by_layer().end(); ++i)
2400 cerr << "region view contains null region" << endl;
2403 if (!(*i)->region()->locked()) {
2404 (*i)->region()->clear_changes ();
2405 (*i)->region()->trim_front (new_bound, this);
2406 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2411 if (!rv->region()->locked()) {
2412 rv->region()->clear_changes ();
2413 rv->region()->trim_front (new_bound, this);
2414 _session->add_command(new StatefulDiffCommand (rv->region()));
2418 commit_reversible_command();
2422 begin_reversible_command (_("End point trim"));
2424 if (selection->selected (rv)) {
2426 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2428 if (!(*i)->region()->locked()) {
2429 (*i)->region()->clear_changes();
2430 (*i)->region()->trim_end (new_bound, this);
2431 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2437 if (!rv->region()->locked()) {
2438 rv->region()->clear_changes ();
2439 rv->region()->trim_end (new_bound, this);
2440 _session->add_command (new StatefulDiffCommand (rv->region()));
2444 commit_reversible_command();
2453 Editor::thaw_region_after_trim (RegionView& rv)
2455 boost::shared_ptr<Region> region (rv.region());
2457 if (region->locked()) {
2461 region->resume_property_changes ();
2463 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2466 arv->unhide_envelope ();
2471 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2476 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2477 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2481 Location* location = find_location_from_marker (marker, is_start);
2482 location->set_hidden (true, this);
2487 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2489 double x1 = frame_to_pixel (start);
2490 double x2 = frame_to_pixel (end);
2491 double y2 = full_canvas_height - 1.0;
2493 zoom_rect->property_x1() = x1;
2494 zoom_rect->property_y1() = 1.0;
2495 zoom_rect->property_x2() = x2;
2496 zoom_rect->property_y2() = y2;
2501 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2503 using namespace Gtkmm2ext;
2505 ArdourPrompter prompter (false);
2507 prompter.set_prompt (_("Name for region:"));
2508 prompter.set_initial_text (clicked_regionview->region()->name());
2509 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2510 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2511 prompter.show_all ();
2512 switch (prompter.run ()) {
2513 case Gtk::RESPONSE_ACCEPT:
2515 prompter.get_result(str);
2517 clicked_regionview->region()->set_name (str);
2526 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2528 /* no brushing without a useful snap setting */
2530 switch (_snap_mode) {
2532 return; /* can't work because it allows region to be placed anywhere */
2537 switch (_snap_type) {
2545 /* don't brush a copy over the original */
2547 if (pos == rv->region()->position()) {
2551 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2553 if (rtv == 0 || !rtv->is_track()) {
2557 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2558 double speed = rtv->track()->speed();
2560 playlist->clear_changes ();
2561 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
2562 playlist->add_region (new_region, (framepos_t) (pos * speed));
2563 _session->add_command (new StatefulDiffCommand (playlist));
2565 // playlist is frozen, so we have to update manually XXX this is disgusting
2567 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2571 Editor::track_height_step_timeout ()
2573 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2574 current_stepping_trackview = 0;
2581 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2583 assert (region_view);
2585 if (!region_view->region()->playlist()) {
2589 _region_motion_group->raise_to_top ();
2591 if (Config->get_edit_mode() == Splice) {
2592 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2594 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2595 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2598 /* sync the canvas to what we think is its current state */
2599 update_canvas_now();
2603 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2605 assert (region_view);
2607 if (!region_view->region()->playlist()) {
2611 _region_motion_group->raise_to_top ();
2613 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2614 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2618 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2620 assert (region_view);
2622 if (!region_view->region()->playlist()) {
2626 if (Config->get_edit_mode() == Splice) {
2630 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2631 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2633 begin_reversible_command (_("Drag region brush"));
2636 /** Start a grab where a time range is selected, track(s) are selected, and the
2637 * user clicks and drags a region with a modifier in order to create a new region containing
2638 * the section of the clicked region that lies within the time range.
2641 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2643 if (clicked_regionview == 0) {
2647 /* lets try to create new Region for the selection */
2649 vector<boost::shared_ptr<Region> > new_regions;
2650 create_region_from_selection (new_regions);
2652 if (new_regions.empty()) {
2656 /* XXX fix me one day to use all new regions */
2658 boost::shared_ptr<Region> region (new_regions.front());
2660 /* add it to the current stream/playlist.
2662 tricky: the streamview for the track will add a new regionview. we will
2663 catch the signal it sends when it creates the regionview to
2664 set the regionview we want to then drag.
2667 latest_regionviews.clear();
2668 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2670 /* A selection grab currently creates two undo/redo operations, one for
2671 creating the new region and another for moving it.
2674 begin_reversible_command (_("selection grab"));
2676 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2678 playlist->clear_changes ();
2679 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2680 _session->add_command(new StatefulDiffCommand (playlist));
2682 commit_reversible_command ();
2686 if (latest_regionviews.empty()) {
2687 /* something went wrong */
2691 /* we need to deselect all other regionviews, and select this one
2692 i'm ignoring undo stuff, because the region creation will take care of it
2694 selection->set (latest_regionviews);
2696 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2702 if (_drags->active ()) {
2705 selection->clear ();
2710 Editor::set_internal_edit (bool yn)
2712 _internal_editing = yn;
2715 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2716 mouse_select_button.get_image ()->show ();
2717 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2718 set_canvas_cursor ();
2720 /* deselect everything to avoid confusion when e.g. we can't now cut a previously selected
2721 region because cut means "cut note" rather than "cut region".
2723 selection->clear ();
2727 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2728 mouse_select_button.get_image ()->show ();
2729 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Select/Move Ranges"));
2730 mouse_mode_toggled (mouse_mode); // sets cursor
2734 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2735 * used by the `join object/range' tool mode.
2738 Editor::update_join_object_range_location (double x, double y)
2740 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2741 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2742 that we're over requires searching the playlist.
2745 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2746 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2750 if (mouse_mode == MouseObject) {
2751 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2752 } else if (mouse_mode == MouseRange) {
2753 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2756 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2757 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2761 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2766 rtv->canvas_display()->w2i (cx, cy);
2768 bool const top_half = cy < rtv->current_height () / 2;
2770 _join_object_range_state = top_half ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2776 Editor::effective_mouse_mode () const
2778 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2780 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2788 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2790 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2793 e->region_view().delete_note (e->note ());