1 /*
2  * Copyright (C) 2005-2017 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2006-2015 David Robillard <d@drobilla.net>
4  * Copyright (C) 2007-2017 Tim Mayberry <mojofunk@gmail.com>
5  * Copyright (C) 2007 Doug McLain <doug@nostar.net>
6  * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
7  * Copyright (C) 2013-2019 Robin Gareus <robin@gareus.org>
8  * Copyright (C) 2014-2019 Ben Loftis <ben@harrisonconsoles.com>
9  * Copyright (C) 2015-2017 Nick Mainsbridge <mainsbridge@gmail.com>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License along
22  * with this program; if not, write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24  */
25 
26 #ifdef WAF_BUILD
27 #include "gtk2ardour-config.h"
28 #endif
29 
30 #include "gtkmm2ext/utils.h"
31 
32 #include "ardour/profile.h"
33 #include "ardour/rc_configuration.h"
34 #include "ardour/smf_source.h"
35 
36 #include "pbd/error.h"
37 
38 #include "canvas/canvas.h"
39 #include "canvas/rectangle.h"
40 #include "canvas/pixbuf.h"
41 #include "canvas/scroll_group.h"
42 #include "canvas/text.h"
43 #include "canvas/debug.h"
44 
45 #include "ardour_ui.h"
46 #include "automation_time_axis.h"
47 #include "editor.h"
48 #include "editing.h"
49 #include "rgb_macros.h"
50 #include "utils.h"
51 #include "audio_time_axis.h"
52 #include "editor_drag.h"
53 #include "region_view.h"
54 #include "editor_group_tabs.h"
55 #include "editor_summary.h"
56 #include "video_timeline.h"
57 #include "keyboard.h"
58 #include "editor_cursors.h"
59 #include "mouse_cursors.h"
60 #include "note_base.h"
61 #include "region_peak_cursor.h"
62 #include "ui_config.h"
63 #include "verbose_cursor.h"
64 
65 #include "pbd/i18n.h"
66 
67 using namespace std;
68 using namespace ARDOUR;
69 using namespace ARDOUR_UI_UTILS;
70 using namespace PBD;
71 using namespace Gtk;
72 using namespace Glib;
73 using namespace Gtkmm2ext;
74 using namespace Editing;
75 
76 void
initialize_canvas()77 Editor::initialize_canvas ()
78 {
79 	_track_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, vertical_adjustment);
80 	_track_canvas = _track_canvas_viewport->canvas ();
81 
82 	_track_canvas->set_background_color (UIConfiguration::instance().color ("arrange base"));
83 	_track_canvas->use_nsglview ();
84 
85 	/* scroll group for items that should not automatically scroll
86 	 *  (e.g verbose cursor). It shares the canvas coordinate space.
87 	*/
88 	no_scroll_group = new ArdourCanvas::Container (_track_canvas->root());
89 
90 	ArdourCanvas::ScrollGroup* hsg;
91 	ArdourCanvas::ScrollGroup* hg;
92 	ArdourCanvas::ScrollGroup* cg;
93 
94 	h_scroll_group = hg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
95 	CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll");
96 	_track_canvas->add_scroller (*hg);
97 
98 	hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_track_canvas->root(),
99 							       ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically|
100 													     ArdourCanvas::ScrollGroup::ScrollsHorizontally));
101 	CANVAS_DEBUG_NAME (hv_scroll_group, "canvas hv scroll");
102 	_track_canvas->add_scroller (*hsg);
103 
104 	cursor_scroll_group = cg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
105 	CANVAS_DEBUG_NAME (cursor_scroll_group, "canvas cursor scroll");
106 	_track_canvas->add_scroller (*cg);
107 
108 	_verbose_cursor = new VerboseCursor (this);
109 	_region_peak_cursor = new RegionPeakCursor (get_noscroll_group ());
110 
111 	/*a group to hold global rects like punch/loop indicators */
112 	global_rect_group = new ArdourCanvas::Container (hv_scroll_group);
113 	CANVAS_DEBUG_NAME (global_rect_group, "global rect group");
114 
115         transport_loop_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
116 	CANVAS_DEBUG_NAME (transport_loop_range_rect, "loop rect");
117 	transport_loop_range_rect->hide();
118 
119 	transport_punch_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
120 	CANVAS_DEBUG_NAME (transport_punch_range_rect, "punch rect");
121 	transport_punch_range_rect->hide();
122 
123 	/*a group to hold time (measure) lines */
124 	time_line_group = new ArdourCanvas::Container (h_scroll_group);
125 	CANVAS_DEBUG_NAME (time_line_group, "time line group");
126 
127 	_trackview_group = new ArdourCanvas::Container (hv_scroll_group);
128 	CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
129 
130 	// used as rubberband rect
131 	rubberband_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
132 	rubberband_rect->hide();
133 
134 	/* a group to hold stuff while it gets dragged around. Must be the
135 	 * uppermost (last) group with hv_scroll_group as a parent
136 	 */
137 	_drag_motion_group = new ArdourCanvas::Container (hv_scroll_group);
138 	CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion");
139 
140 	/* TIME BAR CANVAS */
141 
142 	_time_markers_group = new ArdourCanvas::Container (h_scroll_group);
143 	CANVAS_DEBUG_NAME (_time_markers_group, "time bars");
144 
145 	cd_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, 0.0));
146 	CANVAS_DEBUG_NAME (cd_marker_group, "cd marker group");
147 	/* the vide is temporarily placed a the same location as the
148 	   cd_marker_group, but is moved later.
149 	*/
150 	videotl_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0));
151 	CANVAS_DEBUG_NAME (videotl_group, "videotl group");
152 	marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, timebar_height + 1.0));
153 	CANVAS_DEBUG_NAME (marker_group, "marker group");
154 	transport_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 2.0) + 1.0));
155 	CANVAS_DEBUG_NAME (transport_marker_group, "transport marker group");
156 	range_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 3.0) + 1.0));
157 	CANVAS_DEBUG_NAME (range_marker_group, "range marker group");
158 	tempo_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 4.0) + 1.0));
159 	CANVAS_DEBUG_NAME (tempo_group, "tempo group");
160 	meter_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0));
161 	CANVAS_DEBUG_NAME (meter_group, "meter group");
162 
163 	meter_bar = new ArdourCanvas::Rectangle (meter_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
164 	CANVAS_DEBUG_NAME (meter_bar, "meter Bar");
165 	meter_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
166 
167 	tempo_bar = new ArdourCanvas::Rectangle (tempo_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
168 	CANVAS_DEBUG_NAME (tempo_bar, "Tempo  Bar");
169 	tempo_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
170 
171 	range_marker_bar = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
172 	CANVAS_DEBUG_NAME (range_marker_bar, "Range Marker Bar");
173 	range_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
174 
175 	transport_marker_bar = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
176 	CANVAS_DEBUG_NAME (transport_marker_bar, "transport Marker Bar");
177 	transport_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
178 
179 	marker_bar = new ArdourCanvas::Rectangle (marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
180 	CANVAS_DEBUG_NAME (marker_bar, "Marker Bar");
181 	marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
182 
183 	cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
184 	CANVAS_DEBUG_NAME (cd_marker_bar, "CD Marker Bar");
185 	cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
186 
187 	ARDOUR_UI::instance()->video_timeline = new VideoTimeLine(this, videotl_group, (timebar_height * videotl_bar_height));
188 
189 	cd_marker_bar_drag_rect = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
190 	CANVAS_DEBUG_NAME (cd_marker_bar_drag_rect, "cd marker drag");
191 	cd_marker_bar_drag_rect->set_outline (false);
192 	cd_marker_bar_drag_rect->hide ();
193 
194 	range_bar_drag_rect = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
195 	CANVAS_DEBUG_NAME (range_bar_drag_rect, "range drag");
196 	range_bar_drag_rect->set_outline (false);
197 	range_bar_drag_rect->hide ();
198 
199 	transport_bar_drag_rect = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
200 	CANVAS_DEBUG_NAME (transport_bar_drag_rect, "transport drag");
201 	transport_bar_drag_rect->set_outline (false);
202 	transport_bar_drag_rect->hide ();
203 
204 	transport_punchin_line = new ArdourCanvas::Line (hv_scroll_group);
205 	transport_punchin_line->set_x0 (0);
206 	transport_punchin_line->set_y0 (0);
207 	transport_punchin_line->set_x1 (0);
208 	transport_punchin_line->set_y1 (ArdourCanvas::COORD_MAX);
209 	transport_punchin_line->hide ();
210 
211 	transport_punchout_line  = new ArdourCanvas::Line (hv_scroll_group);
212 	transport_punchout_line->set_x0 (0);
213 	transport_punchout_line->set_y0 (0);
214 	transport_punchout_line->set_x1 (0);
215 	transport_punchout_line->set_y1 (ArdourCanvas::COORD_MAX);
216 	transport_punchout_line->hide();
217 
218 	tempo_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_tempo_bar_event), tempo_bar));
219 	meter_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_meter_bar_event), meter_bar));
220 	marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_marker_bar_event), marker_bar));
221 	cd_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_cd_marker_bar_event), cd_marker_bar));
222 	videotl_group->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_videotl_bar_event), videotl_group));
223 	range_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_range_marker_bar_event), range_marker_bar));
224 	transport_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_transport_marker_bar_event), transport_marker_bar));
225 
226 	_playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event);
227 
228 	_snapped_cursor = new EditorCursor (*this);
229 
230 	_canvas_drop_zone = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, 0.0));
231 	/* this thing is transparent */
232 	_canvas_drop_zone->set_fill (false);
233 	_canvas_drop_zone->set_outline (false);
234 	_canvas_drop_zone->Event.connect (sigc::mem_fun (*this, &Editor::canvas_drop_zone_event));
235 
236 	/* these signals will initially be delivered to the canvas itself, but if they end up remaining unhandled, they are passed to Editor-level
237 	   handlers.
238 	*/
239 
240 	_track_canvas->signal_scroll_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_scroll_event), true));
241 	_track_canvas->signal_motion_notify_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_motion_notify_event));
242 	_track_canvas->signal_button_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_press_event));
243 	_track_canvas->signal_button_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_release_event));
244 	_track_canvas->signal_drag_motion().connect (sigc::mem_fun (*this, &Editor::track_canvas_drag_motion));
245 	_track_canvas->signal_key_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_press));
246 	_track_canvas->signal_key_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_release));
247 
248 	_track_canvas->set_name ("EditorMainCanvas");
249 	_track_canvas->add_events (Gdk::POINTER_MOTION_HINT_MASK | Gdk::SCROLL_MASK | Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
250 	_track_canvas->signal_leave_notify_event().connect (sigc::mem_fun(*this, &Editor::left_track_canvas), false);
251 	_track_canvas->signal_enter_notify_event().connect (sigc::mem_fun(*this, &Editor::entered_track_canvas), false);
252 	_track_canvas->set_flags (CAN_FOCUS);
253 
254 	_track_canvas->PreRender.connect (sigc::mem_fun(*this, &Editor::pre_render));
255 
256 	/* set up drag-n-drop */
257 
258 	vector<TargetEntry> target_table;
259 
260 	target_table.push_back (TargetEntry ("regions")); // DnD from the region list will generate this target
261 	target_table.push_back (TargetEntry ("sources")); // DnD from the source list will generate this target
262 	target_table.push_back (TargetEntry ("text/uri-list"));
263 	target_table.push_back (TargetEntry ("text/plain"));
264 	target_table.push_back (TargetEntry ("application/x-rootwin-drop"));
265 
266 	_track_canvas->drag_dest_set (target_table);
267 	_track_canvas->signal_drag_data_received().connect (sigc::mem_fun(*this, &Editor::track_canvas_drag_data_received));
268 
269 	_track_canvas_viewport->signal_size_allocate().connect (sigc::mem_fun(*this, &Editor::track_canvas_viewport_allocate));
270 
271 	initialize_rulers ();
272 
273 	UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &Editor::color_handler));
274 	color_handler();
275 
276 }
277 
278 void
track_canvas_viewport_allocate(Gtk::Allocation alloc)279 Editor::track_canvas_viewport_allocate (Gtk::Allocation alloc)
280 {
281 	_canvas_viewport_allocation = alloc;
282 	track_canvas_viewport_size_allocated ();
283 }
284 
285 void
track_canvas_viewport_size_allocated()286 Editor::track_canvas_viewport_size_allocated ()
287 {
288 	bool height_changed = _visible_canvas_height != _canvas_viewport_allocation.get_height();
289 
290 	_visible_canvas_width  = _canvas_viewport_allocation.get_width ();
291 	_visible_canvas_height = _canvas_viewport_allocation.get_height ();
292 
293 	_canvas_drop_zone->set_y1 (_canvas_drop_zone->y0() + (_visible_canvas_height - 20.0));
294 
295 	// SHOWTRACKS
296 
297 	if (height_changed) {
298 
299 		vertical_adjustment.set_page_size (_visible_canvas_height);
300 		if ((vertical_adjustment.get_value() + _visible_canvas_height) >= vertical_adjustment.get_upper()) {
301 			/*
302 			   We're increasing the size of the canvas while the bottom is visible.
303 			   We scroll down to keep in step with the controls layout.
304 			*/
305 			vertical_adjustment.set_value (_full_canvas_height - _visible_canvas_height);
306 		}
307 
308 		set_visible_track_count (_visible_track_count);
309 	}
310 
311 	update_fixed_rulers();
312 	update_tempo_based_rulers ();
313 	redisplay_grid (false);
314 	_summary->set_overlays_dirty ();
315 }
316 
317 void
reset_controls_layout_width()318 Editor::reset_controls_layout_width ()
319 {
320 	GtkRequisition req = { 0, 0 };
321 	gint w;
322 
323 	edit_controls_vbox.size_request (req);
324 	w = req.width;
325 
326 	if (_group_tabs->is_visible()) {
327 		_group_tabs->size_request (req);
328 		w += req.width;
329 	}
330 
331 	/* the controls layout has no horizontal scrolling, its visible
332 	   width is always equal to the total width of its contents.
333 	*/
334 
335 	controls_layout.property_width() = w;
336 	controls_layout.property_width_request() = w;
337 }
338 
339 void
reset_controls_layout_height(int32_t h)340 Editor::reset_controls_layout_height (int32_t h)
341 {
342 	/* ensure that the rect that represents the "bottom" of the canvas
343 	 * (the drag-n-drop zone) is, in fact, at the bottom.
344 	 */
345 
346 	_canvas_drop_zone->set_position (ArdourCanvas::Duple (0, h));
347 
348 	/* track controls layout must span the full height of "h" (all tracks)
349 	 * plus the bottom rect.
350 	 */
351 
352 	h += _canvas_drop_zone->height ();
353 
354 	/* set the height of the scrollable area (i.e. the sum of all contained widgets)
355 	 * for the controls layout. The size request is set elsewhere.
356 	 */
357 
358 	controls_layout.property_height() = h;
359 
360 }
361 
362 bool
track_canvas_map_handler(GdkEventAny *)363 Editor::track_canvas_map_handler (GdkEventAny* /*ev*/)
364 {
365 	if (!_cursor_stack.empty()) {
366 		set_canvas_cursor (get_canvas_cursor());
367 	} else {
368 		PBD::error << "cursor stack is empty" << endmsg;
369 	}
370 	return false;
371 }
372 
373 /** This is called when something is dropped onto the track canvas */
374 void
track_canvas_drag_data_received(const RefPtr<Gdk::DragContext> & context,int x,int y,const SelectionData & data,guint info,guint time)375 Editor::track_canvas_drag_data_received (const RefPtr<Gdk::DragContext>& context,
376 					 int x, int y,
377 					 const SelectionData& data,
378 					 guint info, guint time)
379 {
380 	if (!ARDOUR_UI_UTILS::engine_is_running ()) {
381 		return;
382 	}
383 	if (data.get_target() == X_("regions")) {
384 		drop_regions (context, x, y, data, info, time, true);
385 	} else if (data.get_target() == X_("sources")) {
386 		drop_regions (context, x, y, data, info, time, false);
387 	} else {
388 		drop_paths (context, x, y, data, info, time);
389 	}
390 }
391 
392 bool
idle_drop_paths(vector<string> paths,samplepos_t sample,double ypos,bool copy)393 Editor::idle_drop_paths (vector<string> paths, samplepos_t sample, double ypos, bool copy)
394 {
395 	drop_paths_part_two (paths, sample, ypos, copy);
396 	return false;
397 }
398 
399 void
drop_paths_part_two(const vector<string> & paths,samplepos_t sample,double ypos,bool copy)400 Editor::drop_paths_part_two (const vector<string>& paths, samplepos_t sample, double ypos, bool copy)
401 {
402 	RouteTimeAxisView* tv;
403 
404 	/* MIDI files must always be imported, because we consider them
405 	 * writable. So split paths into two vectors, and follow the import
406 	 * path on the MIDI part.
407 	 */
408 
409 	vector<string> midi_paths;
410 	vector<string> audio_paths;
411 
412 	for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
413 		if (SMFSource::safe_midi_file_extension (*i)) {
414 			midi_paths.push_back (*i);
415 		} else {
416 			audio_paths.push_back (*i);
417 		}
418 	}
419 
420 
421 	std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (ypos, false);
422 	if (tvp.first == 0) {
423 
424 		/* drop onto canvas background: create new tracks */
425 
426 		InstrumentSelector is; // instantiation builds instrument-list and sets default.
427 		do_import (midi_paths, Editing::ImportDistinctFiles, ImportAsTrack, SrcBest, SMFTrackName, SMFTempoIgnore, sample, is.selected_instrument(), false);
428 
429 		if (UIConfiguration::instance().get_only_copy_imported_files() || copy) {
430 			do_import (audio_paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack,
431 			           SrcBest, SMFTrackName, SMFTempoIgnore, sample);
432 		} else {
433 			do_embed (audio_paths, Editing::ImportDistinctFiles, ImportAsTrack, sample);
434 		}
435 
436 	} else if ((tv = dynamic_cast<RouteTimeAxisView*> (tvp.first)) != 0) {
437 
438 		/* check that its a track, not a bus */
439 
440 		if (tv->track()) {
441 			/* select the track, then embed/import */
442 			selection->set (tv);
443 
444 			do_import (midi_paths, Editing::ImportSerializeFiles, ImportToTrack,
445 				   SrcBest, SMFTrackName, SMFTempoIgnore, sample);
446 
447 			if (UIConfiguration::instance().get_only_copy_imported_files() || copy) {
448 				do_import (audio_paths, Editing::ImportSerializeFiles, Editing::ImportToTrack,
449 				           SrcBest, SMFTrackName, SMFTempoIgnore, sample, boost::shared_ptr<PluginInfo>(), false);
450 			} else {
451 				do_embed (audio_paths, Editing::ImportSerializeFiles, ImportToTrack, sample);
452 			}
453 		}
454 	}
455 }
456 
457 void
drop_paths(const RefPtr<Gdk::DragContext> & context,int x,int y,const SelectionData & data,guint info,guint time)458 Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
459 		    int x, int y,
460 		    const SelectionData& data,
461 		    guint info, guint time)
462 {
463 	vector<string> paths;
464 	GdkEvent ev;
465 	double cy;
466 
467 	if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
468 
469 		/* D-n-D coordinates are window-relative, so convert to canvas coordinates
470 		 */
471 
472 		ev.type = GDK_BUTTON_RELEASE;
473 		ev.button.x = x;
474 		ev.button.y = y;
475 
476 		MusicSample when (window_event_sample (&ev, 0, &cy), 0);
477 		snap_to (when);
478 
479 		bool copy = ((context->get_actions() & (Gdk::ACTION_COPY | Gdk::ACTION_LINK | Gdk::ACTION_MOVE)) == Gdk::ACTION_COPY);
480 #ifdef __APPLE__
481 		/* We are not allowed to call recursive main event loops from within
482 		   the main event loop with GTK/Quartz. Since import/embed wants
483 		   to push up a progress dialog, defer all this till we go idle.
484 		*/
485 		Glib::signal_idle().connect (sigc::bind (sigc::mem_fun (*this, &Editor::idle_drop_paths), paths, when.sample, cy, copy));
486 #else
487 		drop_paths_part_two (paths, when.sample, cy, copy);
488 #endif
489 	}
490 
491 	context->drag_finish (true, false, time);
492 }
493 
494 /** @param allow_horiz true to allow horizontal autoscroll, otherwise false.
495  *
496  *  @param allow_vert true to allow vertical autoscroll, otherwise false.
497  *
498  */
499 void
maybe_autoscroll(bool allow_horiz,bool allow_vert,bool from_headers)500 Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
501 {
502 	Gtk::Window* toplevel = dynamic_cast<Gtk::Window*>(contents().get_toplevel());
503 
504 	if (!toplevel) {
505 		return;
506 	}
507 
508 	if (!UIConfiguration::instance().get_autoscroll_editor () || autoscroll_active ()) {
509 		return;
510 	}
511 
512 	/* define a rectangular boundary for scrolling. If the mouse moves
513 	 * outside of this area and/or continue to be outside of this area,
514 	 * then we will continuously auto-scroll the canvas in the appropriate
515 	 * direction(s)
516 	 *
517 	 * the boundary is defined in coordinates relative to the toplevel
518 	 * window since that is what we're going to call ::get_pointer() on
519 	 * during autoscrolling to determine if we're still outside the
520 	 * boundary or not.
521 	 */
522 
523 	ArdourCanvas::Rect scrolling_boundary;
524 	Gtk::Allocation alloc;
525 
526 	if (from_headers) {
527 		alloc = controls_layout.get_allocation ();
528 
529 		int wx, wy;
530 
531 		controls_layout.get_parent()->translate_coordinates (*toplevel,
532 		                                                     alloc.get_x(), alloc.get_y(),
533 		                                                     wx, wy);
534 
535 		scrolling_boundary = ArdourCanvas::Rect (wx, wy, wx + alloc.get_width(), wy + alloc.get_height());
536 
537 
538 	} else {
539 		alloc = _track_canvas_viewport->get_allocation ();
540 
541 		/* reduce height by the height of the timebars, which happens
542 		   to correspond to the position of the hv_scroll_group.
543 		*/
544 
545 		alloc.set_height (alloc.get_height() - hv_scroll_group->position().y);
546 		alloc.set_y (alloc.get_y() + hv_scroll_group->position().y);
547 
548 		/* now reduce it again so that we start autoscrolling before we
549 		 * move off the top or bottom of the canvas
550 		 */
551 
552 		alloc.set_height (alloc.get_height() - 20);
553 		alloc.set_y (alloc.get_y() + 10);
554 
555 		/* the effective width of the autoscroll boundary so
556 		   that we start scrolling before we hit the edge.
557 
558 		   this helps when the window is slammed up against the
559 		   right edge of the screen, making it hard to scroll
560 		   effectively.
561 		*/
562 
563 		if (alloc.get_width() > 20) {
564 			alloc.set_width (alloc.get_width() - 20);
565 			alloc.set_x (alloc.get_x() + 10);
566 		}
567 
568 		int wx, wy;
569 
570 		_track_canvas_viewport->get_parent()->translate_coordinates (*toplevel,
571 		                                                             alloc.get_x(), alloc.get_y(),
572 			                                                     wx, wy);
573 
574 		scrolling_boundary = ArdourCanvas::Rect (wx, wy, wx + alloc.get_width(), wy + alloc.get_height());
575 	}
576 
577 	int x, y;
578 	Gdk::ModifierType mask;
579 
580 	toplevel->get_window()->get_pointer (x, y, mask);
581 
582 	if ((allow_horiz && ((x < scrolling_boundary.x0 && _leftmost_sample > 0) || x >= scrolling_boundary.x1)) ||
583 	    (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) {
584 		start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary);
585 	}
586 }
587 
588 bool
drag_active() const589 Editor::drag_active () const
590 {
591 	return _drags->active();
592 }
593 
594 bool
preview_video_drag_active() const595 Editor::preview_video_drag_active () const
596 {
597 	return _drags->preview_video ();
598 }
599 
600 bool
autoscroll_active() const601 Editor::autoscroll_active () const
602 {
603 	return autoscroll_connection.connected ();
604 }
605 
606 std::pair <samplepos_t,samplepos_t>
session_gui_extents(bool use_extra) const607 Editor::session_gui_extents (bool use_extra) const
608 {
609 	if (!_session) {
610 		return std::pair <samplepos_t,samplepos_t>(max_samplepos,0);
611 	}
612 
613 	samplecnt_t session_extent_start = _session->current_start_sample();
614 	samplecnt_t session_extent_end = _session->current_end_sample();
615 
616 	/* calculate the extents of all regions in every playlist
617 	 * NOTE: we should listen to playlists, and cache these values so we don't calculate them every time.
618 	 */
619 	{
620 		boost::shared_ptr<RouteList> rl = _session->get_routes();
621 		for (RouteList::iterator r = rl->begin(); r != rl->end(); ++r) {
622 			boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*r);
623 			if (!tr) {
624 				continue;
625 			}
626 			if (tr->presentation_info ().hidden ()) {
627 				continue;
628 			}
629 			pair<samplepos_t, samplepos_t> e = tr->playlist()->get_extent ();
630 			if (e.first == e.second) {
631 				/* no regions present */
632 				continue;
633 			}
634 			session_extent_start = std::min (session_extent_start, e.first);
635 			session_extent_end   = std::max (session_extent_end, e.second);
636 		}
637 	}
638 
639 	/* ToDo: also incorporate automation regions (in case the session has no audio/midi but is just used for automating plugins or the like) */
640 
641 	/* add additional time to the ui extents (user-defined in config) */
642 	if (use_extra) {
643 		samplecnt_t const extra = UIConfiguration::instance().get_extra_ui_extents_time() * 60 * _session->nominal_sample_rate();
644 		session_extent_end += extra;
645 		session_extent_start -= extra;
646 	}
647 
648 	/* range-check */
649 	if (session_extent_end > max_samplepos) {
650 		session_extent_end = max_samplepos;
651 	}
652 	if (session_extent_start < 0) {
653 		session_extent_start = 0;
654 	}
655 
656 	std::pair <samplepos_t,samplepos_t> ret (session_extent_start, session_extent_end);
657 	return ret;
658 }
659 
660 bool
autoscroll_canvas()661 Editor::autoscroll_canvas ()
662 {
663 	int x, y;
664 	Gdk::ModifierType mask;
665 	sampleoffset_t dx = 0;
666 	bool no_stop = false;
667 	Gtk::Window* toplevel = dynamic_cast<Gtk::Window*>(contents().get_toplevel());
668 
669 	if (!toplevel) {
670 		return false;
671 	}
672 
673 	toplevel->get_window()->get_pointer (x, y, mask);
674 
675 	VisualChange vc;
676 	bool vertical_motion = false;
677 
678 	if (autoscroll_horizontal_allowed) {
679 
680 		samplepos_t new_sample = _leftmost_sample;
681 
682 		/* horizontal */
683 
684 		if (x > autoscroll_boundary.x1) {
685 
686 			/* bring it back into view */
687 			dx = x - autoscroll_boundary.x1;
688 			dx += 10 + (2 * (autoscroll_cnt/2));
689 
690 			dx = pixel_to_sample (dx);
691 
692 			dx *= UIConfiguration::instance().get_draggable_playhead_speed();
693 
694 			if (_leftmost_sample < max_samplepos - dx) {
695 				new_sample = _leftmost_sample + dx;
696 			} else {
697 				new_sample = max_samplepos;
698 			}
699 
700 			no_stop = true;
701 
702 		} else if (x < autoscroll_boundary.x0) {
703 
704 			dx = autoscroll_boundary.x0 - x;
705 			dx += 10 + (2 * (autoscroll_cnt/2));
706 
707 			dx = pixel_to_sample (dx);
708 
709 			dx *= UIConfiguration::instance().get_draggable_playhead_speed();
710 
711 			if (_leftmost_sample >= dx) {
712 				new_sample = _leftmost_sample - dx;
713 			} else {
714 				new_sample = 0;
715 			}
716 
717 			no_stop = true;
718 		}
719 
720 		if (new_sample != _leftmost_sample) {
721 			vc.time_origin = new_sample;
722 			vc.add (VisualChange::TimeOrigin);
723 		}
724 	}
725 
726 	if (autoscroll_vertical_allowed) {
727 
728 		// const double vertical_pos = vertical_adjustment.get_value();
729 		const int speed_factor = 10;
730 
731 		/* vertical */
732 
733 		if (y < autoscroll_boundary.y0) {
734 
735 			/* scroll to make higher tracks visible */
736 
737 			if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
738 				scroll_up_one_track ();
739 				vertical_motion = true;
740 			}
741 			no_stop = true;
742 
743 		} else if (y > autoscroll_boundary.y1) {
744 
745 			if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
746 				scroll_down_one_track ();
747 				vertical_motion = true;
748 			}
749 			no_stop = true;
750 		}
751 
752 	}
753 
754 	if (vc.pending || vertical_motion) {
755 
756 		/* change horizontal first */
757 
758 		if (vc.pending) {
759 			visual_changer (vc);
760 		}
761 
762 		/* now send a motion event to notify anyone who cares
763 		   that we have moved to a new location (because we scrolled)
764 		*/
765 
766 		GdkEventMotion ev;
767 
768 		ev.type = GDK_MOTION_NOTIFY;
769 		ev.state = Gdk::BUTTON1_MASK;
770 
771 		/* the motion handler expects events in canvas coordinate space */
772 
773 		/* we asked for the mouse position above (::get_pointer()) via
774 		 * our own top level window (we being the Editor). Convert into
775 		 * coordinates within the canvas window.
776 		 */
777 
778 		int cx;
779 		int cy;
780 
781 		toplevel->translate_coordinates (*_track_canvas, x, y, cx, cy);
782 
783 		/* clamp x and y to remain within the autoscroll boundary,
784 		 * which is defined in window coordinates
785 		 */
786 
787 		x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1);
788 		y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1);
789 
790 		/* now convert from Editor window coordinates to canvas
791 		 * window coordinates
792 		 */
793 
794 		ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
795 		ev.x = d.x;
796 		ev.y = d.y;
797 		ev.state = mask;
798 
799 		motion_handler (0, (GdkEvent*) &ev, true);
800 
801 	} else if (no_stop) {
802 
803 		/* not changing visual state but pointer is outside the scrolling boundary
804 		 * so we still need to deliver a fake motion event
805 		 */
806 
807 		GdkEventMotion ev;
808 
809 		ev.type = GDK_MOTION_NOTIFY;
810 		ev.state = Gdk::BUTTON1_MASK;
811 
812 		/* the motion handler expects events in canvas coordinate space */
813 
814 		/* first convert from Editor window coordinates to canvas
815 		 * window coordinates
816 		 */
817 
818 		int cx;
819 		int cy;
820 
821 		/* clamp x and y to remain within the visible area. except
822 		 * .. if horizontal scrolling is allowed, always allow us to
823 		 * move back to zero
824 		 */
825 
826 		if (autoscroll_horizontal_allowed) {
827 			x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1);
828 		} else {
829 			x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
830 		}
831 		y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
832 
833 		toplevel->translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
834 
835 		ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
836 		ev.x = d.x;
837 		ev.y = d.y;
838 		ev.state = mask;
839 
840 		motion_handler (0, (GdkEvent*) &ev, true);
841 
842 	} else {
843 		stop_canvas_autoscroll ();
844 		return false;
845 	}
846 
847 	autoscroll_cnt++;
848 
849 	return true; /* call me again */
850 }
851 
852 void
start_canvas_autoscroll(bool allow_horiz,bool allow_vert,const ArdourCanvas::Rect & boundary)853 Editor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary)
854 {
855 	if (!_session) {
856 		return;
857 	}
858 
859 	stop_canvas_autoscroll ();
860 
861 	autoscroll_horizontal_allowed = allow_horiz;
862 	autoscroll_vertical_allowed = allow_vert;
863 	autoscroll_boundary = boundary;
864 
865 	/* do the first scroll right now
866 	*/
867 
868 	autoscroll_canvas ();
869 
870 	/* scroll again at very very roughly 30FPS */
871 
872 	autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::autoscroll_canvas), 30);
873 }
874 
875 void
stop_canvas_autoscroll()876 Editor::stop_canvas_autoscroll ()
877 {
878 	autoscroll_connection.disconnect ();
879 	autoscroll_cnt = 0;
880 }
881 
882 Editor::EnterContext*
get_enter_context(ItemType type)883 Editor::get_enter_context(ItemType type)
884 {
885 	for (ssize_t i = _enter_stack.size() - 1; i >= 0; --i) {
886 		if (_enter_stack[i].item_type == type) {
887 			return &_enter_stack[i];
888 		}
889 	}
890 	return NULL;
891 }
892 
893 bool
left_track_canvas(GdkEventCrossing * ev)894 Editor::left_track_canvas (GdkEventCrossing* ev)
895 {
896 	const bool was_within = within_track_canvas;
897 	DropDownKeys ();
898 	within_track_canvas = false;
899 	set_entered_track (0);
900 	set_entered_regionview (0);
901 	reset_canvas_action_sensitivity (false);
902 
903 	if (was_within) {
904 		if (ev->detail == GDK_NOTIFY_NONLINEAR ||
905 		    ev->detail == GDK_NOTIFY_NONLINEAR_VIRTUAL) {
906 			/* context menu or something similar */
907 			sensitize_the_right_region_actions (false);
908 		} else {
909 			sensitize_the_right_region_actions (true);
910 		}
911 	}
912 
913 	return false;
914 }
915 
916 bool
entered_track_canvas(GdkEventCrossing * ev)917 Editor::entered_track_canvas (GdkEventCrossing* ev)
918 {
919 	const bool was_within = within_track_canvas;
920 	within_track_canvas = true;
921 	reset_canvas_action_sensitivity (true);
922 
923 	if (!was_within) {
924 
925 		if (internal_editing()) {
926 			/* ensure that key events go here because there are
927 			   internal editing bindings associated only with the
928 			   canvas. if the focus is elsewhere, we cannot find them.
929 			*/
930 			_track_canvas->grab_focus ();
931 		}
932 
933 		if (ev->detail == GDK_NOTIFY_NONLINEAR ||
934 		    ev->detail == GDK_NOTIFY_NONLINEAR_VIRTUAL) {
935 			/* context menu or something similar */
936 			sensitize_the_right_region_actions (false);
937 		} else {
938 			sensitize_the_right_region_actions (true);
939 		}
940 	}
941 
942 	return false;
943 }
944 
945 void
ensure_time_axis_view_is_visible(TimeAxisView const & track,bool at_top)946 Editor::ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top)
947 {
948 	if (track.hidden()) {
949 		return;
950 	}
951 
952 	/* compute visible area of trackview group, as offsets from top of
953 	 * trackview group.
954 	 */
955 
956 	double const current_view_min_y = vertical_adjustment.get_value();
957 	double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size();
958 
959 	double const track_min_y = track.y_position ();
960 	double const track_max_y = track.y_position () + track.effective_height ();
961 
962 	if (!at_top &&
963 	    (track_min_y >= current_view_min_y &&
964 	     track_max_y < current_view_max_y)) {
965 		/* already visible, and caller did not ask to place it at the
966 		 * top of the track canvas
967 		 */
968 		return;
969 	}
970 
971 	double new_value;
972 
973 	if (at_top) {
974 		new_value = track_min_y;
975 	} else {
976 		if (track_min_y < current_view_min_y) {
977 			// Track is above the current view
978 			new_value = track_min_y;
979 		} else if (track_max_y > current_view_max_y) {
980 			// Track is below the current view
981 			new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size();
982 		} else {
983 			new_value = track_min_y;
984 		}
985 	}
986 
987 	vertical_adjustment.set_value(new_value);
988 }
989 
990 /** Called when the main vertical_adjustment has changed */
991 void
tie_vertical_scrolling()992 Editor::tie_vertical_scrolling ()
993 {
994 	if (pending_visual_change.idle_handler_id < 0) {
995 		_region_peak_cursor->hide ();
996 		_summary->set_overlays_dirty ();
997 	}
998 }
999 
1000 void
set_horizontal_position(double p)1001 Editor::set_horizontal_position (double p)
1002 {
1003 	horizontal_adjustment.set_value (p);
1004 
1005 	_leftmost_sample = (samplepos_t) floor (p * samples_per_pixel);
1006 }
1007 
1008 void
color_handler()1009 Editor::color_handler()
1010 {
1011 	Gtkmm2ext::Color base = UIConfiguration::instance().color ("ruler base");
1012 	Gtkmm2ext::Color text = UIConfiguration::instance().color ("ruler text");
1013 	timecode_ruler->set_fill_color (base);
1014 	timecode_ruler->set_outline_color (text);
1015 	minsec_ruler->set_fill_color (base);
1016 	minsec_ruler->set_outline_color (text);
1017 	samples_ruler->set_fill_color (base);
1018 	samples_ruler->set_outline_color (text);
1019 	bbt_ruler->set_fill_color (base);
1020 	bbt_ruler->set_outline_color (text);
1021 
1022 	_playhead_cursor->set_color (UIConfiguration::instance().color ("play head"));
1023 
1024 	meter_bar->set_fill_color (UIConfiguration::instance().color_mod ("meter bar", "marker bar"));
1025 	meter_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
1026 
1027 	tempo_bar->set_fill_color (UIConfiguration::instance().color_mod ("tempo bar", "marker bar"));
1028 	tempo_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
1029 
1030 	marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("marker bar", "marker bar"));
1031 	marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
1032 
1033 	cd_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("cd marker bar", "marker bar"));
1034 	cd_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
1035 
1036 	range_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("range marker bar", "marker bar"));
1037 	range_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
1038 
1039 	transport_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("transport marker bar", "marker bar"));
1040 	transport_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
1041 
1042 	cd_marker_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect"));
1043 	cd_marker_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect"));
1044 
1045 	range_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect"));
1046 	range_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect"));
1047 
1048 	transport_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("transport drag rect"));
1049 	transport_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("transport drag rect"));
1050 
1051 	transport_loop_range_rect->set_fill_color (UIConfiguration::instance().color_mod ("transport loop rect", "loop rectangle"));
1052 	transport_loop_range_rect->set_outline_color (UIConfiguration::instance().color ("transport loop rect"));
1053 
1054 	transport_punch_range_rect->set_fill_color (UIConfiguration::instance().color ("transport punch rect"));
1055 	transport_punch_range_rect->set_outline_color (UIConfiguration::instance().color ("transport punch rect"));
1056 
1057 	transport_punchin_line->set_outline_color (UIConfiguration::instance().color ("punch line"));
1058 	transport_punchout_line->set_outline_color (UIConfiguration::instance().color ("punch line"));
1059 
1060 	rubberband_rect->set_outline_color (UIConfiguration::instance().color ("rubber band rect"));
1061 	rubberband_rect->set_fill_color (UIConfiguration::instance().color_mod ("rubber band rect", "selection rect"));
1062 
1063 	location_marker_color = UIConfiguration::instance().color ("location marker");
1064 	location_range_color = UIConfiguration::instance().color ("location range");
1065 	location_cd_marker_color = UIConfiguration::instance().color ("location cd marker");
1066 	location_loop_color = UIConfiguration::instance().color ("location loop");
1067 	location_punch_color = UIConfiguration::instance().color ("location punch");
1068 
1069 	refresh_location_display ();
1070 
1071 	NoteBase::set_colors ();
1072 
1073 	/* redraw the whole thing */
1074 	_track_canvas->set_background_color (UIConfiguration::instance().color ("arrange base"));
1075 	_track_canvas->queue_draw ();
1076 
1077 /*
1078 	redisplay_grid (true);
1079 
1080 	if (_session)
1081 	      _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
1082 */
1083 }
1084 
1085 double
horizontal_position() const1086 Editor::horizontal_position () const
1087 {
1088 	return sample_to_pixel (_leftmost_sample);
1089 }
1090 
1091 bool
track_canvas_key_press(GdkEventKey *)1092 Editor::track_canvas_key_press (GdkEventKey*)
1093 {
1094 	return false;
1095 }
1096 
1097 bool
track_canvas_key_release(GdkEventKey *)1098 Editor::track_canvas_key_release (GdkEventKey*)
1099 {
1100 	return false;
1101 }
1102 
1103 double
clamp_verbose_cursor_x(double x)1104 Editor::clamp_verbose_cursor_x (double x)
1105 {
1106 	if (x < 0) {
1107 		x = 0;
1108 	} else {
1109 		x = min (_visible_canvas_width - 200.0, x);
1110 	}
1111 	return x;
1112 }
1113 
1114 double
clamp_verbose_cursor_y(double y)1115 Editor::clamp_verbose_cursor_y (double y)
1116 {
1117 	y = max (0.0, y);
1118 	y = min (_visible_canvas_height - 50, y);
1119 	return y;
1120 }
1121 
1122 ArdourCanvas::GtkCanvasViewport*
get_track_canvas() const1123 Editor::get_track_canvas() const
1124 {
1125 	return _track_canvas_viewport;
1126 }
1127 
1128 Gdk::Cursor*
get_canvas_cursor() const1129 Editor::get_canvas_cursor () const
1130 {
1131 	/* The top of the cursor stack is always the currently visible cursor. */
1132 	return _cursor_stack.back();
1133 }
1134 
1135 void
set_canvas_cursor(Gdk::Cursor * cursor)1136 Editor::set_canvas_cursor (Gdk::Cursor* cursor)
1137 {
1138 	Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
1139 
1140 	if (win && !_cursors->is_invalid (cursor)) {
1141 		/* glibmm 2.4 doesn't allow null cursor pointer because it uses
1142 		   a Gdk::Cursor& as the argument to Gdk::Window::set_cursor().
1143 		   But a null pointer just means "use parent window cursor",
1144 		   and so should be allowed. Gtkmm 3.x has fixed this API.
1145 
1146 		   For now, drop down and use C API
1147 		*/
1148 		gdk_window_set_cursor (win->gobj(), cursor ? cursor->gobj() : 0);
1149 	}
1150 }
1151 
1152 size_t
push_canvas_cursor(Gdk::Cursor * cursor)1153 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
1154 {
1155 	if (!_cursors->is_invalid (cursor)) {
1156 		_cursor_stack.push_back (cursor);
1157 		set_canvas_cursor (cursor);
1158 	}
1159 	return _cursor_stack.size() - 1;
1160 }
1161 
1162 void
pop_canvas_cursor()1163 Editor::pop_canvas_cursor ()
1164 {
1165 	while (true) {
1166 		if (_cursor_stack.size() <= 1) {
1167 			PBD::error << "attempt to pop default cursor" << endmsg;
1168 			return;
1169 		}
1170 
1171 		_cursor_stack.pop_back();
1172 		if (_cursor_stack.back()) {
1173 			/* Popped to an existing cursor, we're done.  Otherwise, the
1174 			   context that created this cursor has been destroyed, so we need
1175 			   to skip to the next down the stack. */
1176 			set_canvas_cursor (_cursor_stack.back());
1177 			return;
1178 		}
1179 	}
1180 }
1181 
1182 Gdk::Cursor*
which_trim_cursor(bool left) const1183 Editor::which_trim_cursor (bool left) const
1184 {
1185 	if (!entered_regionview) {
1186 		return 0;
1187 	}
1188 
1189 	Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
1190 
1191 	if (left) {
1192 		if (ct & Trimmable::FrontTrimEarlier) {
1193 			return _cursors->left_side_trim;
1194 		} else {
1195 			return _cursors->left_side_trim_right_only;
1196 		}
1197 	} else {
1198 		if (ct & Trimmable::EndTrimLater) {
1199 			return _cursors->right_side_trim;
1200 		} else {
1201 			return _cursors->right_side_trim_left_only;
1202 		}
1203 	}
1204 }
1205 
1206 Gdk::Cursor*
which_mode_cursor() const1207 Editor::which_mode_cursor () const
1208 {
1209 	Gdk::Cursor* mode_cursor = MouseCursors::invalid_cursor ();
1210 
1211 	switch (mouse_mode) {
1212 	case MouseRange:
1213 		mode_cursor = _cursors->selector;
1214 		break;
1215 
1216 	case MouseCut:
1217 		mode_cursor = _cursors->scissors;
1218 		break;
1219 
1220 	case MouseObject:
1221 	case MouseContent:
1222 		/* don't use mode cursor, pick a grabber cursor based on the item */
1223 		break;
1224 
1225 	case MouseDraw:
1226 		mode_cursor = _cursors->midi_pencil;
1227 		break;
1228 
1229 	case MouseTimeFX:
1230 		mode_cursor = _cursors->time_fx; // just use playhead
1231 		break;
1232 
1233 	case MouseAudition:
1234 		mode_cursor = _cursors->speaker;
1235 		break;
1236 	}
1237 
1238 	/* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
1239 	if (get_smart_mode()) {
1240 
1241 		double x, y;
1242 		get_pointer_position (x, y);
1243 
1244 		if (x >= 0 && y >= 0) {
1245 
1246 			vector<ArdourCanvas::Item const *> items;
1247 
1248 			/* Note how we choose a specific scroll group to get
1249 			 * items from. This could be problematic.
1250 			 */
1251 
1252 			hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
1253 
1254 			// first item will be the upper most
1255 
1256 			if (!items.empty()) {
1257 				const ArdourCanvas::Item* i = items.front();
1258 
1259 				if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
1260 					pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
1261 					if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
1262 						mode_cursor = _cursors->up_down;
1263 					}
1264 				}
1265 			}
1266 		}
1267 	}
1268 
1269 	return mode_cursor;
1270 }
1271 
1272 Gdk::Cursor*
which_track_cursor() const1273 Editor::which_track_cursor () const
1274 {
1275 	Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
1276 
1277 	switch (_join_object_range_state) {
1278 	case JOIN_OBJECT_RANGE_NONE:
1279 	case JOIN_OBJECT_RANGE_OBJECT:
1280 		cursor = _cursors->grabber;
1281 		break;
1282 	case JOIN_OBJECT_RANGE_RANGE:
1283 		cursor = _cursors->selector;
1284 		break;
1285 	}
1286 
1287 	return cursor;
1288 }
1289 
1290 Gdk::Cursor*
which_canvas_cursor(ItemType type) const1291 Editor::which_canvas_cursor(ItemType type) const
1292 {
1293 	Gdk::Cursor* cursor = which_mode_cursor ();
1294 
1295 	if (mouse_mode == MouseRange) {
1296 		switch (type) {
1297 		case StartSelectionTrimItem:
1298 			cursor = _cursors->left_side_trim;
1299 			break;
1300 		case EndSelectionTrimItem:
1301 			cursor = _cursors->right_side_trim;
1302 			break;
1303 		default:
1304 			break;
1305 		}
1306 	}
1307 
1308 	if ((mouse_mode == MouseObject || get_smart_mode ()) ||
1309 	    mouse_mode == MouseContent) {
1310 
1311 		/* find correct cursor to use in object/smart mode */
1312 		switch (type) {
1313 		case RegionItem:
1314 		/* We don't choose a cursor for these items on top of a region view,
1315 		   because this would push a new context on the enter stack which
1316 		   means switching the region context for things like smart mode
1317 		   won't actualy change the cursor. */
1318 		// case WaveItem:
1319 		case StreamItem:
1320 		case AutomationTrackItem:
1321 			cursor = which_track_cursor ();
1322 			break;
1323 		case PlayheadCursorItem:
1324 			cursor = _cursors->grabber;
1325 			break;
1326 		case SelectionItem:
1327 			cursor = _cursors->selector;
1328 			break;
1329 		case ControlPointItem:
1330 			cursor = _cursors->fader;
1331 			break;
1332 		case GainLineItem:
1333 			cursor = _cursors->cross_hair;
1334 			break;
1335 		case AutomationLineItem:
1336 			cursor = _cursors->cross_hair;
1337 			break;
1338 		case StartSelectionTrimItem:
1339 			cursor = _cursors->left_side_trim;
1340 			break;
1341 		case EndSelectionTrimItem:
1342 			cursor = _cursors->right_side_trim;
1343 			break;
1344 		case FadeInItem:
1345 			cursor = _cursors->fade_in;
1346 			break;
1347 		case FadeInHandleItem:
1348 			cursor = _cursors->fade_in;
1349 			break;
1350 		case FadeInTrimHandleItem:
1351 			cursor = _cursors->fade_in;
1352 			break;
1353 		case FadeOutItem:
1354 			cursor = _cursors->fade_out;
1355 			break;
1356 		case FadeOutHandleItem:
1357 			cursor = _cursors->fade_out;
1358 			break;
1359 		case FadeOutTrimHandleItem:
1360 			cursor = _cursors->fade_out;
1361 			break;
1362 		case FeatureLineItem:
1363 			cursor = _cursors->cross_hair;
1364 			break;
1365 		case LeftFrameHandle:
1366 			if (effective_mouse_mode() == MouseObject) // (smart mode): if the user is in the btm half, show the trim cursor
1367 				cursor = which_trim_cursor (true);
1368 			else
1369 				cursor = _cursors->selector; // (smart mode): in the top half, just show the selection (range) cursor
1370 			break;
1371 		case RightFrameHandle:
1372 			if (effective_mouse_mode() == MouseObject) // see above
1373 				cursor = which_trim_cursor (false);
1374 			else
1375 				cursor = _cursors->selector;
1376 			break;
1377 		case RegionViewName:
1378 		case RegionViewNameHighlight:
1379 			/* the trim bar is used for trimming, but we have to determine if we are on the left or right side of the region */
1380 			cursor = MouseCursors::invalid_cursor ();
1381 			if (entered_regionview) {
1382 				samplepos_t where;
1383 				bool in_track_canvas;
1384 				if (mouse_sample (where, in_track_canvas)) {
1385 					samplepos_t start = entered_regionview->region()->first_sample();
1386 					samplepos_t end = entered_regionview->region()->last_sample();
1387 					cursor = which_trim_cursor ((where - start) < (end - where));
1388 				}
1389 			}
1390 			break;
1391 		case StartCrossFadeItem:
1392 			cursor = _cursors->fade_in;
1393 			break;
1394 		case EndCrossFadeItem:
1395 			cursor = _cursors->fade_out;
1396 			break;
1397 		case CrossfadeViewItem:
1398 			cursor = _cursors->cross_hair;
1399 			break;
1400 		case NoteItem:
1401 			cursor = _cursors->grabber_note;
1402 		default:
1403 			break;
1404 		}
1405 
1406 	} else if (mouse_mode == MouseDraw) {
1407 
1408 		/* ControlPointItem is not really specific to region gain mode
1409 		   but it is the same cursor so don't worry about this for now.
1410 		   The result is that we'll see the fader cursor if we enter
1411 		   non-region-gain-line control points while in MouseDraw
1412 		   mode, even though we can't edit them in this mode.
1413 		*/
1414 
1415 		switch (type) {
1416 		case GainLineItem:
1417 		case ControlPointItem:
1418 			cursor = _cursors->fader;
1419 			break;
1420 		case NoteItem:
1421 			cursor = _cursors->grabber_note;
1422 		default:
1423 			break;
1424 		}
1425 	}
1426 
1427 	switch (type) {
1428 		/* These items use the timebar cursor at all times */
1429 	case TimecodeRulerItem:
1430 	case MinsecRulerItem:
1431 	case BBTRulerItem:
1432 	case SamplesRulerItem:
1433 		cursor = _cursors->timebar;
1434 		break;
1435 
1436 		/* These items use the grabber cursor at all times */
1437 	case MeterMarkerItem:
1438 	case TempoMarkerItem:
1439 	case MeterBarItem:
1440 	case TempoBarItem:
1441 	case MarkerItem:
1442 	case MarkerBarItem:
1443 	case RangeMarkerBarItem:
1444 	case CdMarkerBarItem:
1445 	case VideoBarItem:
1446 	case TransportMarkerBarItem:
1447 	case DropZoneItem:
1448 		cursor = _cursors->grabber;
1449 		break;
1450 
1451 	default:
1452 		break;
1453 	}
1454 
1455 	return cursor;
1456 }
1457 
1458 void
choose_canvas_cursor_on_entry(ItemType type)1459 Editor::choose_canvas_cursor_on_entry (ItemType type)
1460 {
1461 	if (_drags->active()) {
1462 		return;
1463 	}
1464 
1465 	Gdk::Cursor* cursor = which_canvas_cursor(type);
1466 
1467 	if (!_cursors->is_invalid (cursor)) {
1468 		// Push a new enter context
1469 		const EnterContext ctx = { type, CursorContext::create(*this, cursor) };
1470 		_enter_stack.push_back(ctx);
1471 	}
1472 }
1473 
1474 void
update_all_enter_cursors()1475 Editor::update_all_enter_cursors ()
1476 {
1477 	for (std::vector<EnterContext>::iterator i = _enter_stack.begin(); i != _enter_stack.end(); ++i) {
1478 		i->cursor_ctx->change(which_canvas_cursor(i->item_type));
1479 	}
1480 }
1481 
1482 double
trackviews_height() const1483 Editor::trackviews_height() const
1484 {
1485 	if (!_trackview_group) {
1486 		return 0;
1487 	}
1488 
1489 	return _visible_canvas_height - _trackview_group->canvas_origin().y;
1490 }
1491