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