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