1 /*
2  * Copyright (C) 2005-2006 Taybin Rutkin <taybin@taybin.com>
3  * Copyright (C) 2005-2018 Paul Davis <paul@linuxaudiosystems.com>
4  * Copyright (C) 2006-2015 David Robillard <d@drobilla.net>
5  * Copyright (C) 2006-2016 Tim Mayberry <mojofunk@gmail.com>
6  * Copyright (C) 2006 Sampo Savolainen <v2@iki.fi>
7  * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
8  * Copyright (C) 2007 Doug McLain <doug@nostar.net>
9  * Copyright (C) 2013-2014 Colin Fletcher <colin.m.fletcher@googlemail.com>
10  * Copyright (C) 2013-2019 Robin Gareus <robin@gareus.org>
11  * Copyright (C) 2014-2017 Nick Mainsbridge <mainsbridge@gmail.com>
12  * Copyright (C) 2014-2018 Ben Loftis <ben@harrisonconsoles.com>
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License along
25  * with this program; if not, write to the Free Software Foundation, Inc.,
26  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27  */
28 
29 #include <cassert>
30 #include <cstdlib>
31 #include <stdint.h>
32 #include <cmath>
33 #include <set>
34 #include <string>
35 #include <algorithm>
36 #include <bitset>
37 
38 #include "pbd/error.h"
39 #include "pbd/enumwriter.h"
40 #include "pbd/memento_command.h"
41 #include "pbd/basename.h"
42 #include "pbd/stateful_diff_command.h"
43 
44 #include "gtkmm2ext/bindings.h"
45 #include "gtkmm2ext/utils.h"
46 
47 #include "canvas/canvas.h"
48 
49 #include "ardour/audioplaylist.h"
50 #include "ardour/audioregion.h"
51 #include "ardour/operations.h"
52 #include "ardour/playlist.h"
53 #include "ardour/profile.h"
54 #include "ardour/region_factory.h"
55 #include "ardour/route.h"
56 #include "ardour/session.h"
57 #include "ardour/types.h"
58 
59 #include "widgets/prompter.h"
60 
61 #include "actions.h"
62 #include "ardour_ui.h"
63 #include "editor.h"
64 #include "time_axis_view.h"
65 #include "audio_time_axis.h"
66 #include "audio_region_view.h"
67 #include "midi_region_view.h"
68 #include "marker.h"
69 #include "streamview.h"
70 #include "region_gain_line.h"
71 #include "rc_option_editor.h"
72 #include "automation_time_axis.h"
73 #include "control_point.h"
74 #include "selection.h"
75 #include "keyboard.h"
76 #include "editing.h"
77 #include "rgb_macros.h"
78 #include "control_point_dialog.h"
79 #include "editor_drag.h"
80 #include "automation_region_view.h"
81 #include "edit_note_dialog.h"
82 #include "mouse_cursors.h"
83 #include "editor_cursors.h"
84 #include "region_peak_cursor.h"
85 #include "verbose_cursor.h"
86 #include "note.h"
87 
88 #include "pbd/i18n.h"
89 
90 using namespace std;
91 using namespace ARDOUR;
92 using namespace PBD;
93 using namespace Gtk;
94 using namespace Editing;
95 using Gtkmm2ext::Keyboard;
96 
97 bool
mouse_sample(samplepos_t & where,bool & in_track_canvas) const98 Editor::mouse_sample (samplepos_t& where, bool& in_track_canvas) const
99 {
100 	/* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
101 	 * pays attentions to subwindows. this means that menu windows are ignored, and
102 	 * if the pointer is in a menu, the return window from the call will be the
103 	 * the regular subwindow *under* the menu.
104 	 *
105 	 * this matters quite a lot if the pointer is moving around in a menu that overlaps
106 	 * the track canvas because we will believe that we are within the track canvas
107 	 * when we are not. therefore, we track enter/leave events for the track canvas
108 	 * and allow that to override the result of gdk_window_get_pointer().
109 	 */
110 
111 	if (!within_track_canvas) {
112 		return false;
113 	}
114 
115 	int x, y;
116 	Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
117 
118 	if (!canvas_window) {
119 		return false;
120 	}
121 
122 	Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
123 
124 	if (!pointer_window) {
125 		return false;
126 	}
127 
128 	if (pointer_window != canvas_window) {
129 		in_track_canvas = false;
130 		return false;
131 	}
132 
133 	in_track_canvas = true;
134 
135 	GdkEvent event;
136 	event.type = GDK_BUTTON_RELEASE;
137 	event.button.x = x;
138 	event.button.y = y;
139 
140 	where = window_event_sample (&event, 0, 0);
141 
142 	return true;
143 }
144 
145 samplepos_t
window_event_sample(GdkEvent const * event,double * pcx,double * pcy) const146 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
147 {
148 	ArdourCanvas::Duple d;
149 
150 	if (!gdk_event_get_coords (event, &d.x, &d.y)) {
151 		return 0;
152 	}
153 
154 	/* event coordinates are in window units, so convert to canvas
155 	 */
156 
157 	d = _track_canvas->window_to_canvas (d);
158 
159 	if (pcx) {
160 		*pcx = d.x;
161 	}
162 
163 	if (pcy) {
164 		*pcy = d.y;
165 	}
166 
167 	return pixel_to_sample (d.x);
168 }
169 
170 samplepos_t
canvas_event_sample(GdkEvent const * event,double * pcx,double * pcy) const171 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
172 {
173 	double x;
174 	double y;
175 
176 	/* event coordinates are already in canvas units */
177 
178 	if (!gdk_event_get_coords (event, &x, &y)) {
179 		cerr << "!NO c COORDS for event type " << event->type << endl;
180 		return 0;
181 	}
182 
183 	if (pcx) {
184 		*pcx = x;
185 	}
186 
187 	if (pcy) {
188 		*pcy = y;
189 	}
190 
191 	/* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel
192 	   position is negative (as can be the case with motion events in particular),
193 	   the sample location is always positive.
194 	*/
195 
196 	return pixel_to_sample_from_event (x);
197 }
198 
199 void
set_current_trimmable(boost::shared_ptr<Trimmable> t)200 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
201 {
202 	boost::shared_ptr<Trimmable> st = _trimmable.lock();
203 
204 	if (!st || st == t) {
205 		_trimmable = t;
206 	}
207 }
208 
209 void
set_current_movable(boost::shared_ptr<Movable> m)210 Editor::set_current_movable (boost::shared_ptr<Movable> m)
211 {
212 	boost::shared_ptr<Movable> sm = _movable.lock();
213 
214 	if (!sm || sm != m) {
215 		_movable = m;
216 	}
217 }
218 
219 void
mouse_mode_object_range_toggled()220 Editor::mouse_mode_object_range_toggled()
221 {
222 	set_mouse_mode (mouse_mode, true); /* updates set-mouse-mode-range */
223 }
224 
225 bool
snap_mode_button_clicked(GdkEventButton * ev)226 Editor::snap_mode_button_clicked (GdkEventButton* ev)
227 {
228 	if (ev->button != 3) {
229 		cycle_snap_mode();
230 		return true;
231 	}
232 
233 	RCOptionEditor* rc_option_editor = ARDOUR_UI::instance()->get_rc_option_editor();
234 	if (rc_option_editor) {
235 		ARDOUR_UI::instance()->show_tabbable (rc_option_editor);
236 		rc_option_editor->set_current_page (_("Editor/Snap"));
237 	}
238 
239 	return true;
240 }
241 
242 
243 
244 Glib::RefPtr<Action>
get_mouse_mode_action(MouseMode m) const245 Editor::get_mouse_mode_action(MouseMode m) const
246 {
247 	switch (m) {
248 	case MouseRange:
249 		return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
250 	case MouseObject:
251 		return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
252 	case MouseCut:
253 		return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
254 	case MouseDraw:
255 		return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
256 	case MouseTimeFX:
257 		return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
258 	case MouseContent:
259 		return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-content"));
260 	case MouseAudition:
261 		return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
262 	}
263 	return Glib::RefPtr<Action>();
264 }
265 
266 void
set_mouse_mode(MouseMode m,bool force)267 Editor::set_mouse_mode (MouseMode m, bool force)
268 {
269 	if (_drags->active ()) {
270 		return;
271 	}
272 
273 	if (!force && m == mouse_mode) {
274 		return;
275 	}
276 
277 	if (ARDOUR::Profile->get_mixbus()) {
278 		if (m == MouseAudition) {
279 			m = MouseRange;
280 		}
281 	}
282 
283 	Glib::RefPtr<Action>       act  = get_mouse_mode_action(m);
284 	Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
285 
286 	/* go there and back to ensure that the toggled handler is called to set up mouse_mode */
287 	tact->set_active (false);
288 	tact->set_active (true);
289 
290 	/* NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting */
291 }
292 
293 void
mouse_mode_toggled(MouseMode m)294 Editor::mouse_mode_toggled (MouseMode m)
295 {
296 	if (ARDOUR::Profile->get_mixbus()) {
297 		if (m == MouseAudition)  {
298 			m = MouseRange;
299 		}
300 	}
301 
302 	Glib::RefPtr<Action>       act  = get_mouse_mode_action(m);
303 	Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
304 
305 	if (!tact->get_active()) {
306 		/* this was just the notification that the old mode has been
307 		 * left. we'll get called again with the new mode active in a
308 		 * jiffy.
309 		 */
310 		return;
311 	}
312 
313 	if (_session && mouse_mode == MouseAudition) {
314 		/* stop transport and reset default speed to avoid oddness with
315 		   auditioning */
316 		_session->request_stop ();
317 		_session->reset_transport_speed ();
318 	}
319 
320 	const bool was_internal = internal_editing();
321 
322 	mouse_mode = m;
323 
324 	/* Switch snap type/mode if we're moving to/from an internal tool.  Note
325 	   this must toggle the actions and not call set_snap_*() directly,
326 	   otherwise things get out of sync and the combo box stops working. */
327 	if (!UIConfiguration::instance().get_grid_follows_internal()) {
328 		grid_type_action(pre_internal_grid_type)->set_active(true);
329 		snap_mode_action(pre_internal_snap_mode)->set_active(true);
330 	} else if (!was_internal && internal_editing()) {
331 		grid_type_action(internal_grid_type)->set_active(true);
332 		snap_mode_action(internal_snap_mode)->set_active(true);
333 	} else if (was_internal && !internal_editing()) {
334 		grid_type_action(pre_internal_grid_type)->set_active(true);
335 		snap_mode_action(pre_internal_snap_mode)->set_active(true);
336 	}
337 
338 	instant_save ();
339 
340 	/* this should generate a new enter event which will
341 	   trigger the appropiate cursor.
342 	*/
343 
344 	if (_track_canvas) {
345 		_track_canvas->re_enter ();
346 	}
347 
348 	set_gain_envelope_visibility ();
349 
350 	update_time_selection_display ();
351 
352 	if (internal_editing()) {
353 
354 		/* reinstate any existing MIDI note (and by extension, MIDI
355 		 * region) selection for internal edit mode. This allows a user
356 		 * to enter/exit/enter this mode without losing a selection of
357 		 * notes.
358 		 */
359 
360 		catch_up_on_midi_selection ();
361 
362 		/* ensure that the track canvas has focus, so that key events
363 		   will get directed to the correct place.
364 		*/
365 		_track_canvas->grab_focus ();
366 
367 		/* enable MIDI editing actions, which in turns enables their
368 		   bindings
369 		*/
370 		ActionManager::set_sensitive (_midi_actions, true);
371 
372 		/* mark "magic widget focus" so that we handle key events
373 		 * correctly
374 		 */
375 		Keyboard::magic_widget_grab_focus ();
376 	} else {
377 		/* undo some of the above actions, since we're not in internal
378 		   edit mode.
379 		*/
380 		ActionManager::set_sensitive (_midi_actions, false);
381 		Keyboard::magic_widget_drop_focus ();
382 	}
383 
384 	if (was_internal && !internal_editing()) {
385 		/* drop any selected regions so that they in turn
386 		 * redraw any selected notes. This essentially the
387 		 * opposite of ::catch_up_on_midi_selection() called
388 		 * above.
389 		 */
390 		get_selection().clear_regions ();
391 	}
392 
393 	update_all_enter_cursors ();
394 
395 	MouseModeChanged (); /* EMIT SIGNAL */
396 }
397 
398 bool
internal_editing() const399 Editor::internal_editing() const
400 {
401 	return mouse_mode == Editing::MouseContent || mouse_mode == Editing::MouseDraw;
402 }
403 
404 void
update_time_selection_display()405 Editor::update_time_selection_display ()
406 {
407 	switch (mouse_mode) {
408 	case MouseRange:
409 		selection->clear_objects ();
410 		selection->clear_midi_notes ();
411 		break;
412 	case MouseObject:
413 		selection->clear_time ();
414 		selection->clear_midi_notes ();
415 		break;
416 	case MouseDraw:
417 		/* Clear regions, but not time or tracks, since that
418 		 would destroy the range selection rectangle, which we need to stick
419 		 around for AutomationRangeDrag. */
420 		selection->clear_regions ();
421 		selection->clear_playlists ();
422 		break;
423 	case MouseContent:
424 		/* This handles internal edit.
425 		   Clear everything except points and notes.
426 		*/
427 		selection->clear_regions();
428 		selection->clear_lines();
429 		selection->clear_playlists ();
430 
431 		selection->clear_time ();
432 		selection->clear_tracks ();
433 		break;
434 
435 	case MouseTimeFX:
436 		/* We probably want to keep region selection */
437 		selection->clear_points ();
438 		selection->clear_lines();
439 		selection->clear_playlists ();
440 
441 		selection->clear_time ();
442 		selection->clear_tracks ();
443 		break;
444 
445 	case MouseAudition:
446 		/*Don't lose lines or points if no action in this mode */
447 		selection->clear_regions ();
448 		selection->clear_playlists ();
449 		selection->clear_time ();
450 		selection->clear_tracks ();
451 		break;
452 
453 	default:
454 		/*Clear everything */
455 		selection->clear_objects();
456 		selection->clear_time ();
457 		selection->clear_tracks ();
458 		break;
459 	}
460 }
461 
462 void
step_mouse_mode(bool next)463 Editor::step_mouse_mode (bool next)
464 {
465 	const int n_mouse_modes = (int)MouseContent + 1;
466 	int       current       = (int)current_mouse_mode();
467 	if (next) {
468 		set_mouse_mode((MouseMode)((current + 1) % n_mouse_modes));
469 	} else {
470 		set_mouse_mode((MouseMode)((current + n_mouse_modes - 1) % n_mouse_modes));
471 	}
472 }
473 
474 void
button_selection(ArdourCanvas::Item * item,GdkEvent * event,ItemType item_type)475 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
476 {
477 	/* in object/audition/timefx/gain-automation mode,
478 	 * any button press sets the selection if the object
479 	 * can be selected. this is a bit of hack, because
480 	 * we want to avoid this if the mouse operation is a
481 	 * region alignment.
482 	 *
483 	 * note: not dbl-click or triple-click
484 	 *
485 	 * Also note that there is no region selection in internal edit mode, otherwise
486 	 * for operations operating on the selection (e.g. cut) it is not obvious whether
487 	 * to cut notes or regions.
488 	 */
489 
490 	MouseMode eff_mouse_mode = effective_mouse_mode ();
491 
492 	if (eff_mouse_mode == MouseCut) {
493 		/* never change selection in cut mode */
494 		return;
495 	}
496 
497 	if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
498 		/* context clicks are always about object properties, even if
499 		 * we're in range mode within smart mode.
500 		 */
501 		eff_mouse_mode = MouseObject;
502 	}
503 
504 	/* special case: allow drag of region fade in/out in object mode with join object/range enabled */
505 	if (get_smart_mode()) {
506 		switch (item_type) {
507 			case FadeInHandleItem:
508 			case FadeInTrimHandleItem:
509 			case FadeOutHandleItem:
510 			case FadeOutTrimHandleItem:
511 				eff_mouse_mode = MouseObject;
512 				break;
513 			default:
514 				break;
515 		}
516 	}
517 
518 	if (((mouse_mode != MouseObject) &&
519 	     (mouse_mode != MouseAudition || item_type != RegionItem) &&
520 	     (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
521 	     (mouse_mode != MouseDraw) &&
522 	     (mouse_mode != MouseContent || item_type == RegionItem)) ||
523 	    ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
524 		return;
525 	}
526 
527 	if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
528 
529 		if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
530 
531 			/* almost no selection action on modified button-2 or button-3 events */
532 
533 			if ((item_type != RegionItem && event->button.button != 2)
534 			    /* for selection of control points prior to delete (shift-right click) */
535 			    && !(item_type == ControlPointItem && event->button.button == 3 && event->type == GDK_BUTTON_PRESS)) {
536 				return;
537 			}
538 		}
539 	}
540 
541 	Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
542 	bool press = (event->type == GDK_BUTTON_PRESS);
543 
544 	if (press) {
545 		_mouse_changed_selection = false;
546 	}
547 
548 	switch (item_type) {
549 		case RegionItem:
550 			if (eff_mouse_mode == MouseDraw) {
551 				break;
552 			}
553 			if (press) {
554 				if (eff_mouse_mode != MouseRange) {
555 					_mouse_changed_selection = set_selected_regionview_from_click (press, op);
556 				} else {
557 					/* don't change the selection unless the
558 					 * clicked track is not currently selected. if
559 					 * so, "collapse" the selection to just this track
560 					 */
561 					if (!selection->selected (clicked_axisview)) {
562 						set_selected_track_as_side_effect (Selection::Set);
563 					}
564 				}
565 			} else {
566 				if (eff_mouse_mode != MouseRange) {
567 					_mouse_changed_selection |= set_selected_regionview_from_click (press, op);
568 				}
569 			}
570 			break;
571 
572 		case RegionViewNameHighlight:
573 		case RegionViewName:
574 		case LeftFrameHandle:
575 		case RightFrameHandle:
576 		case FadeInHandleItem:
577 		case FadeInTrimHandleItem:
578 		case FadeInItem:
579 		case FadeOutHandleItem:
580 		case FadeOutTrimHandleItem:
581 		case FadeOutItem:
582 		case StartCrossFadeItem:
583 		case EndCrossFadeItem:
584 			if (get_smart_mode() || eff_mouse_mode != MouseRange) {
585 				_mouse_changed_selection |= set_selected_regionview_from_click (press, op);
586 			} else if (event->type == GDK_BUTTON_PRESS) {
587 				set_selected_track_as_side_effect (op);
588 			}
589 			break;
590 
591 		case ControlPointItem:
592 			/* for object/track exclusivity, we don't call set_selected_track_as_side_effect (op); */
593 
594 			if (eff_mouse_mode != MouseRange) {
595 				if (event->button.button != 3) {
596 					_mouse_changed_selection |= set_selected_control_point_from_click (press, op);
597 				} else {
598 					_mouse_changed_selection |= set_selected_control_point_from_click (press, Selection::Set);
599 				}
600 			}
601 			break;
602 
603 		case GainLineItem:
604 			if (eff_mouse_mode != MouseRange) {
605 				AutomationLine* argl = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
606 
607 				std::list<Selectable*> selectables;
608 				uint32_t before, after;
609 				samplecnt_t const  where = (samplecnt_t) floor (event->button.x * samples_per_pixel) - clicked_regionview->region ()->position ();
610 
611 				if (!argl || !argl->control_points_adjacent (where, before, after)) {
612 					break;
613 				}
614 
615 				selectables.push_back (argl->nth (before));
616 				selectables.push_back (argl->nth (after));
617 
618 				switch (op) {
619 					case Selection::Set:
620 						if (press) {
621 							selection->set (selectables);
622 							_mouse_changed_selection = true;
623 						}
624 						break;
625 					case Selection::Add:
626 						if (press) {
627 							selection->add (selectables);
628 							_mouse_changed_selection = true;
629 						}
630 						break;
631 					case Selection::Toggle:
632 						if (press) {
633 							selection->toggle (selectables);
634 							_mouse_changed_selection = true;
635 						}
636 						break;
637 
638 					case Selection::Extend:
639 						/* XXX */
640 						break;
641 				}
642 			}
643 			break;
644 
645 		case AutomationLineItem:
646 			if (eff_mouse_mode != MouseRange) {
647 				AutomationLine* al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
648 				std::list<Selectable*> selectables;
649 				double mx = event->button.x;
650 				double my = event->button.y;
651 
652 				al->grab_item().canvas_to_item (mx, my);
653 
654 				uint32_t before, after;
655 				samplecnt_t const  where = (samplecnt_t) floor (mx * samples_per_pixel);
656 
657 				if (!al || !al->control_points_adjacent (where, before, after)) {
658 					break;
659 				}
660 
661 				selectables.push_back (al->nth (before));
662 				selectables.push_back (al->nth (after));
663 
664 				switch (op) {
665 					case Selection::Set:
666 						if (press) {
667 							selection->set (selectables);
668 							_mouse_changed_selection = true;
669 						}
670 						break;
671 					case Selection::Add:
672 						if (press) {
673 							selection->add (selectables);
674 							_mouse_changed_selection = true;
675 						}
676 						break;
677 					case Selection::Toggle:
678 						if (press) {
679 							selection->toggle (selectables);
680 							_mouse_changed_selection = true;
681 						}
682 						break;
683 
684 					case Selection::Extend:
685 						/* XXX */
686 						break;
687 				}
688 			}
689 			break;
690 
691 		case StreamItem:
692 			/* for context click, select track */
693 			if (event->button.button == 3) {
694 				selection->clear_tracks ();
695 				set_selected_track_as_side_effect (op);
696 
697 				/* We won't get a release.*/
698 				begin_reversible_selection_op (X_("Button 3 Menu Select"));
699 				commit_reversible_selection_op ();
700 			}
701 			break;
702 
703 		case AutomationTrackItem:
704 			if (eff_mouse_mode != MouseDraw && op == Selection::Set) {
705 				set_selected_track_as_side_effect (op);
706 			}
707 			break;
708 
709 		case NoteItem:
710 			if (press && event->button.button == 3) {
711 				NoteBase* cnote = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
712 				assert (cnote);
713 				if (cnote->region_view().selection_size() == 0 || !cnote->selected()) {
714 					selection->clear_points();
715 					cnote->region_view().unique_select (cnote);
716 					/* we won't get the release, so store the selection change now */
717 					begin_reversible_selection_op (X_("Button 3 Note Selection"));
718 					commit_reversible_selection_op ();
719 				}
720 			}
721 			break;
722 
723 		default:
724 			break;
725 	}
726 
727 	if ((!press) && _mouse_changed_selection) {
728 		begin_reversible_selection_op (X_("Button Selection"));
729 		commit_reversible_selection_op ();
730 		_mouse_changed_selection = false;
731 	}
732 }
733 
734 bool
button_press_handler_1(ArdourCanvas::Item * item,GdkEvent * event,ItemType item_type)735 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
736 {
737 	/* single mouse clicks on any of these item types operate
738 	 * independent of mouse mode, mostly because they are
739 	 * not on the main track canvas or because we want
740 	 * them to be modeless.
741 	 */
742 
743 	NoteBase* note = NULL;
744 
745 	switch (item_type) {
746 	case PlayheadCursorItem:
747 		_drags->set (new CursorDrag (this, *_playhead_cursor, true), event);
748 		return true;
749 
750 	case MarkerItem:
751 		if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
752 			hide_marker (item, event);
753 		} else {
754 			ArdourMarker* marker = static_cast<ArdourMarker*> (item->get_data ("marker"));
755 			if (marker->type() == ArdourMarker::RegionCue) {
756 				_drags->set (new RegionMarkerDrag (this, marker->region_view(), item), event);
757 			} else {
758 				_drags->set (new MarkerDrag (this, item), event);
759 			}
760 		}
761 		return true;
762 
763 	case TempoMarkerItem:
764 	{
765 		if (ArdourKeyboard::indicates_constraint (event->button.state)) {
766 			_drags->set (
767 				new TempoEndDrag (
768 					this,
769 					item
770 					),
771 				event
772 				);
773 		} else {
774 			_drags->set (
775 				new TempoMarkerDrag (
776 					this,
777 					item,
778 					ArdourKeyboard::indicates_copy (event->button.state)
779 					),
780 				event
781 				);
782 		}
783 
784 		return true;
785 	}
786 
787 	case MeterMarkerItem:
788 	{
789 		_drags->set (
790 			new MeterMarkerDrag (
791 				this,
792 				item,
793 				ArdourKeyboard::indicates_copy (event->button.state)
794 				),
795 			event
796 			);
797 		return true;
798 	}
799 
800 	case VideoBarItem:
801 		_drags->set (new VideoTimeLineDrag (this, item), event);
802 		return true;
803 		break;
804 
805 	case MarkerBarItem:
806 	case TempoBarItem:
807 	case TempoCurveItem:
808 	case MeterBarItem:
809 	case TimecodeRulerItem:
810 	case SamplesRulerItem:
811 	case MinsecRulerItem:
812 	case BBTRulerItem:
813 		if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)
814 		    && !ArdourKeyboard::indicates_constraint (event->button.state)) {
815 			_drags->set (new CursorDrag (this, *_playhead_cursor, false), event);
816 		} else if (ArdourKeyboard::indicates_constraint (event->button.state)
817 		           && Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
818 			_drags->set (new TempoTwistDrag (this, item), event);
819 		} else if (ArdourKeyboard::indicates_constraint (event->button.state)) {
820 			_drags->set (new BBTRulerDrag (this, item), event);
821 		}
822 		return true;
823 		break;
824 
825 
826 	case RangeMarkerBarItem:
827 		if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
828 			_drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
829 		} else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
830 			_drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
831 		} else {
832 			_drags->set (new CursorDrag (this, *_playhead_cursor, false), event);
833 		}
834 		return true;
835 		break;
836 
837 	case CdMarkerBarItem:
838 		if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
839 			_drags->set (new CursorDrag (this, *_playhead_cursor, false), event);
840 		} else {
841 			_drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
842 		}
843 		return true;
844 		break;
845 
846 	case TransportMarkerBarItem:
847 		if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
848 			_drags->set (new CursorDrag (this, *_playhead_cursor, false), event);
849 		} else {
850 			_drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
851 		}
852 		return true;
853 		break;
854 
855 	default:
856 		break;
857 	}
858 
859 	if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
860 		/* special case: allow trim of range selections in joined object mode;
861 		 * in theory eff should equal MouseRange in this case, but it doesn't
862 		 * because entering the range selection canvas item results in entered_regionview
863 		 * being set to 0, so update_join_object_range_location acts as if we aren't
864 		 * over a region.
865 		 */
866 		if (item_type == StartSelectionTrimItem) {
867 			_drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
868 		} else if (item_type == EndSelectionTrimItem) {
869 			_drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
870 		}
871 	}
872 
873 	Editing::MouseMode eff = effective_mouse_mode ();
874 
875 	/* special case: allow drag of region fade in/out in object mode with join object/range enabled */
876 	if (get_smart_mode()) {
877 		switch (item_type) {
878 		  case FadeInHandleItem:
879 		  case FadeInTrimHandleItem:
880 		  case FadeOutHandleItem:
881 		  case FadeOutTrimHandleItem:
882 			eff = MouseObject;
883 			break;
884 		default:
885 			break;
886 		}
887 	}
888 
889 	switch (eff) {
890 	case MouseRange:
891 		switch (item_type) {
892 		case StartSelectionTrimItem:
893 			_drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
894 			break;
895 
896 		case EndSelectionTrimItem:
897 			_drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
898 			break;
899 
900 		case SelectionItem:
901 			if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
902 				start_selection_grab (item, event);
903 				return true;
904 			} else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
905 				/* grab selection for moving */
906 				_drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
907 			} else {
908 				/* this was debated, but decided the more common action was to make a new selection */
909 				_drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
910 			}
911 			break;
912 
913 		case StreamItem:
914 			if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
915 				_drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
916 			} else {
917 				_drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
918 			}
919 			return true;
920 			break;
921 
922 		case RegionViewNameHighlight:
923 			if (!clicked_regionview->region()->locked()) {
924 				_drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
925 				return true;
926 			}
927 			break;
928 
929 		default:
930 			if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
931 				_drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
932 			} else {
933 				_drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
934 			}
935 		}
936 		return true;
937 		break;
938 
939 	case MouseCut:
940 		switch (item_type) {
941 		case RegionItem:
942 		case FadeInHandleItem:
943 		case FadeOutHandleItem:
944 		case LeftFrameHandle:
945 		case RightFrameHandle:
946 		case FeatureLineItem:
947 		case RegionViewNameHighlight:
948 		case RegionViewName:
949 		case StreamItem:
950 		case AutomationTrackItem:
951 			_drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, get_canvas_cursor());
952 			return true;
953 			break;
954 		default:
955 			break;
956 		}
957 		break;
958 
959 	case MouseContent:
960 		switch (item_type) {
961 		case NoteItem:
962 			/* Existing note: allow trimming/motion */
963 			if ((note = reinterpret_cast<NoteBase*> (item->get_data ("notebase")))) {
964 				if (note->big_enough_to_trim() && note->mouse_near_ends()) {
965 					_drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
966 				} else {
967 					_drags->set (new NoteDrag (this, item), event);
968 				}
969 			}
970 			return true;
971 
972 		case GainLineItem:
973 			_drags->set (new LineDrag (this, item), event);
974 			return true;
975 			break;
976 
977 		case ControlPointItem:
978 			_drags->set (new ControlPointDrag (this, item), event);
979 			return true;
980 			break;
981 
982 		case AutomationLineItem:
983 			_drags->set (new LineDrag (this, item), event);
984 			return true;
985 			break;
986 
987 		case StreamItem:
988 			/* in the past, we created a new midi region here, but perhaps that is best left to the Draw mode */
989 			break;
990 
991 		case AutomationTrackItem:
992 			/* rubberband drag to select automation points */
993 			_drags->set (new EditorRubberbandSelectDrag (this, item), event);
994 			return true;
995 			break;
996 
997 		case RegionItem:
998 			if (dynamic_cast<AutomationRegionView*>(clicked_regionview)) {
999 				/* rubberband drag to select automation points */
1000 				_drags->set (new EditorRubberbandSelectDrag (this, item), event);
1001 				return true;
1002 			}
1003 			break;
1004 
1005 		default:
1006 			break;
1007 		}
1008 		break;
1009 
1010 	case MouseObject:
1011 		if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
1012 		    event->type == GDK_BUTTON_PRESS) {
1013 
1014 			_drags->set (new EditorRubberbandSelectDrag (this, item), event);
1015 
1016 		} else if (event->type == GDK_BUTTON_PRESS) {
1017 
1018 			switch (item_type) {
1019 			case FadeInHandleItem:
1020 			{
1021 				_drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
1022 				return true;
1023 			}
1024 
1025 			case FadeOutHandleItem:
1026 			{
1027 				_drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
1028 				return true;
1029 			}
1030 
1031 			case StartCrossFadeItem:
1032 			case EndCrossFadeItem:
1033 			/* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor.
1034 			 * For not this is not fully implemented */
1035 #if 0
1036 			if (!clicked_regionview->region()->locked()) {
1037 				_drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
1038 				return true;
1039 			}
1040 #endif
1041 				break;
1042 
1043 			case FeatureLineItem:
1044 			{
1045 				if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
1046 					remove_transient(item);
1047 					return true;
1048 				}
1049 
1050 				_drags->set (new FeatureLineDrag (this, item), event);
1051 				return true;
1052 				break;
1053 			}
1054 
1055 			case RegionItem:
1056 				if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1057 					/* click on an automation region view; do nothing here and let the ARV's signal handler
1058 					   sort it out.
1059 					*/
1060 					break;
1061 				}
1062 
1063 				/* click on a normal region view */
1064 				if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::slip_contents_modifier ())) {
1065 					if (!clicked_regionview->region()->locked() && (Config->get_edit_mode() != Lock)) {
1066 						_drags->add (new RegionSlipContentsDrag (this, item, clicked_regionview, selection->regions.by_layer()));
1067 					}
1068 				} else if (ArdourKeyboard::indicates_copy (event->button.state)) {
1069 					add_region_copy_drag (item, event, clicked_regionview);
1070 				} else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
1071 					add_region_brush_drag (item, event, clicked_regionview);
1072 				} else {
1073 					add_region_drag (item, event, clicked_regionview);
1074 				}
1075 
1076 
1077 				_drags->start_grab (event);
1078 				return true;
1079 				break;
1080 
1081 			case RegionViewNameHighlight:
1082 			case LeftFrameHandle:
1083 			case RightFrameHandle:
1084 				if (!clicked_regionview->region()->locked()) {
1085 					_drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1086 					return true;
1087 				}
1088 				break;
1089 
1090 			case FadeInTrimHandleItem:
1091 			case FadeOutTrimHandleItem:
1092 				if (!clicked_regionview->region()->locked()) {
1093 					_drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
1094 					return true;
1095 				}
1096 				break;
1097 
1098 			case RegionViewName:
1099 			{
1100 				/* rename happens on edit clicks */
1101 				if (clicked_regionview->get_name_highlight()) {
1102 					_drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1103 					return true;
1104 				}
1105 				break;
1106 			}
1107 
1108 			case ControlPointItem:
1109 				_drags->set (new ControlPointDrag (this, item), event);
1110 				return true;
1111 				break;
1112 
1113 			case AutomationLineItem:
1114 				_drags->set (new LineDrag (this, item), event);
1115 				return true;
1116 				break;
1117 
1118 			case StreamItem:
1119 				_drags->set (new EditorRubberbandSelectDrag (this, item), event);
1120 				return true;
1121 
1122 			case AutomationTrackItem:
1123 			{
1124 				TimeAxisView* parent = clicked_axisview->get_parent ();
1125 				AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1126 				assert (atv);
1127 				if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1128 
1129 					RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1130 					assert (p);
1131 					boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1132 					if (pl->n_regions() == 0) {
1133 						/* Parent has no regions; create one so that we have somewhere to put automation */
1134 						_drags->set (new RegionCreateDrag (this, item, parent), event);
1135 					} else {
1136 						/* See if there's a region before the click that we can extend, and extend it if so */
1137 						samplepos_t const t = canvas_event_sample (event);
1138 						boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1139 						if (!prev) {
1140 							_drags->set (new RegionCreateDrag (this, item, parent), event);
1141 						} else {
1142 							prev->set_length (t - prev->position (), get_grid_music_divisions (event->button.state));
1143 						}
1144 					}
1145 				} else {
1146 					/* rubberband drag to select automation points */
1147 					_drags->set (new EditorRubberbandSelectDrag (this, item), event);
1148 				}
1149 				break;
1150 			}
1151 
1152 			case SelectionItem:
1153 			{
1154 				break;
1155 			}
1156 
1157 			case MarkerBarItem:
1158 
1159 				break;
1160 
1161 			default:
1162 				break;
1163 			}
1164 		}
1165 		return true;
1166 		break;
1167 
1168 	case MouseDraw:
1169 		switch (item_type) {
1170 		case GainLineItem:
1171 			_drags->set (new LineDrag (this, item), event);
1172 			return true;
1173 
1174 		case ControlPointItem:
1175 			_drags->set (new ControlPointDrag (this, item), event);
1176 			return true;
1177 			break;
1178 
1179 		case SelectionItem:
1180 			{
1181 				if (selection->time.empty ()) {
1182 					/* nothing to do */
1183 					return true;
1184 				}
1185 				pair<TimeAxisView*, int> tvp = trackview_by_y_position (event->button.y, false);
1186 				if (!tvp.first) {
1187 					/* clicked outside of a track */
1188 					return true;
1189 				}
1190 				/* handle automation lanes first */
1191 				AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1192 				if (atv) {
1193 					/* smart "join" mode: drag automation */
1194 					_drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1195 					return true;
1196 				}
1197 				if (dynamic_cast<AutomationRegionView*>(clicked_regionview)) {
1198 					/* MIDI CC or similar -- TODO handle multiple? */
1199 					list<RegionView*> rvl;
1200 					rvl.push_back (clicked_regionview);
1201 					_drags->set (new AutomationRangeDrag (this, rvl, selection->time,
1202 								clicked_regionview->get_time_axis_view().y_position(),
1203 								clicked_regionview->get_time_axis_view().current_height()),
1204 							event, _cursors->up_down);
1205 					return true;
1206 				}
1207 
1208 				/* shift+drag: only apply to clicked_regionview (if any) */
1209 				if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
1210 					if (dynamic_cast<AudioRegionView*>(clicked_regionview) == 0) {
1211 						return true;
1212 					}
1213 					list<RegionView*> rvl;
1214 					rvl.push_back (clicked_regionview);
1215 					// TODO: handle layer_display() == Stacked
1216 					_drags->set (new AutomationRangeDrag (this, rvl, selection->time,
1217 								clicked_regionview->get_time_axis_view().y_position(),
1218 								clicked_regionview->get_time_axis_view().current_height()),
1219 							event, _cursors->up_down);
1220 					return true;
1221 				}
1222 
1223 				/* collect all audio regions-views in the given range selection */
1224 				list<RegionView*> rvl;
1225 				TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
1226 				for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
1227 					RouteTimeAxisView* tatv;
1228 					boost::shared_ptr<Playlist> playlist;
1229 					if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) == 0) {
1230 						continue;
1231 					}
1232 					if ((playlist = (*i)->playlist()) == 0) {
1233 						continue;
1234 					}
1235 					if (boost::dynamic_pointer_cast<AudioPlaylist> (playlist) == 0) {
1236 						continue;
1237 					}
1238 					for (list<AudioRange>::const_iterator j = selection->time.begin(); j != selection->time.end(); ++j) {
1239 						boost::shared_ptr<RegionList> rl = playlist->regions_touched (j->start, j->end);
1240 						for (RegionList::iterator ir = rl->begin(); ir != rl->end(); ++ir) {
1241 							RegionView* rv;
1242 							if ((rv = tatv->view()->find_view (*ir)) != 0) {
1243 								rvl.push_back (rv);
1244 							}
1245 						}
1246 					}
1247 				}
1248 				/* region-gain drag */
1249 				if (!rvl.empty ()) {
1250 					double y_pos = tvp.first->y_position();
1251 					double height = tvp.first->current_height();
1252 					StreamView* cv = tvp.first->view ();
1253 					if (cv->layer_display() == Stacked && cv->layers() > 1) {
1254 						height /= cv->layers();
1255 						double yy = event->button.y - _trackview_group->canvas_origin().y;
1256 						y_pos += floor ((yy - y_pos) / height) * height;
1257 					}
1258 					_drags->set (new AutomationRangeDrag (this, rvl, selection->time, y_pos, height),
1259 							event, _cursors->up_down);
1260 				}
1261 				return true;
1262 				break;
1263 			}
1264 
1265 		case AutomationLineItem:
1266 			_drags->set (new LineDrag (this, item), event);
1267 			break;
1268 
1269 		case NoteItem:
1270 			if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
1271 				if (note->big_enough_to_trim() && note->mouse_near_ends()) {
1272 					/* Note is big and pointer is near the end, trim */
1273 					_drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
1274 				} else {
1275 					/* Drag note */
1276 					_drags->set (new NoteDrag (this, item), event);
1277 				}
1278 				return true;
1279 			}
1280 			return true;
1281 
1282 		case StreamItem:
1283 			if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1284 				_drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1285 			}
1286 			return true;
1287 
1288 		default:
1289 			break;
1290 		}
1291 		return true;
1292 		break;
1293 
1294 	case MouseTimeFX:
1295 		if (item_type == NoteItem) {
1296 			/* resize-drag notes */
1297 			if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
1298 				if (note->big_enough_to_trim()) {
1299 					_drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
1300 				}
1301 			}
1302 			return true;
1303 		} else if (clicked_regionview) {
1304 			/* do time-FX  */
1305 			_drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1306 			return true;
1307 		}
1308 		break;
1309 
1310 	case MouseAudition:
1311 		_drags->set (new ScrubDrag (this, item), event, _cursors->transparent);
1312 		scrub_reversals = 0;
1313 		scrub_reverse_distance = 0;
1314 		last_scrub_x = event->button.x;
1315 		scrubbing_direction = 0;
1316 		return true;
1317 		break;
1318 
1319 	default:
1320 		break;
1321 	}
1322 
1323 	return false;
1324 }
1325 
1326 bool
button_press_handler_2(ArdourCanvas::Item * item,GdkEvent * event,ItemType item_type)1327 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1328 {
1329 	Editing::MouseMode const eff = effective_mouse_mode ();
1330 	switch (eff) {
1331 	case MouseObject:
1332 		switch (item_type) {
1333 		case RegionItem:
1334 			if (ArdourKeyboard::indicates_copy (event->button.state)) {
1335 				add_region_copy_drag (item, event, clicked_regionview);
1336 			} else {
1337 				add_region_drag (item, event, clicked_regionview);
1338 			}
1339 			_drags->start_grab (event);
1340 			return true;
1341 			break;
1342 		case ControlPointItem:
1343 			_drags->set (new ControlPointDrag (this, item), event);
1344 			return true;
1345 			break;
1346 
1347 		default:
1348 			break;
1349 		}
1350 
1351 		switch (item_type) {
1352 		case RegionViewNameHighlight:
1353 			_drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1354 			return true;
1355 			break;
1356 
1357 		case LeftFrameHandle:
1358 		case RightFrameHandle:
1359 			_drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1360 			return true;
1361 			break;
1362 
1363 		case RegionViewName:
1364 			_drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1365 			return true;
1366 			break;
1367 
1368 		default:
1369 			break;
1370 		}
1371 
1372 		break;
1373 
1374 	case MouseDraw:
1375 		return false;
1376 
1377 	case MouseRange:
1378 		/* relax till release */
1379 		return true;
1380 		break;
1381 
1382 	default:
1383 		break;
1384 	}
1385 
1386 	return false;
1387 }
1388 
1389 bool
button_press_handler(ArdourCanvas::Item * item,GdkEvent * event,ItemType item_type)1390 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1391 {
1392 	if (event->type == GDK_2BUTTON_PRESS) {
1393 		_drags->mark_double_click ();
1394 		gdk_pointer_ungrab (GDK_CURRENT_TIME);
1395 		return true;
1396 	}
1397 
1398 	if (event->type != GDK_BUTTON_PRESS) {
1399 		return false;
1400 	}
1401 
1402 	_track_canvas->grab_focus();
1403 
1404 	if (_session && _session->actively_recording()) {
1405 		return true;
1406 	}
1407 
1408 	button_selection (item, event, item_type);
1409 
1410 	if (!_drags->active () &&
1411 	    (Keyboard::is_delete_event (&event->button) ||
1412 	     Keyboard::is_context_menu_event (&event->button) ||
1413 	     Keyboard::is_edit_event (&event->button))) {
1414 
1415 		/* handled by button release */
1416 		return true;
1417 	}
1418 
1419 	/* not rolling, effectively in range mode, follow edits enabled (likely
1420 	 * to start range drag), not in a fade handle (since that means we are
1421 	 * not starting a range drag): locate the PH here
1422 	 */
1423 
1424 	if ((item_type != FadeInHandleItem) &&
1425 	    (item_type != FadeOutHandleItem) &&
1426 	    !_drags->active () &&
1427 	    _session &&
1428 	    !_session->transport_rolling() &&
1429 	    (effective_mouse_mode() == MouseRange) &&
1430 	    UIConfiguration::instance().get_follow_edits() &&
1431 	    !_session->config.get_external_sync()) {
1432 
1433 		MusicSample where (canvas_event_sample (event), 0);
1434 		snap_to (where);
1435 		_session->request_locate (where.sample, MustStop);
1436 	}
1437 
1438 	switch (event->button.button) {
1439 	case 1:
1440 		return button_press_handler_1 (item, event, item_type);
1441 		break;
1442 
1443 	case 2:
1444 		return button_press_handler_2 (item, event, item_type);
1445 		break;
1446 
1447 	case 3:
1448 		break;
1449 
1450 	default:
1451 		return button_press_dispatch (&event->button);
1452 		break;
1453 
1454 	}
1455 
1456 	return false;
1457 }
1458 
1459 bool
button_press_dispatch(GdkEventButton * ev)1460 Editor::button_press_dispatch (GdkEventButton* ev)
1461 {
1462 	/* this function is intended only for buttons 4 and above. */
1463 
1464 	Gtkmm2ext::MouseButton b (ev->state, ev->button);
1465 	return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1466 }
1467 
1468 bool
button_release_dispatch(GdkEventButton * ev)1469 Editor::button_release_dispatch (GdkEventButton* ev)
1470 {
1471 	/* this function is intended only for buttons 4 and above. */
1472 
1473 	Gtkmm2ext::MouseButton b (ev->state, ev->button);
1474 	return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1475 }
1476 
1477 bool
button_release_handler(ArdourCanvas::Item * item,GdkEvent * event,ItemType item_type)1478 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1479 {
1480 	MusicSample where (canvas_event_sample (event), 0);
1481 	AutomationTimeAxisView* atv = 0;
1482 
1483 	_press_cursor_ctx.reset();
1484 
1485 	/* no action if we're recording */
1486 
1487 	if (_session && _session->actively_recording()) {
1488 		return true;
1489 	}
1490 
1491 	bool were_dragging = false;
1492 
1493 	if (!Keyboard::is_context_menu_event (&event->button)) {
1494 
1495 		/* see if we're finishing a drag */
1496 
1497 		if (_drags->active ()) {
1498 			bool const r = _drags->end_grab (event);
1499 			if (r) {
1500 				/* grab dragged, so do nothing else */
1501 				return true;
1502 			}
1503 
1504 			were_dragging = true;
1505 		}
1506 
1507 		update_region_layering_order_editor ();
1508 	}
1509 
1510 	/* edit events get handled here */
1511 
1512 	if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1513 		switch (item_type) {
1514 		case RegionItem:
1515 			show_region_properties ();
1516 			break;
1517 		case TempoMarkerItem: {
1518 			ArdourMarker* marker;
1519 			TempoMarker* tempo_marker;
1520 
1521 			if ((marker = reinterpret_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1522 				fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1523 				abort(); /*NOTREACHED*/
1524 			}
1525 
1526 			if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1527 				fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1528 				abort(); /*NOTREACHED*/
1529 			}
1530 
1531 			edit_tempo_marker (*tempo_marker);
1532 			break;
1533 		}
1534 
1535 		case MeterMarkerItem: {
1536 			ArdourMarker* marker;
1537 			MeterMarker* meter_marker;
1538 
1539 			if ((marker = reinterpret_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1540 				fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1541 				abort(); /*NOTREACHED*/
1542 			}
1543 
1544 			if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1545 				fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1546 				abort(); /*NOTREACHED*/
1547 			}
1548 			edit_meter_marker (*meter_marker);
1549 			break;
1550 		}
1551 
1552 		case RegionViewName:
1553 			if (clicked_regionview->name_active()) {
1554 				return mouse_rename_region (item, event);
1555 			}
1556 			break;
1557 
1558 		case ControlPointItem:
1559 			edit_control_point (item);
1560 			break;
1561 
1562 		default:
1563 			break;
1564 		}
1565 		return true;
1566 	}
1567 
1568 	/* context menu events get handled here */
1569 	if (Keyboard::is_context_menu_event (&event->button)) {
1570 
1571 		context_click_event = *event;
1572 
1573 		if (!_drags->active ()) {
1574 
1575 			/* no matter which button pops up the context menu, tell the menu
1576 			   widget to use button 1 to drive menu selection.
1577 			*/
1578 
1579 			switch (item_type) {
1580 			case FadeInItem:
1581 			case FadeInHandleItem:
1582 			case FadeInTrimHandleItem:
1583 			case StartCrossFadeItem:
1584 				popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1585 				break;
1586 
1587 			case FadeOutItem:
1588 			case FadeOutHandleItem:
1589 			case FadeOutTrimHandleItem:
1590 			case EndCrossFadeItem:
1591 				popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1592 				break;
1593 
1594 			case LeftFrameHandle:
1595 			case RightFrameHandle:
1596 				break;
1597 
1598 			case StreamItem:
1599 				popup_track_context_menu (1, event->button.time, item_type, false);
1600 				break;
1601 
1602 			case RegionItem:
1603 			case RegionViewNameHighlight:
1604 			case RegionViewName:
1605 				popup_track_context_menu (1, event->button.time, item_type, false);
1606 				break;
1607 
1608 			case SelectionItem:
1609 				popup_track_context_menu (1, event->button.time, item_type, true);
1610 				break;
1611 
1612 			case AutomationTrackItem:
1613 				popup_track_context_menu (1, event->button.time, item_type, false);
1614 				break;
1615 
1616 			case MarkerBarItem:
1617 			case RangeMarkerBarItem:
1618 			case TransportMarkerBarItem:
1619 			case CdMarkerBarItem:
1620 			case TempoBarItem:
1621 			case TempoCurveItem:
1622 			case MeterBarItem:
1623 			case VideoBarItem:
1624 			case TimecodeRulerItem:
1625 			case SamplesRulerItem:
1626 			case MinsecRulerItem:
1627 			case BBTRulerItem:
1628 				popup_ruler_menu (where.sample, item_type);
1629 				break;
1630 
1631 			case MarkerItem:
1632 				marker_context_menu (&event->button, item);
1633 				break;
1634 
1635 			case TempoMarkerItem:
1636 				tempo_or_meter_marker_context_menu (&event->button, item);
1637 				break;
1638 
1639 			case MeterMarkerItem:
1640 				tempo_or_meter_marker_context_menu (&event->button, item);
1641 				break;
1642 
1643 			case CrossfadeViewItem:
1644 				popup_track_context_menu (1, event->button.time, item_type, false);
1645 				break;
1646 
1647 			case ControlPointItem:
1648 				popup_control_point_context_menu (item, event);
1649 				break;
1650 
1651 			case NoteItem:
1652 				if (internal_editing()) {
1653 					popup_note_context_menu (item, event);
1654 				}
1655 				break;
1656 
1657 			default:
1658 				break;
1659 			}
1660 
1661 			return true;
1662 		}
1663 	}
1664 
1665 	/* delete events get handled here */
1666 
1667 	Editing::MouseMode const eff = effective_mouse_mode ();
1668 
1669 	if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1670 
1671 		switch (item_type) {
1672 		case TempoMarkerItem:
1673 			remove_tempo_marker (item);
1674 			break;
1675 
1676 		case MeterMarkerItem:
1677 			remove_meter_marker (item);
1678 			break;
1679 
1680 		case MarkerItem:
1681 			remove_marker (*item);
1682 			break;
1683 
1684 		case RegionItem:
1685 			if (eff == MouseObject) {
1686 				remove_clicked_region ();
1687 			}
1688 			break;
1689 
1690 		case ControlPointItem:
1691 			remove_control_point (item);
1692 			break;
1693 
1694 		case NoteItem:
1695 			remove_midi_note (item, event);
1696 			break;
1697 
1698 		default:
1699 			break;
1700 		}
1701 		return true;
1702 	}
1703 
1704 	switch (event->button.button) {
1705 	case 1:
1706 
1707 		switch (item_type) {
1708 		/* see comments in button_press_handler */
1709 		case PlayheadCursorItem:
1710 		case MarkerItem:
1711 		case GainLineItem:
1712 		case AutomationLineItem:
1713 		case StartSelectionTrimItem:
1714 		case EndSelectionTrimItem:
1715 			return true;
1716 
1717 		case MarkerBarItem:
1718 			if (!_dragging_playhead) {
1719 				snap_to_with_modifier (where, event, RoundNearest, SnapToGrid_Scaled);
1720 				mouse_add_new_marker (where.sample);
1721 			}
1722 			return true;
1723 
1724 		case CdMarkerBarItem:
1725 			if (!_dragging_playhead) {
1726 				/* if we get here then a dragged range wasn't done */
1727 				snap_to_with_modifier (where, event, RoundNearest, SnapToGrid_Scaled);
1728 				mouse_add_new_marker (where.sample, true);
1729 			}
1730 			return true;
1731 		case TempoBarItem:
1732 		case TempoCurveItem:
1733 			if (!_dragging_playhead && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1734 				snap_to_with_modifier (where, event);
1735 				mouse_add_new_tempo_event (where.sample);
1736 			}
1737 			return true;
1738 
1739 		case MeterBarItem:
1740 			if (!_dragging_playhead && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1741 				mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1742 			}
1743 			return true;
1744 			break;
1745 
1746 		case TimecodeRulerItem:
1747 		case SamplesRulerItem:
1748 		case MinsecRulerItem:
1749 		case BBTRulerItem:
1750 			return true;
1751 			break;
1752 
1753 		default:
1754 			break;
1755 		}
1756 
1757 		switch (eff) {
1758 		case MouseDraw:
1759 			switch (item_type) {
1760 			case RegionItem:
1761 			{
1762 				/* check that we didn't drag before releasing, since
1763 				   its really annoying to create new control
1764 				   points when doing this.
1765 				*/
1766 				AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1767 				if (!were_dragging && arv) {
1768 					bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1769 					arv->add_gain_point_event (item, event, with_guard_points);
1770 				}
1771 				return true;
1772 				break;
1773 			}
1774 
1775 			case AutomationTrackItem: {
1776 				bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1777 				atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1778 				if (atv) {
1779 					atv->add_automation_event (event, where.sample, event->button.y, with_guard_points);
1780 				}
1781 				return true;
1782 				break;
1783 			}
1784 			default:
1785 				break;
1786 			}
1787 			break;
1788 
1789 		case MouseAudition:
1790 			if (scrubbing_direction == 0) {
1791 				/* no drag, just a click */
1792 				switch (item_type) {
1793 				case RegionItem:
1794 					play_selected_region ();
1795 					break;
1796 				default:
1797 					break;
1798 				}
1799 			} else if (_session) {
1800 				/* make sure we stop */
1801 				_session->request_stop ();
1802 			}
1803 			break;
1804 
1805 		default:
1806 			break;
1807 
1808 		}
1809 
1810 		/* do any (de)selection operations that should occur on button release */
1811 		button_selection (item, event, item_type);
1812 
1813 		return true;
1814 		break;
1815 
1816 
1817 	case 2:
1818 		switch (eff) {
1819 
1820 		case MouseObject:
1821 			switch (item_type) {
1822 			case RegionItem:
1823 				if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1824 					raise_region ();
1825 				} else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1826 					lower_region ();
1827 				} else {
1828 					/* Button2 click is unused */
1829 				}
1830 				return true;
1831 
1832 				break;
1833 
1834 			default:
1835 				break;
1836 			}
1837 			break;
1838 
1839 		case MouseDraw:
1840 			return true;
1841 
1842 		case MouseRange:
1843 			// x_style_paste (where, 1.0);
1844 			return true;
1845 			break;
1846 
1847 		default:
1848 			break;
1849 		}
1850 
1851 		break;
1852 
1853 	case 3:
1854 		break;
1855 
1856 	default:
1857 		break;
1858 	}
1859 
1860 	return false;
1861 }
1862 
1863 bool
enter_handler(ArdourCanvas::Item * item,GdkEvent * event,ItemType item_type)1864 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1865 {
1866 	ControlPoint* cp;
1867 	ArdourMarker * marker;
1868 	MeterMarker* m_marker = 0;
1869 	TempoMarker* t_marker = 0;
1870 	double fraction;
1871 	bool ret = true;
1872 
1873 	/* by the time we reach here, entered_regionview and entered trackview
1874 	 * will have already been set as appropriate. Things are done this
1875 	 * way because this method isn't passed a pointer to a variable type of
1876 	 * thing that is entered (which may or may not be canvas item).
1877 	 * (e.g. the actual entered regionview)
1878 	 */
1879 
1880 	choose_canvas_cursor_on_entry (item_type);
1881 
1882 	switch (item_type) {
1883 	case ControlPointItem:
1884 		if (mouse_mode == MouseDraw || mouse_mode == MouseObject || mouse_mode == MouseContent) {
1885 			cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1886 			cp->show ();
1887 
1888 			fraction = 1.0 - (cp->get_y() / cp->line().height());
1889 
1890 			_verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1891 			_verbose_cursor->show ();
1892 		}
1893 		break;
1894 
1895 	case GainLineItem:
1896 		if (mouse_mode == MouseDraw) {
1897 			ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1898 			if (line) {
1899 				line->set_outline_color (UIConfiguration::instance().color ("entered gain line"));
1900 			}
1901 		}
1902 		break;
1903 
1904 	case AutomationLineItem:
1905 		if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1906 			ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1907 			if (line) {
1908 				line->set_outline_color (UIConfiguration::instance().color ("entered automation line"));
1909 			}
1910 		}
1911 		break;
1912 
1913 	case AutomationTrackItem:
1914 		AutomationTimeAxisView* atv;
1915 		if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1916 			clear_entered_track = false;
1917 			set_entered_track (atv);
1918 		}
1919 		break;
1920 
1921 	case MarkerItem:
1922 		if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
1923 			break;
1924 		}
1925 		entered_marker = marker;
1926 		marker->set_entered (true);
1927 		break;
1928 
1929 	case MeterMarkerItem:
1930 		if ((m_marker = static_cast<MeterMarker *> (item->get_data ("marker"))) == 0) {
1931 			break;
1932 		}
1933 		entered_marker = m_marker;
1934 		if (m_marker->meter().position_lock_style() == MusicTime) {
1935 			m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker"));
1936 		} else {
1937 			m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker music"));
1938 		}
1939 		break;
1940 
1941 	case TempoMarkerItem:
1942 		if ((t_marker = static_cast<TempoMarker *> (item->get_data ("marker"))) == 0) {
1943 			break;
1944 		}
1945 		entered_marker = t_marker;
1946 		if (t_marker->tempo().position_lock_style() == MusicTime) {
1947 			t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker"));
1948 		} else {
1949 			t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker music"));
1950 		}
1951 		break;
1952 
1953 	case FadeInHandleItem:
1954 	case FadeInTrimHandleItem:
1955 		if (mouse_mode == MouseObject) {
1956 			ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1957 			if (rect) {
1958 				RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1959 				rect->set_fill_color (rv->get_fill_color());
1960 			}
1961 		}
1962 		break;
1963 
1964 	case FadeOutHandleItem:
1965 	case FadeOutTrimHandleItem:
1966 		if (mouse_mode == MouseObject) {
1967 			ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1968 			if (rect) {
1969 				RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1970 				rect->set_fill_color (rv->get_fill_color ());
1971 			}
1972 		}
1973 		break;
1974 
1975 	case FeatureLineItem:
1976 	{
1977 		ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1978 		line->set_outline_color (0xFF0000FF);
1979 	}
1980 	break;
1981 
1982 	case SelectionItem:
1983 		break;
1984 
1985 	case WaveItem:
1986 	{
1987 		if (entered_regionview) {
1988 			entered_regionview->entered();
1989 		}
1990 	}
1991 	break;
1992 
1993 	default:
1994 		break;
1995 	}
1996 
1997 	/* third pass to handle entered track status in a comprehensible way.
1998 	 */
1999 
2000 	switch (item_type) {
2001 	case GainLineItem:
2002 	case AutomationLineItem:
2003 	case ControlPointItem:
2004 		/* these do not affect the current entered track state */
2005 		clear_entered_track = false;
2006 		break;
2007 
2008 	case AutomationTrackItem:
2009 		/* handled above already */
2010 		break;
2011 
2012 	default:
2013 
2014 		break;
2015 	}
2016 
2017 	return ret;
2018 }
2019 
2020 bool
leave_handler(ArdourCanvas::Item * item,GdkEvent *,ItemType item_type)2021 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
2022 {
2023 	AutomationLine* al;
2024 	ArdourMarker *marker;
2025 	TempoMarker *t_marker;
2026 	MeterMarker *m_marker;
2027 	bool ret = true;
2028 
2029 	if (!_enter_stack.empty()) {
2030 		_enter_stack.pop_back();
2031 	}
2032 
2033 	switch (item_type) {
2034 	case ControlPointItem:
2035 		_verbose_cursor->hide ();
2036 		break;
2037 
2038 	case GainLineItem:
2039 	case AutomationLineItem:
2040 		al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2041 		{
2042 			ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2043 			if (line) {
2044 				line->set_outline_color (al->get_line_color());
2045 			}
2046 		}
2047 		break;
2048 
2049 	case MarkerItem:
2050 		if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
2051 			break;
2052 		}
2053 		entered_marker = 0;
2054 		marker->set_entered (false);
2055 		break;
2056 
2057 	case MeterMarkerItem:
2058 		if ((m_marker = static_cast<MeterMarker *> (item->get_data ("marker"))) == 0) {
2059 			break;
2060 		}
2061 		entered_marker = 0;
2062 		if (m_marker->meter().position_lock_style() == MusicTime) {
2063 			m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker music"));
2064 		} else {
2065 			m_marker->set_color_rgba (UIConfiguration::instance().color ("meter marker"));
2066 		}
2067 		break;
2068 
2069 	case TempoMarkerItem:
2070 		if ((t_marker = static_cast<TempoMarker *> (item->get_data ("marker"))) == 0) {
2071 			break;
2072 		}
2073 		entered_marker = 0;
2074 		if (t_marker->tempo().position_lock_style() == MusicTime) {
2075 			t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker music"));
2076 		} else {
2077 			t_marker->set_color_rgba (UIConfiguration::instance().color ("tempo marker"));
2078 		}
2079 		break;
2080 
2081 	case FadeInTrimHandleItem:
2082 	case FadeOutTrimHandleItem:
2083 	case FadeInHandleItem:
2084 	case FadeOutHandleItem:
2085 	{
2086 		ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
2087 		if (rect) {
2088 			rect->set_fill_color (UIConfiguration::instance().color ("inactive fade handle"));
2089 		}
2090 	}
2091 	break;
2092 
2093 	case AutomationTrackItem:
2094 		break;
2095 
2096 	case FeatureLineItem:
2097 	{
2098 		ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2099 		line->set_outline_color (UIConfiguration::instance().color ("zero line"));
2100 	}
2101 	break;
2102 
2103 	default:
2104 		break;
2105 	}
2106 
2107 	return ret;
2108 }
2109 
2110 void
scrub(samplepos_t sample,double current_x)2111 Editor::scrub (samplepos_t sample, double current_x)
2112 {
2113 	double delta;
2114 
2115 	if (scrubbing_direction == 0) {
2116 		/* first move */
2117 		_session->request_locate (sample, MustStop);
2118 		_session->request_transport_speed (0.1, false);
2119 		scrubbing_direction = 1;
2120 
2121 	} else {
2122 
2123 		if (last_scrub_x > current_x) {
2124 
2125 			/* pointer moved to the left */
2126 
2127 			if (scrubbing_direction > 0) {
2128 
2129 				/* we reversed direction to go backwards */
2130 
2131 				scrub_reversals++;
2132 				scrub_reverse_distance += (int) (last_scrub_x - current_x);
2133 
2134 			} else {
2135 
2136 				/* still moving to the left (backwards) */
2137 
2138 				scrub_reversals = 0;
2139 				scrub_reverse_distance = 0;
2140 
2141 				delta = 0.01 * (last_scrub_x - current_x);
2142 				_session->request_transport_speed_nonzero (_session->actual_speed() - delta, false);
2143 			}
2144 
2145 		} else {
2146 			/* pointer moved to the right */
2147 
2148 			if (scrubbing_direction < 0) {
2149 				/* we reversed direction to go forward */
2150 
2151 				scrub_reversals++;
2152 				scrub_reverse_distance += (int) (current_x - last_scrub_x);
2153 
2154 			} else {
2155 				/* still moving to the right */
2156 
2157 				scrub_reversals = 0;
2158 				scrub_reverse_distance = 0;
2159 
2160 				delta = 0.01 * (current_x - last_scrub_x);
2161 				_session->request_transport_speed_nonzero (_session->actual_speed() + delta, false);
2162 			}
2163 		}
2164 
2165 		/* if there have been more than 2 opposite motion moves detected, or one that moves
2166 		   back more than 10 pixels, reverse direction
2167 		*/
2168 
2169 		if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2170 
2171 			if (scrubbing_direction > 0) {
2172 				/* was forwards, go backwards */
2173 				_session->request_transport_speed (-0.1, false);
2174 				scrubbing_direction = -1;
2175 			} else {
2176 				/* was backwards, go forwards */
2177 				_session->request_transport_speed (0.1, false);
2178 				scrubbing_direction = 1;
2179 			}
2180 
2181 			scrub_reverse_distance = 0;
2182 			scrub_reversals = 0;
2183 		}
2184 	}
2185 
2186 	last_scrub_x = current_x;
2187 }
2188 
2189 bool
motion_handler(ArdourCanvas::Item *,GdkEvent * event,bool from_autoscroll)2190 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2191 {
2192 	_last_motion_y = event->motion.y;
2193 
2194 	if (event->motion.is_hint) {
2195 		gint x, y;
2196 
2197 		/* We call this so that MOTION_NOTIFY events continue to be
2198 		 * delivered to the canvas. We need to do this because we set
2199 		 * Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2200 		 * the density of the events, at the expense of a round-trip
2201 		 * to the server. Given that this will mostly occur on cases
2202 		 * where DISPLAY = :0.0, and given the cost of what the motion
2203 		 * event might do, its a good tradeoff.
2204 		 */
2205 
2206 		_track_canvas->get_pointer (x, y);
2207 	}
2208 
2209 	if (current_stepping_trackview) {
2210 		/* don't keep the persistent stepped trackview if the mouse moves */
2211 		current_stepping_trackview = 0;
2212 		step_timeout.disconnect ();
2213 	}
2214 
2215 	if (_session && _session->actively_recording()) {
2216 		/* Sorry. no dragging stuff around while we record */
2217 		return true;
2218 	}
2219 
2220 	update_join_object_range_location (event->motion.y);
2221 
2222 	if (_drags->active ()) {
2223 		//drags change the snapped_cursor location, because we are snapping the thing being dragged, not the actual mouse cursor
2224 		return _drags->motion_handler (event, from_autoscroll);
2225 	} else {
2226 		bool ignored;
2227 		bool peaks_visible = false;
2228 		MusicSample where (0, 0);
2229 		if (mouse_sample (where.sample, ignored)) {
2230 
2231 			/* display peaks */
2232 			if (mouse_mode == MouseContent || ArdourKeyboard::indicates_snap (event->motion.state)) {
2233 				AudioRegionView* arv = dynamic_cast<AudioRegionView*>(entered_regionview);
2234 				if (arv) {
2235 					_region_peak_cursor->set (arv, where.sample, samples_per_pixel);
2236 					peaks_visible = true;
2237 				}
2238 			}
2239 
2240 			/* the snapped_cursor shows where an operation (like Split) is going to occur */
2241 			snap_to_with_modifier (where, event);
2242 			set_snapped_cursor_position (where.sample);
2243 		}
2244 
2245 		if (!peaks_visible) {
2246 			_region_peak_cursor->hide ();
2247 		}
2248 	}
2249 
2250 	return false;
2251 }
2252 
2253 bool
can_remove_control_point(ArdourCanvas::Item * item)2254 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2255 {
2256 	ControlPoint* control_point;
2257 
2258 	if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2259 		fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2260 		abort(); /*NOTREACHED*/
2261 	}
2262 
2263 	AutomationLine& line = control_point->line ();
2264 	if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2265 		/* we shouldn't remove the first or last gain point in region gain lines */
2266 		if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2267 			return false;
2268 		}
2269 	}
2270 
2271 	return true;
2272 }
2273 
2274 void
remove_control_point(ArdourCanvas::Item * item)2275 Editor::remove_control_point (ArdourCanvas::Item* item)
2276 {
2277 	if (!can_remove_control_point (item)) {
2278 		return;
2279 	}
2280 
2281 	ControlPoint* control_point;
2282 
2283 	if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2284 		fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2285 		abort(); /*NOTREACHED*/
2286 	}
2287 
2288 	control_point->line().remove_point (*control_point);
2289 }
2290 
2291 void
edit_control_point(ArdourCanvas::Item * item)2292 Editor::edit_control_point (ArdourCanvas::Item* item)
2293 {
2294 	ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2295 
2296 	if (p == 0) {
2297 		fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2298 		abort(); /*NOTREACHED*/
2299 	}
2300 
2301 	ControlPointDialog d (p);
2302 
2303 	if (d.run () != RESPONSE_ACCEPT) {
2304 		return;
2305 	}
2306 
2307 	p->line().modify_point_y (*p, d.get_y_fraction ());
2308 }
2309 
2310 void
edit_notes(MidiRegionView * mrv)2311 Editor::edit_notes (MidiRegionView* mrv)
2312 {
2313 	MidiRegionView::Selection const & s = mrv->selection();
2314 
2315 	if (s.empty ()) {
2316 		return;
2317 	}
2318 
2319 	EditNoteDialog* d = new EditNoteDialog (mrv, s);
2320 	d->show_all ();
2321 
2322 	d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2323 }
2324 
2325 void
note_edit_done(int r,EditNoteDialog * d)2326 Editor::note_edit_done (int r, EditNoteDialog* d)
2327 {
2328 	d->done (r);
2329 	delete d;
2330 }
2331 
2332 void
edit_region(RegionView * rv)2333 Editor::edit_region (RegionView* rv)
2334 {
2335 	if (UIConfiguration::instance().get_use_double_click_to_zoom_to_selection()) {
2336 		temporal_zoom_selection (Both);
2337 	} else {
2338 		rv->show_region_editor ();
2339 	}
2340 }
2341 
2342 void
visible_order_range(int * low,int * high) const2343 Editor::visible_order_range (int* low, int* high) const
2344 {
2345 	*low = TimeAxisView::max_order ();
2346 	*high = 0;
2347 
2348 	for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2349 
2350 		RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2351 
2352 		if (rtv && !rtv->hidden()) {
2353 
2354 			if (*high < rtv->order()) {
2355 				*high = rtv->order ();
2356 			}
2357 
2358 			if (*low > rtv->order()) {
2359 				*low = rtv->order ();
2360 			}
2361 		}
2362 	}
2363 }
2364 
2365 void
region_view_item_click(AudioRegionView & rv,GdkEventButton * event)2366 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2367 {
2368 	/* Either add to or set the set the region selection, unless
2369 	 * this is an alignment click (control used)
2370 	 */
2371 
2372 	if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2373 
2374 		samplepos_t where = get_preferred_edit_position();
2375 
2376 		if (where >= 0) {
2377 
2378 			if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2379 
2380 				align_region (rv.region(), SyncPoint, where);
2381 
2382 			} else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2383 
2384 				align_region (rv.region(), End, where);
2385 
2386 			} else {
2387 
2388 				align_region (rv.region(), Start, where);
2389 			}
2390 		}
2391 	}
2392 }
2393 
2394 void
collect_new_region_view(RegionView * rv)2395 Editor::collect_new_region_view (RegionView* rv)
2396 {
2397 	latest_regionviews.push_back (rv);
2398 }
2399 
2400 void
collect_and_select_new_region_view(RegionView * rv)2401 Editor::collect_and_select_new_region_view (RegionView* rv)
2402 {
2403 	selection->add(rv);
2404 	latest_regionviews.push_back (rv);
2405 }
2406 
2407 void
cancel_selection()2408 Editor::cancel_selection ()
2409 {
2410 	for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2411 		(*i)->hide_selection ();
2412 	}
2413 
2414 	selection->clear ();
2415 	clicked_selection = 0;
2416 }
2417 
2418 void
cancel_time_selection()2419 Editor::cancel_time_selection ()
2420 {
2421 	for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2422 		(*i)->hide_selection ();
2423 	}
2424 	selection->time.clear ();
2425 	clicked_selection = 0;
2426 }
2427 
2428 void
point_trim(GdkEvent * event,samplepos_t new_bound)2429 Editor::point_trim (GdkEvent* event, samplepos_t new_bound)
2430 {
2431 	RegionView* rv = clicked_regionview;
2432 
2433 	/* Choose action dependant on which button was pressed */
2434 	switch (event->button.button) {
2435 	case 1:
2436 		begin_reversible_command (_("start point trim"));
2437 
2438 		if (selection->selected (rv)) {
2439 			for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2440 			     i != selection->regions.by_layer().end(); ++i)
2441 			{
2442 				if (!(*i)->region()->locked()) {
2443 					(*i)->region()->clear_changes ();
2444 					(*i)->region()->trim_front (new_bound);
2445 					_session->add_command(new StatefulDiffCommand ((*i)->region()));
2446 				}
2447 			}
2448 
2449 		} else {
2450 			if (!rv->region()->locked()) {
2451 				rv->region()->clear_changes ();
2452 				rv->region()->trim_front (new_bound);
2453 				_session->add_command(new StatefulDiffCommand (rv->region()));
2454 			}
2455 		}
2456 
2457 		commit_reversible_command();
2458 
2459 		break;
2460 	case 2:
2461 		begin_reversible_command (_("end point trim"));
2462 
2463 		if (selection->selected (rv)) {
2464 
2465 			for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2466 			{
2467 				if (!(*i)->region()->locked()) {
2468 					(*i)->region()->clear_changes();
2469 					(*i)->region()->trim_end (new_bound);
2470 					_session->add_command(new StatefulDiffCommand ((*i)->region()));
2471 				}
2472 			}
2473 
2474 		} else {
2475 
2476 			if (!rv->region()->locked()) {
2477 				rv->region()->clear_changes ();
2478 				rv->region()->trim_end (new_bound);
2479 				_session->add_command (new StatefulDiffCommand (rv->region()));
2480 			}
2481 		}
2482 
2483 		commit_reversible_command();
2484 
2485 		break;
2486 	default:
2487 		break;
2488 	}
2489 }
2490 
2491 void
hide_marker(ArdourCanvas::Item * item,GdkEvent *)2492 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2493 {
2494 	ArdourMarker* marker;
2495 	bool is_start;
2496 
2497 	if ((marker = static_cast<ArdourMarker *> (item->get_data ("marker"))) == 0) {
2498 		fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2499 		abort(); /*NOTREACHED*/
2500 	}
2501 
2502 	Location* location = find_location_from_marker (marker, is_start);
2503 	location->set_hidden (true, this);
2504 }
2505 
2506 gint
mouse_rename_region(ArdourCanvas::Item *,GdkEvent *)2507 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2508 {
2509 	using namespace Gtkmm2ext;
2510 
2511 	ArdourWidgets::Prompter prompter (false);
2512 
2513 	prompter.set_prompt (_("Name for region:"));
2514 	prompter.set_initial_text (clicked_regionview->region()->name());
2515 	prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2516 	prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2517 	prompter.show_all ();
2518 	switch (prompter.run ()) {
2519 	case Gtk::RESPONSE_ACCEPT:
2520 		string str;
2521 		prompter.get_result(str);
2522 		if (str.length()) {
2523 			clicked_regionview->region()->set_name (str);
2524 		}
2525 		break;
2526 	}
2527 	return true;
2528 }
2529 
2530 
2531 void
mouse_brush_insert_region(RegionView * rv,samplepos_t pos)2532 Editor::mouse_brush_insert_region (RegionView* rv, samplepos_t pos)
2533 {
2534 	/* no brushing without a useful quantize setting */
2535 	if (_grid_type == GridTypeNone)
2536 		return;
2537 
2538 	/* don't brush a copy over the original */
2539 
2540 	if (pos == rv->region()->position()) {
2541 		return;
2542 	}
2543 
2544 	RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2545 
2546 	if (!rtv || !rtv->is_track()) {
2547 		return;
2548 	}
2549 
2550 	boost::shared_ptr<Playlist> playlist = rtv->playlist();
2551 
2552 	playlist->clear_changes ();
2553 	boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2554 	playlist->add_region (new_region, pos);
2555 	_session->add_command (new StatefulDiffCommand (playlist));
2556 
2557 	/* playlist is frozen, so we have to update manually XXX this is disgusting */
2558 
2559 	//playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2560 }
2561 
2562 gint
track_height_step_timeout()2563 Editor::track_height_step_timeout ()
2564 {
2565 	if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2566 		current_stepping_trackview = 0;
2567 		return false;
2568 	}
2569 	return true;
2570 }
2571 
2572 void
add_region_drag(ArdourCanvas::Item * item,GdkEvent *,RegionView * region_view)2573 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2574 {
2575 	assert (region_view);
2576 
2577 	if (!region_view->region()->playlist()) {
2578 		return;
2579 	}
2580 
2581 	switch (Config->get_edit_mode()) {
2582 		case Splice:
2583 			_drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2584 			break;
2585 		case Ripple:
2586 			_drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2587 			break;
2588 		default:
2589 			_drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false));
2590 			break;
2591 
2592 	}
2593 }
2594 
2595 void
add_region_copy_drag(ArdourCanvas::Item * item,GdkEvent *,RegionView * region_view)2596 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2597 {
2598 	assert (region_view);
2599 
2600 	if (!region_view->region()->playlist()) {
2601 		return;
2602 	}
2603 
2604 	_drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true));
2605 }
2606 
2607 void
add_region_brush_drag(ArdourCanvas::Item * item,GdkEvent *,RegionView * region_view)2608 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2609 {
2610 	assert (region_view);
2611 
2612 	if (!region_view->region()->playlist()) {
2613 		return;
2614 	}
2615 
2616 	if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2617 		return;
2618 	}
2619 
2620 	std::list<RegionView*> empty;
2621 	_drags->add (new RegionBrushDrag (this, item, region_view, empty));
2622 }
2623 
2624 /** Start a grab where a time range is selected, track(s) are selected, and the
2625  *  user clicks and drags a region with a modifier in order to create a new region containing
2626  *  the section of the clicked region that lies within the time range.
2627  */
2628 void
start_selection_grab(ArdourCanvas::Item *,GdkEvent * event)2629 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2630 {
2631 	if (clicked_regionview == 0) {
2632 		return;
2633 	}
2634 
2635 	/* lets try to create new Region for the selection */
2636 
2637 	vector<boost::shared_ptr<Region> > new_regions;
2638 	create_region_from_selection (new_regions);
2639 
2640 	if (new_regions.empty()) {
2641 		return;
2642 	}
2643 
2644 	/* XXX fix me one day to use all new regions */
2645 
2646 	boost::shared_ptr<Region> region (new_regions.front());
2647 
2648 	/* add it to the current stream/playlist.
2649 	 *
2650 	 * tricky: the streamview for the track will add a new regionview. we will
2651 	 * catch the signal it sends when it creates the regionview to
2652 	 * set the regionview we want to then drag.
2653 	 */
2654 
2655 	latest_regionviews.clear();
2656 	sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2657 
2658 	/* A selection grab currently creates two undo/redo operations, one for
2659 	 * creating the new region and another for moving it.
2660 	 */
2661 	begin_reversible_command (Operations::selection_grab);
2662 
2663 	boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2664 
2665 	playlist->clear_changes ();
2666 	clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2667 	_session->add_command(new StatefulDiffCommand (playlist));
2668 
2669 	c.disconnect ();
2670 
2671 	if (latest_regionviews.empty()) {
2672 		/* something went wrong */
2673 		abort_reversible_command ();
2674 		return;
2675 	}
2676 
2677 	/* we need to deselect all other regionviews, and select this one
2678 	 * i'm ignoring undo stuff, because the region creation will take care of it
2679 	 */
2680 
2681 	selection->set (latest_regionviews);
2682 
2683 	commit_reversible_command ();
2684 
2685 	_drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false), event);
2686 }
2687 
2688 void
escape()2689 Editor::escape ()
2690 {
2691 	if (_drags->active ()) {
2692 		_drags->abort ();
2693 	} else if (_session) {
2694 
2695 		midi_action (&MidiRegionView::clear_note_selection);
2696 
2697 		selection->clear ();
2698 
2699 		/* if session is playing a range, cancel that */
2700 		if (_session->get_play_range()) {
2701 			_session->request_cancel_play_range();
2702 		}
2703 
2704 		if (_session->solo_selection_active()) {
2705 			StripableList sl;
2706 			_session->solo_selection (sl, false);
2707 		}
2708 	}
2709 
2710 	ARDOUR_UI::instance()->reset_focus (&contents());
2711 }
2712 
2713 /** Update _join_object_range_state which indicate whether we are over the top
2714  *  or bottom half of a route view, used by the `join object/range' tool
2715  *  mode. Coordinates in canvas space.
2716  */
2717 void
update_join_object_range_location(double y)2718 Editor::update_join_object_range_location (double y)
2719 {
2720 	if (!get_smart_mode()) {
2721 		_join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2722 		return;
2723 	}
2724 
2725 	JoinObjectRangeState const old = _join_object_range_state;
2726 
2727 	if (mouse_mode == MouseObject) {
2728 		_join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2729 	} else if (mouse_mode == MouseRange) {
2730 		_join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2731 	}
2732 
2733 	if (entered_regionview) {
2734 
2735 		/* TODO: there is currently a bug here(?)
2736 		 * when we are inside a region fade handle, it acts as though we are in range mode because it is in the top half of the region
2737 		 * can it be fixed here?
2738 		 */
2739 
2740 		ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2741 		double const c = item_space.y / entered_regionview->height();
2742 
2743 		_join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2744 
2745 		Editor::EnterContext* ctx = get_enter_context(RegionItem);
2746 		if (_join_object_range_state != old && ctx) {
2747 			ctx->cursor_ctx->change(which_track_cursor());
2748 		}
2749 
2750 	} else if (entered_track) {
2751 
2752 		RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2753 
2754 		if (entered_route_view) {
2755 
2756 			double cx = 0;
2757 			double cy = y;
2758 
2759 			entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2760 
2761 			double track_height = entered_route_view->view()->child_height();
2762 			if (UIConfiguration::instance().get_show_name_highlight()) {
2763 				track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2764 			}
2765 			double const c = cy / track_height;
2766 
2767 
2768 			if (c <= 0.5) {
2769 				_join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2770 			} else {
2771 				_join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2772 			}
2773 
2774 		} else {
2775 			/* Other kinds of tracks use object mode */
2776 			_join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2777 		}
2778 
2779 		Editor::EnterContext* ctx = get_enter_context(StreamItem);
2780 		if (_join_object_range_state != old && ctx) {
2781 			ctx->cursor_ctx->change(which_track_cursor());
2782 		}
2783 	}
2784 }
2785 
2786 Editing::MouseMode
effective_mouse_mode() const2787 Editor::effective_mouse_mode () const
2788 {
2789 	if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2790 		return MouseObject;
2791 	} else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2792 		return MouseRange;
2793 	}
2794 
2795 	return mouse_mode;
2796 }
2797 
2798 void
remove_midi_note(ArdourCanvas::Item * item,GdkEvent *)2799 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2800 {
2801 	NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2802 	assert (e);
2803 
2804 	e->region_view().delete_note (e->note ());
2805 }
2806 
2807 /** Obtain the pointer position in canvas coordinates */
2808 void
get_pointer_position(double & x,double & y) const2809 Editor::get_pointer_position (double& x, double& y) const
2810 {
2811 	int px, py;
2812 	_track_canvas->get_pointer (px, py);
2813 	_track_canvas->window_to_canvas (px, py, x, y);
2814 }
2815