fix math bug with numthreads computation
[ardour2.git] / gtk2_ardour / editor_mouse.cc
blobd23e07d393a550a699d08c23e90de7d847adec37
1 /*
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.
20 #include <cassert>
21 #include <cstdlib>
22 #include <stdint.h>
23 #include <cmath>
24 #include <set>
25 #include <string>
26 #include <algorithm>
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"
37 #include "actions.h"
38 #include "editor.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"
43 #include "marker.h"
44 #include "streamview.h"
45 #include "region_gain_line.h"
46 #include "automation_time_axis.h"
47 #include "control_point.h"
48 #include "prompter.h"
49 #include "utils.h"
50 #include "selection.h"
51 #include "keyboard.h"
52 #include "editing.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"
73 #include <bitset>
75 #include "i18n.h"
77 using namespace std;
78 using namespace ARDOUR;
79 using namespace PBD;
80 using namespace Gtk;
81 using namespace Editing;
82 using Gtkmm2ext::Keyboard;
84 bool
85 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
87 int x, y;
88 double wx, wy;
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;
93 if (!canvas_window) {
94 return false;
97 pointer_window = canvas_window->get_pointer (x, y, mask);
99 if (pointer_window == track_canvas->get_bin_window()) {
100 wx = x;
101 wy = y;
102 in_track_canvas = true;
104 } else {
105 in_track_canvas = false;
106 return false;
109 GdkEvent event;
110 event.type = GDK_BUTTON_RELEASE;
111 event.button.x = wx;
112 event.button.y = wy;
114 where = event_frame (&event, 0, 0);
115 return true;
118 nframes64_t
119 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
121 double cx, cy;
123 if (pcx == 0) {
124 pcx = &cx;
126 if (pcy == 0) {
127 pcy = &cy;
130 *pcx = 0;
131 *pcy = 0;
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);
141 break;
142 case GDK_MOTION_NOTIFY:
143 *pcx = event->motion.x;
144 *pcy = event->motion.y;
145 _trackview_group->w2i(*pcx, *pcy);
146 break;
147 case GDK_ENTER_NOTIFY:
148 case GDK_LEAVE_NOTIFY:
149 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
150 break;
151 case GDK_KEY_PRESS:
152 case GDK_KEY_RELEASE:
153 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
154 break;
155 default:
156 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
157 break;
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);
168 Gdk::Cursor*
169 Editor::which_grabber_cursor ()
171 Gdk::Cursor* c = grabber_cursor;
173 if (_internal_editing) {
174 switch (mouse_mode) {
175 case MouseRange:
176 c = midi_pencil_cursor;
177 break;
179 case MouseObject:
180 c = grabber_cursor;
181 break;
183 case MouseTimeFX:
184 c = midi_resize_cursor;
185 break;
187 default:
188 break;
191 } else {
193 switch (_edit_point) {
194 case EditAtMouse:
195 c = grabber_edit_point_cursor;
196 break;
197 default:
198 break;
202 return c;
205 void
206 Editor::set_canvas_cursor ()
208 if (_internal_editing) {
210 switch (mouse_mode) {
211 case MouseRange:
212 current_canvas_cursor = midi_pencil_cursor;
213 break;
215 case MouseObject:
216 current_canvas_cursor = which_grabber_cursor();
217 break;
219 case MouseTimeFX:
220 current_canvas_cursor = midi_resize_cursor;
221 break;
223 default:
224 return;
227 } else {
229 switch (mouse_mode) {
230 case MouseRange:
231 current_canvas_cursor = selector_cursor;
232 break;
234 case MouseObject:
235 current_canvas_cursor = which_grabber_cursor();
236 break;
238 case MouseGain:
239 current_canvas_cursor = cross_hair_cursor;
240 break;
242 case MouseZoom:
243 current_canvas_cursor = zoom_cursor;
244 break;
246 case MouseTimeFX:
247 current_canvas_cursor = time_fx_cursor; // just use playhead
248 break;
250 case MouseAudition:
251 current_canvas_cursor = speaker_cursor;
252 break;
256 switch (_join_object_range_state) {
257 case JOIN_OBJECT_RANGE_NONE:
258 break;
259 case JOIN_OBJECT_RANGE_OBJECT:
260 current_canvas_cursor = which_grabber_cursor ();
261 break;
262 case JOIN_OBJECT_RANGE_RANGE:
263 current_canvas_cursor = selector_cursor;
264 break;
267 if (is_drawable()) {
268 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
272 void
273 Editor::set_mouse_mode (MouseMode m, bool force)
275 if (_drags->active ()) {
276 return;
279 if (!force && m == mouse_mode) {
280 return;
283 Glib::RefPtr<Action> act;
285 switch (m) {
286 case MouseRange:
287 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
288 break;
290 case MouseObject:
291 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
292 break;
294 case MouseGain:
295 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
296 break;
298 case MouseZoom:
299 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
300 break;
302 case MouseTimeFX:
303 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
304 break;
306 case MouseAudition:
307 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
308 break;
311 assert (act);
313 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
314 assert (tact);
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);
321 void
322 Editor::mouse_mode_toggled (MouseMode m)
324 mouse_mode = m;
326 instant_save ();
328 cerr << "Mouse mode toggled to " << m << endl;
330 if (!internal_editing()) {
331 if (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
333 /* in all modes except range and joined object/range, hide the range selection,
334 show the object (region) selection.
337 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
338 (*i)->set_should_show_selection (true);
340 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
341 (*i)->hide_selection ();
344 } else {
347 in range or object/range mode, show the range selection.
350 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
351 (*i)->show_selection (selection->time);
356 set_canvas_cursor ();
359 void
360 Editor::step_mouse_mode (bool next)
362 switch (current_mouse_mode()) {
363 case MouseObject:
364 if (next) {
365 if (Profile->get_sae()) {
366 set_mouse_mode (MouseZoom);
367 } else {
368 set_mouse_mode (MouseRange);
370 } else {
371 set_mouse_mode (MouseTimeFX);
373 break;
375 case MouseRange:
376 if (next) set_mouse_mode (MouseZoom);
377 else set_mouse_mode (MouseObject);
378 break;
380 case MouseZoom:
381 if (next) {
382 if (Profile->get_sae()) {
383 set_mouse_mode (MouseTimeFX);
384 } else {
385 set_mouse_mode (MouseGain);
387 } else {
388 if (Profile->get_sae()) {
389 set_mouse_mode (MouseObject);
390 } else {
391 set_mouse_mode (MouseRange);
394 break;
396 case MouseGain:
397 if (next) set_mouse_mode (MouseTimeFX);
398 else set_mouse_mode (MouseZoom);
399 break;
401 case MouseTimeFX:
402 if (next) {
403 set_mouse_mode (MouseAudition);
404 } else {
405 if (Profile->get_sae()) {
406 set_mouse_mode (MouseZoom);
407 } else {
408 set_mouse_mode (MouseGain);
411 break;
413 case MouseAudition:
414 if (next) set_mouse_mode (MouseObject);
415 else set_mouse_mode (MouseTimeFX);
416 break;
420 void
421 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
423 /* in object/audition/timefx/gain-automation mode,
424 any button press sets the selection if the object
425 can be selected. this is a bit of hack, because
426 we want to avoid this if the mouse operation is a
427 region alignment.
429 note: not dbl-click or triple-click
432 if (((mouse_mode != MouseObject) &&
433 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
434 (mouse_mode != MouseAudition || item_type != RegionItem) &&
435 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
436 (mouse_mode != MouseGain) &&
437 (mouse_mode != MouseRange)) ||
439 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
441 return;
444 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
446 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
448 /* almost no selection action on modified button-2 or button-3 events */
450 if (item_type != RegionItem && event->button.button != 2) {
451 return;
456 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
457 bool press = (event->type == GDK_BUTTON_PRESS);
459 // begin_reversible_command (_("select on click"));
461 switch (item_type) {
462 case RegionItem:
463 if (mouse_mode != MouseRange || internal_editing() || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
464 set_selected_regionview_from_click (press, op, true);
465 } else if (event->type == GDK_BUTTON_PRESS) {
466 selection->clear_tracks ();
467 set_selected_track_as_side_effect (true);
469 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
470 clicked_selection = select_range_around_region (selection->regions.front());
473 break;
475 case RegionViewNameHighlight:
476 case RegionViewName:
477 case LeftFrameHandle:
478 case RightFrameHandle:
479 if (mouse_mode != MouseRange || internal_editing() || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
480 set_selected_regionview_from_click (press, op, true);
481 } else if (event->type == GDK_BUTTON_PRESS) {
482 set_selected_track_as_side_effect ();
484 break;
487 case FadeInHandleItem:
488 case FadeInItem:
489 case FadeOutHandleItem:
490 case FadeOutItem:
491 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
492 set_selected_regionview_from_click (press, op, true);
493 } else if (event->type == GDK_BUTTON_PRESS) {
494 set_selected_track_as_side_effect ();
496 break;
498 case ControlPointItem:
499 set_selected_track_as_side_effect ();
500 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
501 set_selected_control_point_from_click (op, false);
503 break;
505 case StreamItem:
506 /* for context click, select track */
507 if (event->button.button == 3) {
508 selection->clear_tracks ();
509 set_selected_track_as_side_effect (true);
511 break;
513 case AutomationTrackItem:
514 set_selected_track_as_side_effect (true);
515 break;
517 default:
518 break;
522 bool
523 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
525 if (_drags->active ()) {
526 _drags->abort ();
529 /* single mouse clicks on any of these item types operate
530 independent of mouse mode, mostly because they are
531 not on the main track canvas or because we want
532 them to be modeless.
535 switch (item_type) {
536 case PlayheadCursorItem:
537 _drags->set (new CursorDrag (this, item, true), event);
538 return true;
540 case MarkerItem:
541 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
542 hide_marker (item, event);
543 } else {
544 _drags->set (new MarkerDrag (this, item), event);
546 return true;
548 case TempoMarkerItem:
549 _drags->set (
550 new TempoMarkerDrag (
551 this,
552 item,
553 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
555 event
557 return true;
559 case MeterMarkerItem:
560 _drags->set (
561 new MeterMarkerDrag (
562 this,
563 item,
564 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
566 event
568 return true;
570 case MarkerBarItem:
571 case TempoBarItem:
572 case MeterBarItem:
573 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
574 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
576 return true;
577 break;
580 case RangeMarkerBarItem:
581 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
582 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
583 } else {
584 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
586 return true;
587 break;
589 case CdMarkerBarItem:
590 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
591 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
592 } else {
593 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
595 return true;
596 break;
598 case TransportMarkerBarItem:
599 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
600 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
601 } else {
602 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
604 return true;
605 break;
607 default:
608 break;
611 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
612 /* special case: allow trim of range selections in joined object mode;
613 in theory eff should equal MouseRange in this case, but it doesn't
614 because entering the range selection canvas item results in entered_regionview
615 being set to 0, so update_join_object_range_location acts as if we aren't
616 over a region.
618 if (item_type == StartSelectionTrimItem) {
619 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
620 } else if (item_type == EndSelectionTrimItem) {
621 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
625 Editing::MouseMode eff = effective_mouse_mode ();
627 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
628 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
629 eff = MouseObject;
632 switch (eff) {
633 case MouseRange:
634 switch (item_type) {
635 case StartSelectionTrimItem:
636 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
637 break;
639 case EndSelectionTrimItem:
640 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
641 break;
643 case SelectionItem:
644 if (Keyboard::modifier_state_contains
645 (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
646 // contains and not equals because I can't use alt as a modifier alone.
647 start_selection_grab (item, event);
648 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
649 /* grab selection for moving */
650 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
651 } else {
652 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
653 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
654 if (tvp.first) {
655 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
656 if (join_object_range_button.get_active() && atv) {
657 /* smart "join" mode: drag automation */
658 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
659 } else {
660 /* this was debated, but decided the more common action was to
661 make a new selection */
662 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
666 break;
668 case NoteItem:
669 if (internal_editing()) {
670 /* trim notes if we're in internal edit mode and near the ends of the note */
671 _drags->set (new NoteResizeDrag (this, item), event);
673 return true;
675 case StreamItem:
676 if (internal_editing()) {
677 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
678 return true;
679 } else {
680 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
681 return true;
683 break;
685 case RegionViewNameHighlight:
686 case LeftFrameHandle:
687 case RightFrameHandle:
689 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
690 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
691 return true;
692 break;
695 default:
696 if (!internal_editing()) {
697 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
700 return true;
701 break;
703 case MouseObject:
704 switch (item_type) {
705 case NoteItem:
706 if (internal_editing()) {
707 _drags->set (new NoteDrag (this, item), event);
708 return true;
710 break;
712 default:
713 break;
716 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
717 event->type == GDK_BUTTON_PRESS) {
719 _drags->set (new RubberbandSelectDrag (this, item), event);
721 } else if (event->type == GDK_BUTTON_PRESS) {
723 switch (item_type) {
724 case FadeInHandleItem:
726 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
727 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
728 return true;
731 case FadeOutHandleItem:
733 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
734 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
735 return true;
738 case RegionItem:
739 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
740 add_region_copy_drag (item, event, clicked_regionview);
741 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
742 add_region_brush_drag (item, event, clicked_regionview);
743 } else {
744 add_region_drag (item, event, clicked_regionview);
747 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
748 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
751 _drags->start_grab (event);
752 break;
754 case RegionViewNameHighlight:
755 case LeftFrameHandle:
756 case RightFrameHandle:
758 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
759 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
760 return true;
761 break;
764 case RegionViewName:
766 /* rename happens on edit clicks */
767 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
768 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
769 return true;
770 break;
773 case ControlPointItem:
774 _drags->set (new ControlPointDrag (this, item), event);
775 return true;
776 break;
778 case AutomationLineItem:
779 _drags->set (new LineDrag (this, item), event);
780 return true;
781 break;
783 case StreamItem:
784 if (internal_editing()) {
785 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
786 return true;
787 } else {
788 _drags->set (new RubberbandSelectDrag (this, item), event);
790 break;
792 case AutomationTrackItem:
793 /* rubberband drag to select automation points */
794 _drags->set (new RubberbandSelectDrag (this, item), event);
795 break;
797 case SelectionItem:
799 if (join_object_range_button.get_active()) {
800 /* we're in "smart" joined mode, and we've clicked on a Selection */
801 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
802 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
803 if (tvp.first) {
804 /* if we're over an automation track, start a drag of its data */
805 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
806 if (atv) {
807 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
810 /* if we're over a track and a region, and in the `object' part of a region,
811 put a selection around the region and drag both
813 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
814 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
815 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
816 if (t) {
817 boost::shared_ptr<Playlist> pl = t->playlist ();
818 if (pl) {
820 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
821 if (r) {
822 RegionView* rv = rtv->view()->find_view (r);
823 clicked_selection = select_range_around_region (rv);
824 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
825 list<RegionView*> rvs;
826 rvs.push_back (rv);
827 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
828 _drags->start_grab (event);
835 break;
838 #ifdef WITH_CMT
839 case ImageFrameHandleStartItem:
840 imageframe_start_handle_op(item, event) ;
841 return(true) ;
842 break ;
843 case ImageFrameHandleEndItem:
844 imageframe_end_handle_op(item, event) ;
845 return(true) ;
846 break ;
847 case MarkerViewHandleStartItem:
848 markerview_item_start_handle_op(item, event) ;
849 return(true) ;
850 break ;
851 case MarkerViewHandleEndItem:
852 markerview_item_end_handle_op(item, event) ;
853 return(true) ;
854 break ;
855 case MarkerViewItem:
856 start_markerview_grab(item, event) ;
857 break ;
858 case ImageFrameItem:
859 start_imageframe_grab(item, event) ;
860 break ;
861 #endif
863 case MarkerBarItem:
865 break;
867 default:
868 break;
871 return true;
872 break;
874 case MouseGain:
875 switch (item_type) {
876 case RegionItem:
877 /* start a grab so that if we finish after moving
878 we can tell what happened.
880 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
881 break;
883 case GainLineItem:
884 _drags->set (new LineDrag (this, item), event);
885 return true;
887 case ControlPointItem:
888 _drags->set (new ControlPointDrag (this, item), event);
889 return true;
890 break;
892 default:
893 break;
895 return true;
896 break;
898 switch (item_type) {
899 case ControlPointItem:
900 _drags->set (new ControlPointDrag (this, item), event);
901 break;
903 case AutomationLineItem:
904 _drags->set (new LineDrag (this, item), event);
905 break;
907 case RegionItem:
908 // XXX need automation mode to identify which
909 // line to use
910 // start_line_grab_from_regionview (item, event);
911 break;
913 default:
914 break;
916 return true;
917 break;
919 case MouseZoom:
920 if (event->type == GDK_BUTTON_PRESS) {
921 _drags->set (new MouseZoomDrag (this, item), event);
924 return true;
925 break;
927 case MouseTimeFX:
928 if (internal_editing() && item_type == NoteItem) {
929 /* drag notes if we're in internal edit mode */
930 _drags->set (new NoteResizeDrag (this, item), event);
931 return true;
932 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
933 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
934 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
935 return true;
937 break;
939 case MouseAudition:
940 _drags->set (new ScrubDrag (this, item), event);
941 scrub_reversals = 0;
942 scrub_reverse_distance = 0;
943 last_scrub_x = event->button.x;
944 scrubbing_direction = 0;
945 track_canvas->get_window()->set_cursor (*transparent_cursor);
946 return true;
947 break;
949 default:
950 break;
953 return false;
956 bool
957 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
959 Editing::MouseMode const eff = effective_mouse_mode ();
960 switch (eff) {
961 case MouseObject:
962 switch (item_type) {
963 case RegionItem:
964 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
965 add_region_copy_drag (item, event, clicked_regionview);
966 } else {
967 add_region_drag (item, event, clicked_regionview);
969 _drags->start_grab (event);
970 return true;
971 break;
972 case ControlPointItem:
973 _drags->set (new ControlPointDrag (this, item), event);
974 return true;
975 break;
977 default:
978 break;
981 switch (item_type) {
982 case RegionViewNameHighlight:
983 case LeftFrameHandle:
984 case RightFrameHandle:
985 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
986 return true;
987 break;
989 case RegionViewName:
990 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
991 return true;
992 break;
994 default:
995 break;
998 break;
1000 case MouseRange:
1001 /* relax till release */
1002 return true;
1003 break;
1006 case MouseZoom:
1007 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1008 temporal_zoom_session();
1009 } else {
1010 temporal_zoom_to_frame (true, event_frame(event));
1012 return true;
1013 break;
1015 default:
1016 break;
1019 return false;
1022 bool
1023 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1025 if (event->type != GDK_BUTTON_PRESS) {
1026 return false;
1029 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1031 if (canvas_window) {
1032 Glib::RefPtr<const Gdk::Window> pointer_window;
1033 int x, y;
1034 double wx, wy;
1035 Gdk::ModifierType mask;
1037 pointer_window = canvas_window->get_pointer (x, y, mask);
1039 if (pointer_window == track_canvas->get_bin_window()) {
1040 track_canvas->window_to_world (x, y, wx, wy);
1041 allow_vertical_scroll = true;
1042 } else {
1043 allow_vertical_scroll = false;
1047 track_canvas->grab_focus();
1049 if (_session && _session->actively_recording()) {
1050 return true;
1053 button_selection (item, event, item_type);
1055 if (!_drags->active () &&
1056 (Keyboard::is_delete_event (&event->button) ||
1057 Keyboard::is_context_menu_event (&event->button) ||
1058 Keyboard::is_edit_event (&event->button))) {
1060 /* handled by button release */
1061 return true;
1064 switch (event->button.button) {
1065 case 1:
1066 return button_press_handler_1 (item, event, item_type);
1067 break;
1069 case 2:
1070 return button_press_handler_2 (item, event, item_type);
1071 break;
1073 case 3:
1074 break;
1076 default:
1077 break;
1081 return false;
1084 bool
1085 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1087 nframes64_t where = event_frame (event, 0, 0);
1088 AutomationTimeAxisView* atv = 0;
1090 /* no action if we're recording */
1092 if (_session && _session->actively_recording()) {
1093 return true;
1096 /* first, see if we're finishing a drag ... */
1098 bool were_dragging = false;
1099 if (_drags->active ()) {
1100 bool const r = _drags->end_grab (event);
1101 if (r) {
1102 /* grab dragged, so do nothing else */
1103 return true;
1106 were_dragging = true;
1109 button_selection (item, event, item_type);
1111 /* edit events get handled here */
1113 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1114 switch (item_type) {
1115 case RegionItem:
1116 edit_region ();
1117 break;
1119 case TempoMarkerItem:
1120 edit_tempo_marker (item);
1121 break;
1123 case MeterMarkerItem:
1124 edit_meter_marker (item);
1125 break;
1127 case RegionViewName:
1128 if (clicked_regionview->name_active()) {
1129 return mouse_rename_region (item, event);
1131 break;
1133 case ControlPointItem:
1134 edit_control_point (item);
1135 break;
1137 default:
1138 break;
1140 return true;
1143 /* context menu events get handled here */
1145 if (Keyboard::is_context_menu_event (&event->button)) {
1147 if (!_drags->active ()) {
1149 /* no matter which button pops up the context menu, tell the menu
1150 widget to use button 1 to drive menu selection.
1153 switch (item_type) {
1154 case FadeInItem:
1155 case FadeInHandleItem:
1156 case FadeOutItem:
1157 case FadeOutHandleItem:
1158 popup_fade_context_menu (1, event->button.time, item, item_type);
1159 break;
1161 case StreamItem:
1162 popup_track_context_menu (1, event->button.time, item_type, false, where);
1163 break;
1165 case RegionItem:
1166 case RegionViewNameHighlight:
1167 case LeftFrameHandle:
1168 case RightFrameHandle:
1169 case RegionViewName:
1170 popup_track_context_menu (1, event->button.time, item_type, false, where);
1171 break;
1173 case SelectionItem:
1174 popup_track_context_menu (1, event->button.time, item_type, true, where);
1175 break;
1177 case AutomationTrackItem:
1178 popup_track_context_menu (1, event->button.time, item_type, false, where);
1179 break;
1181 case MarkerBarItem:
1182 case RangeMarkerBarItem:
1183 case TransportMarkerBarItem:
1184 case CdMarkerBarItem:
1185 case TempoBarItem:
1186 case MeterBarItem:
1187 popup_ruler_menu (where, item_type);
1188 break;
1190 case MarkerItem:
1191 marker_context_menu (&event->button, item);
1192 break;
1194 case TempoMarkerItem:
1195 tempo_or_meter_marker_context_menu (&event->button, item);
1196 break;
1198 case MeterMarkerItem:
1199 tempo_or_meter_marker_context_menu (&event->button, item);
1200 break;
1202 case CrossfadeViewItem:
1203 popup_track_context_menu (1, event->button.time, item_type, false, where);
1204 break;
1206 #ifdef WITH_CMT
1207 case ImageFrameItem:
1208 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1209 break ;
1210 case ImageFrameTimeAxisItem:
1211 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1212 break ;
1213 case MarkerViewItem:
1214 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1215 break ;
1216 case MarkerTimeAxisItem:
1217 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1218 break ;
1219 #endif
1221 default:
1222 break;
1225 return true;
1229 /* delete events get handled here */
1231 Editing::MouseMode const eff = effective_mouse_mode ();
1233 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1235 switch (item_type) {
1236 case TempoMarkerItem:
1237 remove_tempo_marker (item);
1238 break;
1240 case MeterMarkerItem:
1241 remove_meter_marker (item);
1242 break;
1244 case MarkerItem:
1245 remove_marker (*item, event);
1246 break;
1248 case RegionItem:
1249 if (eff == MouseObject) {
1250 remove_clicked_region ();
1252 break;
1254 case ControlPointItem:
1255 if (eff == MouseGain) {
1256 remove_gain_control_point (item, event);
1257 } else {
1258 remove_control_point (item, event);
1260 break;
1262 case NoteItem:
1263 remove_midi_note (item, event);
1264 break;
1266 default:
1267 break;
1269 return true;
1272 switch (event->button.button) {
1273 case 1:
1275 switch (item_type) {
1276 /* see comments in button_press_handler */
1277 case PlayheadCursorItem:
1278 case MarkerItem:
1279 case GainLineItem:
1280 case AutomationLineItem:
1281 case StartSelectionTrimItem:
1282 case EndSelectionTrimItem:
1283 return true;
1285 case MarkerBarItem:
1286 if (!_dragging_playhead) {
1287 snap_to_with_modifier (where, event, 0, true);
1288 mouse_add_new_marker (where);
1290 return true;
1292 case CdMarkerBarItem:
1293 if (!_dragging_playhead) {
1294 // if we get here then a dragged range wasn't done
1295 snap_to_with_modifier (where, event, 0, true);
1296 mouse_add_new_marker (where, true);
1298 return true;
1300 case TempoBarItem:
1301 if (!_dragging_playhead) {
1302 snap_to_with_modifier (where, event);
1303 mouse_add_new_tempo_event (where);
1305 return true;
1307 case MeterBarItem:
1308 if (!_dragging_playhead) {
1309 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1311 return true;
1312 break;
1314 default:
1315 break;
1318 switch (eff) {
1319 case MouseObject:
1320 switch (item_type) {
1321 case AutomationTrackItem:
1322 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1323 if (atv) {
1324 atv->add_automation_event (item, event, where, event->button.y);
1326 return true;
1327 break;
1329 default:
1330 break;
1332 break;
1334 case MouseGain:
1335 switch (item_type) {
1336 case RegionItem:
1338 /* check that we didn't drag before releasing, since
1339 its really annoying to create new control
1340 points when doing this.
1342 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1343 if (were_dragging && arv) {
1344 arv->add_gain_point_event (item, event);
1346 return true;
1347 break;
1350 case AutomationTrackItem:
1351 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1352 add_automation_event (item, event, where, event->button.y);
1353 return true;
1354 break;
1355 default:
1356 break;
1358 break;
1360 case MouseAudition:
1361 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1362 if (scrubbing_direction == 0) {
1363 /* no drag, just a click */
1364 switch (item_type) {
1365 case RegionItem:
1366 play_selected_region ();
1367 break;
1368 default:
1369 break;
1371 } else {
1372 /* make sure we stop */
1373 _session->request_transport_speed (0.0);
1375 break;
1377 default:
1378 break;
1382 return true;
1383 break;
1386 case 2:
1387 switch (eff) {
1389 case MouseObject:
1390 switch (item_type) {
1391 case RegionItem:
1392 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1393 raise_region ();
1394 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1395 lower_region ();
1396 } else {
1397 // Button2 click is unused
1399 return true;
1401 break;
1403 default:
1404 break;
1406 break;
1408 case MouseRange:
1410 // x_style_paste (where, 1.0);
1411 return true;
1412 break;
1414 default:
1415 break;
1418 break;
1420 case 3:
1421 break;
1423 default:
1424 break;
1426 return false;
1429 bool
1430 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1432 ControlPoint* cp;
1433 Marker * marker;
1434 double fraction;
1435 bool ret = true;
1437 if (last_item_entered != item) {
1438 last_item_entered = item;
1439 last_item_entered_n = 0;
1442 switch (item_type) {
1443 case ControlPointItem:
1444 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1445 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1446 cp->set_visible (true);
1448 double at_x, at_y;
1449 at_x = cp->get_x();
1450 at_y = cp->get_y ();
1451 cp->i2w (at_x, at_y);
1452 at_x += 10.0;
1453 at_y += 10.0;
1455 fraction = 1.0 - (cp->get_y() / cp->line().height());
1457 if (is_drawable() && !_drags->active ()) {
1458 track_canvas->get_window()->set_cursor (*fader_cursor);
1461 last_item_entered_n++;
1462 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1463 if (last_item_entered_n < 10) {
1464 show_verbose_canvas_cursor ();
1467 break;
1469 case GainLineItem:
1470 if (mouse_mode == MouseGain) {
1471 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1472 if (line)
1473 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1474 if (is_drawable()) {
1475 track_canvas->get_window()->set_cursor (*fader_cursor);
1478 break;
1480 case AutomationLineItem:
1481 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1483 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1484 if (line)
1485 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1487 if (is_drawable()) {
1488 track_canvas->get_window()->set_cursor (*fader_cursor);
1491 break;
1493 case RegionViewNameHighlight:
1494 if (is_drawable() && (mouse_mode == MouseObject || (internal_editing() && mouse_mode == MouseRange))) {
1495 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1497 break;
1499 case LeftFrameHandle:
1500 if (is_drawable() && (mouse_mode == MouseObject || (internal_editing() && mouse_mode == MouseRange))) {
1501 track_canvas->get_window()->set_cursor (*left_side_trim_cursor);
1503 break;
1505 case RightFrameHandle:
1506 if (is_drawable() && (mouse_mode == MouseObject || (internal_editing() && mouse_mode == MouseRange))) {
1507 track_canvas->get_window()->set_cursor (*right_side_trim_cursor);
1509 break;
1511 case StartSelectionTrimItem:
1512 case EndSelectionTrimItem:
1514 #ifdef WITH_CMT
1515 case ImageFrameHandleStartItem:
1516 case ImageFrameHandleEndItem:
1517 case MarkerViewHandleStartItem:
1518 case MarkerViewHandleEndItem:
1519 #endif
1521 if (is_drawable()) {
1522 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1524 break;
1526 case PlayheadCursorItem:
1527 if (is_drawable()) {
1528 switch (_edit_point) {
1529 case EditAtMouse:
1530 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1531 break;
1532 default:
1533 track_canvas->get_window()->set_cursor (*grabber_cursor);
1534 break;
1537 break;
1539 case RegionViewName:
1541 /* when the name is not an active item, the entire name highlight is for trimming */
1543 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1544 if (mouse_mode == MouseObject && is_drawable()) {
1545 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1548 break;
1551 case AutomationTrackItem:
1552 if (is_drawable()) {
1553 Gdk::Cursor *cursor;
1554 switch (mouse_mode) {
1555 case MouseRange:
1556 cursor = selector_cursor;
1557 break;
1558 case MouseZoom:
1559 cursor = zoom_cursor;
1560 break;
1561 default:
1562 cursor = cross_hair_cursor;
1563 break;
1566 track_canvas->get_window()->set_cursor (*cursor);
1568 AutomationTimeAxisView* atv;
1569 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1570 clear_entered_track = false;
1571 set_entered_track (atv);
1574 break;
1576 case MarkerBarItem:
1577 case RangeMarkerBarItem:
1578 case TransportMarkerBarItem:
1579 case CdMarkerBarItem:
1580 case MeterBarItem:
1581 case TempoBarItem:
1582 if (is_drawable()) {
1583 track_canvas->get_window()->set_cursor (*timebar_cursor);
1585 break;
1587 case MarkerItem:
1588 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1589 break;
1591 entered_marker = marker;
1592 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1593 // fall through
1594 case MeterMarkerItem:
1595 case TempoMarkerItem:
1596 if (is_drawable()) {
1597 track_canvas->get_window()->set_cursor (*timebar_cursor);
1599 break;
1601 case FadeInHandleItem:
1602 if (mouse_mode == MouseObject) {
1603 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1604 if (rect) {
1605 rect->property_fill_color_rgba() = 0;
1606 rect->property_outline_pixels() = 1;
1608 track_canvas->get_window()->set_cursor (*fade_in_cursor);
1610 break;
1612 case FadeOutHandleItem:
1613 if (mouse_mode == MouseObject) {
1614 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1615 if (rect) {
1616 rect->property_fill_color_rgba() = 0;
1617 rect->property_outline_pixels() = 1;
1619 track_canvas->get_window()->set_cursor (*fade_out_cursor);
1621 break;
1623 default:
1624 break;
1627 /* second pass to handle entered track status in a comprehensible way.
1630 switch (item_type) {
1631 case GainLineItem:
1632 case AutomationLineItem:
1633 case ControlPointItem:
1634 /* these do not affect the current entered track state */
1635 clear_entered_track = false;
1636 break;
1638 case AutomationTrackItem:
1639 /* handled above already */
1640 break;
1642 default:
1643 set_entered_track (0);
1644 break;
1647 return ret;
1650 bool
1651 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1653 AutomationLine* al;
1654 ControlPoint* cp;
1655 Marker *marker;
1656 Location *loc;
1657 RegionView* rv;
1658 bool is_start;
1659 bool ret = true;
1661 switch (item_type) {
1662 case ControlPointItem:
1663 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1664 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1665 if (cp->line().npoints() > 1 && !cp->selected()) {
1666 cp->set_visible (false);
1670 if (is_drawable()) {
1671 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1674 hide_verbose_canvas_cursor ();
1675 break;
1677 case RegionViewNameHighlight:
1678 case LeftFrameHandle:
1679 case RightFrameHandle:
1680 case StartSelectionTrimItem:
1681 case EndSelectionTrimItem:
1682 case PlayheadCursorItem:
1684 #ifdef WITH_CMT
1685 case ImageFrameHandleStartItem:
1686 case ImageFrameHandleEndItem:
1687 case MarkerViewHandleStartItem:
1688 case MarkerViewHandleEndItem:
1689 #endif
1691 if (is_drawable()) {
1692 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1694 break;
1696 case GainLineItem:
1697 case AutomationLineItem:
1698 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1700 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1701 if (line)
1702 line->property_fill_color_rgba() = al->get_line_color();
1704 if (is_drawable()) {
1705 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1707 break;
1709 case RegionViewName:
1710 /* see enter_handler() for notes */
1711 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1712 if (is_drawable() && mouse_mode == MouseObject) {
1713 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1716 break;
1718 case RangeMarkerBarItem:
1719 case TransportMarkerBarItem:
1720 case CdMarkerBarItem:
1721 case MeterBarItem:
1722 case TempoBarItem:
1723 case MarkerBarItem:
1724 if (is_drawable()) {
1725 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1727 break;
1729 case MarkerItem:
1730 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1731 break;
1733 entered_marker = 0;
1734 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1735 location_flags_changed (loc, this);
1737 // fall through
1738 case MeterMarkerItem:
1739 case TempoMarkerItem:
1741 if (is_drawable()) {
1742 track_canvas->get_window()->set_cursor (*timebar_cursor);
1745 break;
1747 case FadeInHandleItem:
1748 case FadeOutHandleItem:
1749 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1751 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1752 if (rect) {
1753 rect->property_fill_color_rgba() = rv->get_fill_color();
1754 rect->property_outline_pixels() = 0;
1757 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1758 break;
1760 case AutomationTrackItem:
1761 if (is_drawable()) {
1762 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1763 clear_entered_track = true;
1764 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1766 break;
1768 default:
1769 break;
1772 return ret;
1775 gint
1776 Editor::left_automation_track ()
1778 if (clear_entered_track) {
1779 set_entered_track (0);
1780 clear_entered_track = false;
1782 return false;
1785 void
1786 Editor::scrub (nframes64_t frame, double current_x)
1788 double delta;
1790 if (scrubbing_direction == 0) {
1791 /* first move */
1792 _session->request_locate (frame, false);
1793 _session->request_transport_speed (0.1);
1794 scrubbing_direction = 1;
1796 } else {
1798 if (last_scrub_x > current_x) {
1800 /* pointer moved to the left */
1802 if (scrubbing_direction > 0) {
1804 /* we reversed direction to go backwards */
1806 scrub_reversals++;
1807 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1809 } else {
1811 /* still moving to the left (backwards) */
1813 scrub_reversals = 0;
1814 scrub_reverse_distance = 0;
1816 delta = 0.01 * (last_scrub_x - current_x);
1817 _session->request_transport_speed (_session->transport_speed() - delta);
1820 } else {
1821 /* pointer moved to the right */
1823 if (scrubbing_direction < 0) {
1824 /* we reversed direction to go forward */
1826 scrub_reversals++;
1827 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1829 } else {
1830 /* still moving to the right */
1832 scrub_reversals = 0;
1833 scrub_reverse_distance = 0;
1835 delta = 0.01 * (current_x - last_scrub_x);
1836 _session->request_transport_speed (_session->transport_speed() + delta);
1840 /* if there have been more than 2 opposite motion moves detected, or one that moves
1841 back more than 10 pixels, reverse direction
1844 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1846 if (scrubbing_direction > 0) {
1847 /* was forwards, go backwards */
1848 _session->request_transport_speed (-0.1);
1849 scrubbing_direction = -1;
1850 } else {
1851 /* was backwards, go forwards */
1852 _session->request_transport_speed (0.1);
1853 scrubbing_direction = 1;
1856 scrub_reverse_distance = 0;
1857 scrub_reversals = 0;
1861 last_scrub_x = current_x;
1864 bool
1865 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1867 if (event->motion.is_hint) {
1868 gint x, y;
1870 /* We call this so that MOTION_NOTIFY events continue to be
1871 delivered to the canvas. We need to do this because we set
1872 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1873 the density of the events, at the expense of a round-trip
1874 to the server. Given that this will mostly occur on cases
1875 where DISPLAY = :0.0, and given the cost of what the motion
1876 event might do, its a good tradeoff.
1879 track_canvas->get_pointer (x, y);
1882 if (current_stepping_trackview) {
1883 /* don't keep the persistent stepped trackview if the mouse moves */
1884 current_stepping_trackview = 0;
1885 step_timeout.disconnect ();
1888 if (_session && _session->actively_recording()) {
1889 /* Sorry. no dragging stuff around while we record */
1890 return true;
1893 JoinObjectRangeState const old = _join_object_range_state;
1894 update_join_object_range_location (event->motion.x, event->motion.y);
1895 if (_join_object_range_state != old) {
1896 set_canvas_cursor ();
1899 bool handled = false;
1900 if (_drags->active ()) {
1901 handled = _drags->motion_handler (event, from_autoscroll);
1903 if (!handled) {
1904 return false;
1907 track_canvas_motion (event);
1908 return true;
1911 void
1912 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1914 ControlPoint* control_point;
1916 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1917 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1918 /*NOTREACHED*/
1921 // We shouldn't remove the first or last gain point
1922 if (control_point->line().is_last_point(*control_point) ||
1923 control_point->line().is_first_point(*control_point)) {
1924 return;
1927 control_point->line().remove_point (*control_point);
1930 void
1931 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1933 ControlPoint* control_point;
1935 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1936 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1937 /*NOTREACHED*/
1940 control_point->line().remove_point (*control_point);
1943 void
1944 Editor::edit_control_point (ArdourCanvas::Item* item)
1946 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1948 if (p == 0) {
1949 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1950 /*NOTREACHED*/
1953 ControlPointDialog d (p);
1954 d.set_position (Gtk::WIN_POS_MOUSE);
1955 ensure_float (d);
1957 if (d.run () != RESPONSE_ACCEPT) {
1958 return;
1961 p->line().modify_point_y (*p, d.get_y_fraction ());
1965 void
1966 Editor::visible_order_range (int* low, int* high) const
1968 *low = TimeAxisView::max_order ();
1969 *high = 0;
1971 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1973 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1975 if (!rtv->hidden()) {
1977 if (*high < rtv->order()) {
1978 *high = rtv->order ();
1981 if (*low > rtv->order()) {
1982 *low = rtv->order ();
1988 void
1989 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1991 /* Either add to or set the set the region selection, unless
1992 this is an alignment click (control used)
1995 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1996 TimeAxisView* tv = &rv.get_time_axis_view();
1997 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1998 double speed = 1.0;
1999 if (rtv && rtv->is_track()) {
2000 speed = rtv->track()->speed();
2003 nframes64_t where = get_preferred_edit_position();
2005 if (where >= 0) {
2007 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2009 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
2011 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2013 align_region (rv.region(), End, (nframes64_t) (where * speed));
2015 } else {
2017 align_region (rv.region(), Start, (nframes64_t) (where * speed));
2023 void
2024 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
2026 char buf[128];
2027 Timecode::Time timecode;
2028 BBT_Time bbt;
2029 int hours, mins;
2030 nframes64_t frame_rate;
2031 float secs;
2033 if (_session == 0) {
2034 return;
2037 AudioClock::Mode m;
2039 if (Profile->get_sae() || Profile->get_small_screen()) {
2040 m = ARDOUR_UI::instance()->primary_clock.mode();
2041 } else {
2042 m = ARDOUR_UI::instance()->secondary_clock.mode();
2045 switch (m) {
2046 case AudioClock::BBT:
2047 _session->bbt_time (frame, bbt);
2048 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2049 break;
2051 case AudioClock::Timecode:
2052 _session->timecode_time (frame, timecode);
2053 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2054 break;
2056 case AudioClock::MinSec:
2057 /* XXX this is copied from show_verbose_duration_cursor() */
2058 frame_rate = _session->frame_rate();
2059 hours = frame / (frame_rate * 3600);
2060 frame = frame % (frame_rate * 3600);
2061 mins = frame / (frame_rate * 60);
2062 frame = frame % (frame_rate * 60);
2063 secs = (float) frame / (float) frame_rate;
2064 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2065 break;
2067 default:
2068 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2069 break;
2072 if (xpos >= 0 && ypos >=0) {
2073 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2074 } else {
2075 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_position(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2077 show_verbose_canvas_cursor ();
2080 void
2081 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
2083 char buf[128];
2084 Timecode::Time timecode;
2085 BBT_Time sbbt;
2086 BBT_Time ebbt;
2087 int hours, mins;
2088 nframes64_t distance, frame_rate;
2089 float secs;
2090 Meter meter_at_start(_session->tempo_map().meter_at(start));
2092 if (_session == 0) {
2093 return;
2096 AudioClock::Mode m;
2098 if (Profile->get_sae() || Profile->get_small_screen()) {
2099 m = ARDOUR_UI::instance()->primary_clock.mode ();
2100 } else {
2101 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2104 switch (m) {
2105 case AudioClock::BBT:
2106 _session->bbt_time (start, sbbt);
2107 _session->bbt_time (end, ebbt);
2109 /* subtract */
2110 /* XXX this computation won't work well if the
2111 user makes a selection that spans any meter changes.
2114 ebbt.bars -= sbbt.bars;
2115 if (ebbt.beats >= sbbt.beats) {
2116 ebbt.beats -= sbbt.beats;
2117 } else {
2118 ebbt.bars--;
2119 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2121 if (ebbt.ticks >= sbbt.ticks) {
2122 ebbt.ticks -= sbbt.ticks;
2123 } else {
2124 ebbt.beats--;
2125 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2128 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2129 break;
2131 case AudioClock::Timecode:
2132 _session->timecode_duration (end - start, timecode);
2133 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2134 break;
2136 case AudioClock::MinSec:
2137 /* XXX this stuff should be elsewhere.. */
2138 distance = end - start;
2139 frame_rate = _session->frame_rate();
2140 hours = distance / (frame_rate * 3600);
2141 distance = distance % (frame_rate * 3600);
2142 mins = distance / (frame_rate * 60);
2143 distance = distance % (frame_rate * 60);
2144 secs = (float) distance / (float) frame_rate;
2145 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2146 break;
2148 default:
2149 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2150 break;
2153 if (xpos >= 0 && ypos >=0) {
2154 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2156 else {
2157 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2160 show_verbose_canvas_cursor ();
2163 void
2164 Editor::collect_new_region_view (RegionView* rv)
2166 latest_regionviews.push_back (rv);
2169 void
2170 Editor::collect_and_select_new_region_view (RegionView* rv)
2172 selection->add(rv);
2173 latest_regionviews.push_back (rv);
2176 void
2177 Editor::cancel_selection ()
2179 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2180 (*i)->hide_selection ();
2183 selection->clear ();
2184 clicked_selection = 0;
2188 void
2189 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction)
2191 boost::shared_ptr<Region> region (rv.region());
2193 if (region->locked()) {
2194 return;
2197 nframes64_t new_bound;
2199 double speed = 1.0;
2200 TimeAxisView* tvp = clicked_axisview;
2201 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2203 if (tv && tv->is_track()) {
2204 speed = tv->track()->speed();
2207 if (left_direction) {
2208 if (swap_direction) {
2209 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2210 } else {
2211 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2213 } else {
2214 if (swap_direction) {
2215 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2216 } else {
2217 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2221 region->trim_start ((nframes64_t) (new_bound * speed), this);
2222 rv.region_changed (PropertyChange (ARDOUR::Properties::start));
2225 void
2226 Editor::single_start_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2228 boost::shared_ptr<Region> region (rv.region());
2230 if (region->locked()) {
2231 return;
2234 double speed = 1.0;
2235 TimeAxisView* tvp = clicked_axisview;
2236 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2238 if (tv && tv->is_track()) {
2239 speed = tv->track()->speed();
2242 nframes64_t pre_trim_first_frame = region->first_frame();
2244 region->trim_front ((nframes64_t) (new_bound * speed), this);
2246 if (no_overlap) {
2247 //Get the next region on the left of this region and shrink/expand it.
2248 boost::shared_ptr<Playlist> playlist (region->playlist());
2249 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2251 bool regions_touching = false;
2253 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2254 regions_touching = true;
2257 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2258 if (region_left != 0 &&
2259 (region_left->last_frame() > region->first_frame() || regions_touching))
2261 region_left->trim_end(region->first_frame() - 1, this);
2265 rv.region_changed (ARDOUR::bounds_change);
2268 void
2269 Editor::single_end_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
2271 boost::shared_ptr<Region> region (rv.region());
2273 if (region->locked()) {
2274 return;
2277 double speed = 1.0;
2278 TimeAxisView* tvp = clicked_axisview;
2279 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2281 if (tv && tv->is_track()) {
2282 speed = tv->track()->speed();
2285 nframes64_t pre_trim_last_frame = region->last_frame();
2287 region->trim_end ((nframes64_t) (new_bound * speed), this);
2289 if (no_overlap) {
2290 //Get the next region on the right of this region and shrink/expand it.
2291 boost::shared_ptr<Playlist> playlist (region->playlist());
2292 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2294 bool regions_touching = false;
2296 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)) {
2297 regions_touching = true;
2300 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2301 if (region_right != 0 &&
2302 (region_right->first_frame() < region->last_frame() || regions_touching))
2304 region_right->trim_front(region->last_frame() + 1, this);
2307 rv.region_changed (ARDOUR::bounds_change);
2309 } else {
2310 rv.region_changed (PropertyChange (ARDOUR::Properties::length));
2315 void
2316 Editor::point_trim (GdkEvent* event, nframes64_t new_bound)
2318 RegionView* rv = clicked_regionview;
2320 /* Choose action dependant on which button was pressed */
2321 switch (event->button.button) {
2322 case 1:
2323 begin_reversible_command (_("Start point trim"));
2325 if (selection->selected (rv)) {
2326 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2327 i != selection->regions.by_layer().end(); ++i)
2329 if ( (*i) == NULL){
2330 cerr << "region view contains null region" << endl;
2333 if (!(*i)->region()->locked()) {
2334 (*i)->region()->clear_history ();
2335 (*i)->region()->trim_front (new_bound, this);
2336 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2340 } else {
2341 if (!rv->region()->locked()) {
2342 rv->region()->clear_history ();
2343 rv->region()->trim_front (new_bound, this);
2344 _session->add_command(new StatefulDiffCommand (rv->region()));
2348 commit_reversible_command();
2350 break;
2351 case 2:
2352 begin_reversible_command (_("End point trim"));
2354 if (selection->selected (rv)) {
2356 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2358 if (!(*i)->region()->locked()) {
2359 (*i)->region()->clear_history();
2360 (*i)->region()->trim_end (new_bound, this);
2361 _session->add_command(new StatefulDiffCommand ((*i)->region()));
2365 } else {
2367 if (!rv->region()->locked()) {
2368 rv->region()->clear_history ();
2369 rv->region()->trim_end (new_bound, this);
2370 _session->add_command (new StatefulDiffCommand (rv->region()));
2374 commit_reversible_command();
2376 break;
2377 default:
2378 break;
2382 void
2383 Editor::thaw_region_after_trim (RegionView& rv)
2385 boost::shared_ptr<Region> region (rv.region());
2387 if (region->locked()) {
2388 return;
2391 region->resume_property_changes ();
2393 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2395 if (arv) {
2396 arv->unhide_envelope ();
2400 void
2401 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2403 Marker* marker;
2404 bool is_start;
2406 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2407 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2408 /*NOTREACHED*/
2411 Location* location = find_location_from_marker (marker, is_start);
2412 location->set_hidden (true, this);
2416 void
2417 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2419 double x1 = frame_to_pixel (start);
2420 double x2 = frame_to_pixel (end);
2421 double y2 = full_canvas_height - 1.0;
2423 zoom_rect->property_x1() = x1;
2424 zoom_rect->property_y1() = 1.0;
2425 zoom_rect->property_x2() = x2;
2426 zoom_rect->property_y2() = y2;
2430 gint
2431 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2433 using namespace Gtkmm2ext;
2435 ArdourPrompter prompter (false);
2437 prompter.set_prompt (_("Name for region:"));
2438 prompter.set_initial_text (clicked_regionview->region()->name());
2439 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2440 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2441 prompter.show_all ();
2442 switch (prompter.run ()) {
2443 case Gtk::RESPONSE_ACCEPT:
2444 string str;
2445 prompter.get_result(str);
2446 if (str.length()) {
2447 clicked_regionview->region()->set_name (str);
2449 break;
2451 return true;
2455 void
2456 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2458 /* no brushing without a useful snap setting */
2460 switch (_snap_mode) {
2461 case SnapMagnetic:
2462 return; /* can't work because it allows region to be placed anywhere */
2463 default:
2464 break; /* OK */
2467 switch (_snap_type) {
2468 case SnapToMark:
2469 return;
2471 default:
2472 break;
2475 /* don't brush a copy over the original */
2477 if (pos == rv->region()->position()) {
2478 return;
2481 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2483 if (rtv == 0 || !rtv->is_track()) {
2484 return;
2487 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2488 double speed = rtv->track()->speed();
2490 playlist->clear_history ();
2491 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
2492 playlist->add_region (new_region, (nframes64_t) (pos * speed));
2493 _session->add_command (new StatefulDiffCommand (playlist));
2495 // playlist is frozen, so we have to update manually XXX this is disgusting
2497 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2500 gint
2501 Editor::track_height_step_timeout ()
2503 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2504 current_stepping_trackview = 0;
2505 return false;
2507 return true;
2510 void
2511 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2513 assert (region_view);
2515 _region_motion_group->raise_to_top ();
2517 if (Config->get_edit_mode() == Splice) {
2518 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2519 } else {
2520 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2521 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2524 /* sync the canvas to what we think is its current state */
2525 update_canvas_now();
2528 void
2529 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2531 assert (region_view);
2533 _region_motion_group->raise_to_top ();
2535 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2536 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2539 void
2540 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2542 assert (region_view);
2544 if (Config->get_edit_mode() == Splice) {
2545 return;
2548 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2549 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2551 begin_reversible_command (_("Drag region brush"));
2554 /** Start a grab where a time range is selected, track(s) are selected, and the
2555 * user clicks and drags a region with a modifier in order to create a new region containing
2556 * the section of the clicked region that lies within the time range.
2558 void
2559 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2561 if (clicked_regionview == 0) {
2562 return;
2565 /* lets try to create new Region for the selection */
2567 vector<boost::shared_ptr<Region> > new_regions;
2568 create_region_from_selection (new_regions);
2570 if (new_regions.empty()) {
2571 return;
2574 /* XXX fix me one day to use all new regions */
2576 boost::shared_ptr<Region> region (new_regions.front());
2578 /* add it to the current stream/playlist.
2580 tricky: the streamview for the track will add a new regionview. we will
2581 catch the signal it sends when it creates the regionview to
2582 set the regionview we want to then drag.
2585 latest_regionviews.clear();
2586 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2588 /* A selection grab currently creates two undo/redo operations, one for
2589 creating the new region and another for moving it.
2592 begin_reversible_command (_("selection grab"));
2594 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2596 playlist->clear_history ();
2597 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2598 _session->add_command(new StatefulDiffCommand (playlist));
2600 commit_reversible_command ();
2602 c.disconnect ();
2604 if (latest_regionviews.empty()) {
2605 /* something went wrong */
2606 return;
2609 /* we need to deselect all other regionviews, and select this one
2610 i'm ignoring undo stuff, because the region creation will take care of it
2612 selection->set (latest_regionviews);
2614 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2617 void
2618 Editor::escape ()
2620 if (_drags->active ()) {
2621 _drags->abort ();
2622 } else {
2623 selection->clear ();
2627 void
2628 Editor::set_internal_edit (bool yn)
2630 _internal_editing = yn;
2632 if (yn) {
2633 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2634 mouse_select_button.get_image ()->show ();
2635 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2637 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2638 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2639 if (mtv) {
2640 mtv->start_step_editing ();
2644 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2645 (*i)->hide_selection ();
2648 start_step_editing ();
2649 set_canvas_cursor ();
2651 } else {
2653 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2654 mouse_select_button.get_image ()->show ();
2655 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Select/Move Ranges"));
2656 stop_step_editing ();
2658 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2659 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2660 if (mtv) {
2661 mtv->stop_step_editing ();
2665 mouse_mode_toggled (mouse_mode);
2669 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2670 * used by the `join object/range' tool mode.
2672 void
2673 Editor::update_join_object_range_location (double x, double y)
2675 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2676 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2677 that we're over requires searching the playlist.
2680 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2681 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2682 return;
2685 if (mouse_mode == MouseObject) {
2686 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2687 } else if (mouse_mode == MouseRange) {
2688 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2691 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2692 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2694 if (tvp.first) {
2696 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2697 if (rtv) {
2699 double cx = 0;
2700 double cy = y;
2701 rtv->canvas_display()->w2i (cx, cy);
2703 bool const top_half = cy < rtv->current_height () / 2;
2705 _join_object_range_state = top_half ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2710 Editing::MouseMode
2711 Editor::effective_mouse_mode () const
2713 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2714 return MouseObject;
2715 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2716 return MouseRange;
2719 return mouse_mode;
2722 void
2723 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2725 ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2726 assert (e);
2728 e->region_view().delete_note (e->note ());