new_grid: Rewrite of Snap and Grid. (squashed commit)
[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                 boost::shared_ptr<RouteList> rl = _session->get_routes();
600                 for (RouteList::iterator r = rl->begin(); r != rl->end(); ++r) {
601                         boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*r);
602                         if (tr) {
603                                 boost::shared_ptr<Playlist> pl = tr->playlist();
604                                 if ( pl && !pl->all_regions_empty() ) {
605                                         pair<samplepos_t, samplepos_t> e;
606                                         e = pl->get_extent();
607                                         if (e.first < session_extent_start) {
608                                                 session_extent_start = e.first;
609                                         }
610                                         if (e.second > session_extent_end) {
611                                                 session_extent_end = e.second;
612                                         }
613                                 }
614                         }
615                 }
616         }
617
618         //ToDo: also incorporate automation regions (in case the session has no audio/midi but is just used for automating plugins or the like)
619
620         //add additional time to the ui extents ( user-defined in config )
621         if (use_extra) {
622                 samplecnt_t const extra = UIConfiguration::instance().get_extra_ui_extents_time() * 60 * _session->nominal_sample_rate();
623                 session_extent_end += extra;
624                 session_extent_start -= extra;
625         }
626                         
627         //range-check
628         if (session_extent_end > max_samplepos) {
629                 session_extent_end = max_samplepos;
630         }
631         if (session_extent_start < 0) {
632                 session_extent_start = 0;
633         }
634         
635         std::pair <samplepos_t,samplepos_t> ret (session_extent_start, session_extent_end);
636         return ret;
637 }
638
639 bool
640 Editor::autoscroll_canvas ()
641 {
642         int x, y;
643         Gdk::ModifierType mask;
644         sampleoffset_t dx = 0;
645         bool no_stop = false;
646         Gtk::Window* toplevel = dynamic_cast<Gtk::Window*>(contents().get_toplevel());
647
648         if (!toplevel) {
649                 return false;
650         }
651
652         toplevel->get_window()->get_pointer (x, y, mask);
653
654         VisualChange vc;
655         bool vertical_motion = false;
656
657         if (autoscroll_horizontal_allowed) {
658
659                 samplepos_t new_sample = _leftmost_sample;
660
661                 /* horizontal */
662
663                 if (x > autoscroll_boundary.x1) {
664
665                         /* bring it back into view */
666                         dx = x - autoscroll_boundary.x1;
667                         dx += 10 + (2 * (autoscroll_cnt/2));
668
669                         dx = pixel_to_sample (dx);
670                         
671                         dx *= UIConfiguration::instance().get_draggable_playhead_speed();
672
673                         if (_leftmost_sample < max_samplepos - dx) {
674                                 new_sample = _leftmost_sample + dx;
675                         } else {
676                                 new_sample = max_samplepos;
677                         }
678
679                         no_stop = true;
680
681                 } else if (x < autoscroll_boundary.x0) {
682
683                         dx = autoscroll_boundary.x0 - x;
684                         dx += 10 + (2 * (autoscroll_cnt/2));
685
686                         dx = pixel_to_sample (dx);
687
688                         dx *= UIConfiguration::instance().get_draggable_playhead_speed();
689
690                         if (_leftmost_sample >= dx) {
691                                 new_sample = _leftmost_sample - dx;
692                         } else {
693                                 new_sample = 0;
694                         }
695
696                         no_stop = true;
697                 }
698
699                 if (new_sample != _leftmost_sample) {
700                         vc.time_origin = new_sample;
701                         vc.add (VisualChange::TimeOrigin);
702                 }
703         }
704
705         if (autoscroll_vertical_allowed) {
706
707                 // const double vertical_pos = vertical_adjustment.get_value();
708                 const int speed_factor = 10;
709
710                 /* vertical */
711
712                 if (y < autoscroll_boundary.y0) {
713
714                         /* scroll to make higher tracks visible */
715
716                         if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
717                                 scroll_up_one_track ();
718                                 vertical_motion = true;
719                         }
720                         no_stop = true;
721
722                 } else if (y > autoscroll_boundary.y1) {
723
724                         if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
725                                 scroll_down_one_track ();
726                                 vertical_motion = true;
727                         }
728                         no_stop = true;
729                 }
730
731         }
732
733         if (vc.pending || vertical_motion) {
734
735                 /* change horizontal first */
736
737                 if (vc.pending) {
738                         visual_changer (vc);
739                 }
740
741                 /* now send a motion event to notify anyone who cares
742                    that we have moved to a new location (because we scrolled)
743                 */
744
745                 GdkEventMotion ev;
746
747                 ev.type = GDK_MOTION_NOTIFY;
748                 ev.state = Gdk::BUTTON1_MASK;
749
750                 /* the motion handler expects events in canvas coordinate space */
751
752                 /* we asked for the mouse position above (::get_pointer()) via
753                  * our own top level window (we being the Editor). Convert into
754                  * coordinates within the canvas window.
755                  */
756
757                 int cx;
758                 int cy;
759
760                 toplevel->translate_coordinates (*_track_canvas, x, y, cx, cy);
761
762                 /* clamp x and y to remain within the autoscroll boundary,
763                  * which is defined in window coordinates
764                  */
765
766                 x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1);
767                 y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1);
768
769                 /* now convert from Editor window coordinates to canvas
770                  * window coordinates
771                  */
772
773                 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
774                 ev.x = d.x;
775                 ev.y = d.y;
776                 ev.state = mask;
777
778                 motion_handler (0, (GdkEvent*) &ev, true);
779
780         } else if (no_stop) {
781
782                 /* not changing visual state but pointer is outside the scrolling boundary
783                  * so we still need to deliver a fake motion event
784                  */
785
786                 GdkEventMotion ev;
787
788                 ev.type = GDK_MOTION_NOTIFY;
789                 ev.state = Gdk::BUTTON1_MASK;
790
791                 /* the motion handler expects events in canvas coordinate space */
792
793                 /* first convert from Editor window coordinates to canvas
794                  * window coordinates
795                  */
796
797                 int cx;
798                 int cy;
799
800                 /* clamp x and y to remain within the visible area. except
801                  * .. if horizontal scrolling is allowed, always allow us to
802                  * move back to zero
803                  */
804
805                 if (autoscroll_horizontal_allowed) {
806                         x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1);
807                 } else {
808                         x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
809                 }
810                 y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
811
812                 toplevel->translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
813
814                 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
815                 ev.x = d.x;
816                 ev.y = d.y;
817                 ev.state = mask;
818
819                 motion_handler (0, (GdkEvent*) &ev, true);
820
821         } else {
822                 stop_canvas_autoscroll ();
823                 return false;
824         }
825
826         autoscroll_cnt++;
827
828         return true; /* call me again */
829 }
830
831 void
832 Editor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary)
833 {
834         if (!_session) {
835                 return;
836         }
837
838         stop_canvas_autoscroll ();
839
840         autoscroll_horizontal_allowed = allow_horiz;
841         autoscroll_vertical_allowed = allow_vert;
842         autoscroll_boundary = boundary;
843
844         /* do the first scroll right now
845         */
846
847         autoscroll_canvas ();
848
849         /* scroll again at very very roughly 30FPS */
850
851         autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::autoscroll_canvas), 30);
852 }
853
854 void
855 Editor::stop_canvas_autoscroll ()
856 {
857         autoscroll_connection.disconnect ();
858         autoscroll_cnt = 0;
859 }
860
861 Editor::EnterContext*
862 Editor::get_enter_context(ItemType type)
863 {
864         for (ssize_t i = _enter_stack.size() - 1; i >= 0; --i) {
865                 if (_enter_stack[i].item_type == type) {
866                         return &_enter_stack[i];
867                 }
868         }
869         return NULL;
870 }
871
872 bool
873 Editor::left_track_canvas (GdkEventCrossing* ev)
874 {
875         const bool was_within = within_track_canvas;
876         DropDownKeys ();
877         within_track_canvas = false;
878         set_entered_track (0);
879         set_entered_regionview (0);
880         reset_canvas_action_sensitivity (false);
881
882         if (was_within) {
883                 if (ev->detail == GDK_NOTIFY_NONLINEAR ||
884                     ev->detail == GDK_NOTIFY_NONLINEAR_VIRTUAL) {
885                         /* context menu or something similar */
886                         sensitize_the_right_region_actions (false);
887                 } else {
888                         sensitize_the_right_region_actions (true);
889                 }
890         }
891
892         return false;
893 }
894
895 bool
896 Editor::entered_track_canvas (GdkEventCrossing* ev)
897 {
898         const bool was_within = within_track_canvas;
899         within_track_canvas = true;
900         reset_canvas_action_sensitivity (true);
901
902         if (!was_within) {
903                 if (ev->detail == GDK_NOTIFY_NONLINEAR ||
904                     ev->detail == GDK_NOTIFY_NONLINEAR_VIRTUAL) {
905                         /* context menu or something similar */
906                         sensitize_the_right_region_actions (false);
907                 } else {
908                         sensitize_the_right_region_actions (true);
909                 }
910         }
911
912         return false;
913 }
914
915 void
916 Editor::ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top)
917 {
918         if (track.hidden()) {
919                 return;
920         }
921
922         /* compute visible area of trackview group, as offsets from top of
923          * trackview group.
924          */
925
926         double const current_view_min_y = vertical_adjustment.get_value();
927         double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size();
928
929         double const track_min_y = track.y_position ();
930         double const track_max_y = track.y_position () + track.effective_height ();
931
932         if (!at_top &&
933             (track_min_y >= current_view_min_y &&
934              track_max_y < current_view_max_y)) {
935                 /* already visible, and caller did not ask to place it at the
936                  * top of the track canvas
937                  */
938                 return;
939         }
940
941         double new_value;
942
943         if (at_top) {
944                 new_value = track_min_y;
945         } else {
946                 if (track_min_y < current_view_min_y) {
947                         // Track is above the current view
948                         new_value = track_min_y;
949                 } else if (track_max_y > current_view_max_y) {
950                         // Track is below the current view
951                         new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size();
952                 } else {
953                         new_value = track_min_y;
954                 }
955         }
956
957         vertical_adjustment.set_value(new_value);
958 }
959
960 /** Called when the main vertical_adjustment has changed */
961 void
962 Editor::tie_vertical_scrolling ()
963 {
964         if (pending_visual_change.idle_handler_id < 0) {
965                 _summary->set_overlays_dirty ();
966         }
967 }
968
969 void
970 Editor::set_horizontal_position (double p)
971 {
972         horizontal_adjustment.set_value (p);
973
974         _leftmost_sample = (samplepos_t) floor (p * samples_per_pixel);
975 }
976
977 void
978 Editor::color_handler()
979 {
980         Gtkmm2ext::Color base = UIConfiguration::instance().color ("ruler base");
981         Gtkmm2ext::Color text = UIConfiguration::instance().color ("ruler text");
982         timecode_ruler->set_fill_color (base);
983         timecode_ruler->set_outline_color (text);
984         minsec_ruler->set_fill_color (base);
985         minsec_ruler->set_outline_color (text);
986         samples_ruler->set_fill_color (base);
987         samples_ruler->set_outline_color (text);
988         bbt_ruler->set_fill_color (base);
989         bbt_ruler->set_outline_color (text);
990
991         playhead_cursor->set_color (UIConfiguration::instance().color ("play head"));
992
993         meter_bar->set_fill_color (UIConfiguration::instance().color_mod ("meter bar", "marker bar"));
994         meter_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
995
996         tempo_bar->set_fill_color (UIConfiguration::instance().color_mod ("tempo bar", "marker bar"));
997         tempo_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
998
999         marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("marker bar", "marker bar"));
1000         marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
1001
1002         cd_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("cd marker bar", "marker bar"));
1003         cd_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
1004
1005         range_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("range marker bar", "marker bar"));
1006         range_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
1007
1008         transport_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("transport marker bar", "marker bar"));
1009         transport_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
1010
1011         cd_marker_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect"));
1012         cd_marker_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect"));
1013
1014         range_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect"));
1015         range_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect"));
1016
1017         transport_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("transport drag rect"));
1018         transport_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("transport drag rect"));
1019
1020         transport_loop_range_rect->set_fill_color (UIConfiguration::instance().color_mod ("transport loop rect", "loop rectangle"));
1021         transport_loop_range_rect->set_outline_color (UIConfiguration::instance().color ("transport loop rect"));
1022
1023         transport_punch_range_rect->set_fill_color (UIConfiguration::instance().color ("transport punch rect"));
1024         transport_punch_range_rect->set_outline_color (UIConfiguration::instance().color ("transport punch rect"));
1025
1026         transport_punchin_line->set_outline_color (UIConfiguration::instance().color ("punch line"));
1027         transport_punchout_line->set_outline_color (UIConfiguration::instance().color ("punch line"));
1028
1029         rubberband_rect->set_outline_color (UIConfiguration::instance().color ("rubber band rect"));
1030         rubberband_rect->set_fill_color (UIConfiguration::instance().color_mod ("rubber band rect", "selection rect"));
1031
1032         location_marker_color = UIConfiguration::instance().color ("location marker");
1033         location_range_color = UIConfiguration::instance().color ("location range");
1034         location_cd_marker_color = UIConfiguration::instance().color ("location cd marker");
1035         location_loop_color = UIConfiguration::instance().color ("location loop");
1036         location_punch_color = UIConfiguration::instance().color ("location punch");
1037
1038         refresh_location_display ();
1039
1040         NoteBase::set_colors ();
1041
1042         /* redraw the whole thing */
1043         _track_canvas->set_background_color (UIConfiguration::instance().color ("arrange base"));
1044         _track_canvas->queue_draw ();
1045
1046 /*
1047         redisplay_grid (true);
1048
1049         if (_session)
1050               _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
1051 */
1052 }
1053
1054 double
1055 Editor::horizontal_position () const
1056 {
1057         return sample_to_pixel (_leftmost_sample);
1058 }
1059
1060 bool
1061 Editor::track_canvas_key_press (GdkEventKey*)
1062 {
1063         return false;
1064 }
1065
1066 bool
1067 Editor::track_canvas_key_release (GdkEventKey*)
1068 {
1069         return false;
1070 }
1071
1072 double
1073 Editor::clamp_verbose_cursor_x (double x)
1074 {
1075         if (x < 0) {
1076                 x = 0;
1077         } else {
1078                 x = min (_visible_canvas_width - 200.0, x);
1079         }
1080         return x;
1081 }
1082
1083 double
1084 Editor::clamp_verbose_cursor_y (double y)
1085 {
1086         y = max (0.0, y);
1087         y = min (_visible_canvas_height - 50, y);
1088         return y;
1089 }
1090
1091 ArdourCanvas::GtkCanvasViewport*
1092 Editor::get_track_canvas() const
1093 {
1094         return _track_canvas_viewport;
1095 }
1096
1097 Gdk::Cursor*
1098 Editor::get_canvas_cursor () const
1099 {
1100         /* The top of the cursor stack is always the currently visible cursor. */
1101         return _cursor_stack.back();
1102 }
1103
1104 void
1105 Editor::set_canvas_cursor (Gdk::Cursor* cursor)
1106 {
1107         Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
1108
1109         if (win && !_cursors->is_invalid (cursor)) {
1110                 /* glibmm 2.4 doesn't allow null cursor pointer because it uses
1111                    a Gdk::Cursor& as the argument to Gdk::Window::set_cursor().
1112                    But a null pointer just means "use parent window cursor",
1113                    and so should be allowed. Gtkmm 3.x has fixed this API.
1114
1115                    For now, drop down and use C API
1116                 */
1117                 gdk_window_set_cursor (win->gobj(), cursor ? cursor->gobj() : 0);
1118         }
1119 }
1120
1121 size_t
1122 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
1123 {
1124         if (!_cursors->is_invalid (cursor)) {
1125                 _cursor_stack.push_back (cursor);
1126                 set_canvas_cursor (cursor);
1127         }
1128         return _cursor_stack.size() - 1;
1129 }
1130
1131 void
1132 Editor::pop_canvas_cursor ()
1133 {
1134         while (true) {
1135                 if (_cursor_stack.size() <= 1) {
1136                         PBD::error << "attempt to pop default cursor" << endmsg;
1137                         return;
1138                 }
1139
1140                 _cursor_stack.pop_back();
1141                 if (_cursor_stack.back()) {
1142                         /* Popped to an existing cursor, we're done.  Otherwise, the
1143                            context that created this cursor has been destroyed, so we need
1144                            to skip to the next down the stack. */
1145                         set_canvas_cursor (_cursor_stack.back());
1146                         return;
1147                 }
1148         }
1149 }
1150
1151 Gdk::Cursor*
1152 Editor::which_trim_cursor (bool left) const
1153 {
1154         if (!entered_regionview) {
1155                 return 0;
1156         }
1157
1158         Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
1159
1160         if (left) {
1161
1162                 if (ct & Trimmable::FrontTrimEarlier) {
1163                         return _cursors->left_side_trim;
1164                 } else {
1165                         return _cursors->left_side_trim_right_only;
1166                 }
1167         } else {
1168                 if (ct & Trimmable::EndTrimLater) {
1169                         return _cursors->right_side_trim;
1170                 } else {
1171                         return _cursors->right_side_trim_left_only;
1172                 }
1173         }
1174 }
1175
1176 Gdk::Cursor*
1177 Editor::which_mode_cursor () const
1178 {
1179         Gdk::Cursor* mode_cursor = MouseCursors::invalid_cursor ();
1180
1181         switch (mouse_mode) {
1182         case MouseRange:
1183                 mode_cursor = _cursors->selector;
1184                 break;
1185
1186         case MouseCut:
1187                 mode_cursor = _cursors->scissors;
1188                 break;
1189
1190         case MouseObject:
1191         case MouseContent:
1192                 /* don't use mode cursor, pick a grabber cursor based on the item */
1193                 break;
1194
1195         case MouseDraw:
1196                 mode_cursor = _cursors->midi_pencil;
1197                 break;
1198
1199         case MouseTimeFX:
1200                 mode_cursor = _cursors->time_fx; // just use playhead
1201                 break;
1202
1203         case MouseAudition:
1204                 mode_cursor = _cursors->speaker;
1205                 break;
1206         }
1207
1208         /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
1209         if (get_smart_mode()) {
1210
1211                 double x, y;
1212                 get_pointer_position (x, y);
1213
1214                 if (x >= 0 && y >= 0) {
1215
1216                         vector<ArdourCanvas::Item const *> items;
1217
1218                         /* Note how we choose a specific scroll group to get
1219                          * items from. This could be problematic.
1220                          */
1221
1222                         hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
1223
1224                         // first item will be the upper most
1225
1226                         if (!items.empty()) {
1227                                 const ArdourCanvas::Item* i = items.front();
1228
1229                                 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
1230                                         pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
1231                                         if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
1232                                                 mode_cursor = _cursors->up_down;
1233                                         }
1234                                 }
1235                         }
1236                 }
1237         }
1238
1239         return mode_cursor;
1240 }
1241
1242 Gdk::Cursor*
1243 Editor::which_track_cursor () const
1244 {
1245         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
1246
1247         switch (_join_object_range_state) {
1248         case JOIN_OBJECT_RANGE_NONE:
1249         case JOIN_OBJECT_RANGE_OBJECT:
1250                 cursor = _cursors->grabber;
1251                 break;
1252         case JOIN_OBJECT_RANGE_RANGE:
1253                 cursor = _cursors->selector;
1254                 break;
1255         }
1256
1257         return cursor;
1258 }
1259
1260 Gdk::Cursor*
1261 Editor::which_canvas_cursor(ItemType type) const
1262 {
1263         Gdk::Cursor* cursor = which_mode_cursor ();
1264
1265         if (mouse_mode == MouseRange) {
1266                 switch (type) {
1267                 case StartSelectionTrimItem:
1268                         cursor = _cursors->left_side_trim;
1269                         break;
1270                 case EndSelectionTrimItem:
1271                         cursor = _cursors->right_side_trim;
1272                         break;
1273                 default:
1274                         break;
1275                 }
1276         }
1277
1278         if ((mouse_mode == MouseObject || get_smart_mode ()) ||
1279             mouse_mode == MouseContent) {
1280
1281                 /* find correct cursor to use in object/smart mode */
1282
1283                 switch (type) {
1284                 case RegionItem:
1285                 /* We don't choose a cursor for these items on top of a region view,
1286                    because this would push a new context on the enter stack which
1287                    means switching the region context for things like smart mode
1288                    won't actualy change the cursor. */
1289                 // case RegionViewNameHighlight:
1290                 // case RegionViewName:
1291                 // case WaveItem:
1292                 case StreamItem:
1293                 case AutomationTrackItem:
1294                         cursor = which_track_cursor ();
1295                         break;
1296                 case PlayheadCursorItem:
1297                         cursor = _cursors->grabber;
1298                         break;
1299                 case SelectionItem:
1300                         cursor = _cursors->selector;
1301                         break;
1302                 case ControlPointItem:
1303                         cursor = _cursors->fader;
1304                         break;
1305                 case GainLineItem:
1306                         cursor = _cursors->cross_hair;
1307                         break;
1308                 case AutomationLineItem:
1309                         cursor = _cursors->cross_hair;
1310                         break;
1311                 case StartSelectionTrimItem:
1312                         cursor = _cursors->left_side_trim;
1313                         break;
1314                 case EndSelectionTrimItem:
1315                         cursor = _cursors->right_side_trim;
1316                         break;
1317                 case FadeInItem:
1318                         cursor = _cursors->fade_in;
1319                         break;
1320                 case FadeInHandleItem:
1321                         cursor = _cursors->fade_in;
1322                         break;
1323                 case FadeInTrimHandleItem:
1324                         cursor = _cursors->fade_in;
1325                         break;
1326                 case FadeOutItem:
1327                         cursor = _cursors->fade_out;
1328                         break;
1329                 case FadeOutHandleItem:
1330                         cursor = _cursors->fade_out;
1331                         break;
1332                 case FadeOutTrimHandleItem:
1333                         cursor = _cursors->fade_out;
1334                         break;
1335                 case FeatureLineItem:
1336                         cursor = _cursors->cross_hair;
1337                         break;
1338                 case LeftFrameHandle:
1339                         if ( effective_mouse_mode() == MouseObject )  // (smart mode): if the user is in the btm half, show the trim cursor
1340                                 cursor = which_trim_cursor (true);
1341                         else
1342                                 cursor = _cursors->selector;  // (smart mode): in the top half, just show the selection (range) cursor
1343                         break;
1344                 case RightFrameHandle:
1345                         if ( effective_mouse_mode() == MouseObject )  //see above
1346                                 cursor = which_trim_cursor (false);
1347                         else
1348                                 cursor = _cursors->selector;
1349                         break;
1350                 case StartCrossFadeItem:
1351                         cursor = _cursors->fade_in;
1352                         break;
1353                 case EndCrossFadeItem:
1354                         cursor = _cursors->fade_out;
1355                         break;
1356                 case CrossfadeViewItem:
1357                         cursor = _cursors->cross_hair;
1358                         break;
1359                 case NoteItem:
1360                         cursor = _cursors->grabber_note;
1361                 default:
1362                         break;
1363                 }
1364
1365         } else if (mouse_mode == MouseDraw) {
1366
1367                 /* ControlPointItem is not really specific to region gain mode
1368                    but it is the same cursor so don't worry about this for now.
1369                    The result is that we'll see the fader cursor if we enter
1370                    non-region-gain-line control points while in MouseDraw
1371                    mode, even though we can't edit them in this mode.
1372                 */
1373
1374                 switch (type) {
1375                 case GainLineItem:
1376                 case ControlPointItem:
1377                         cursor = _cursors->fader;
1378                         break;
1379                 case NoteItem:
1380                         cursor = _cursors->grabber_note;
1381                 default:
1382                         break;
1383                 }
1384         }
1385
1386         switch (type) {
1387                 /* These items use the timebar cursor at all times */
1388         case TimecodeRulerItem:
1389         case MinsecRulerItem:
1390         case BBTRulerItem:
1391         case SamplesRulerItem:
1392                 cursor = _cursors->timebar;
1393                 break;
1394
1395                 /* These items use the grabber cursor at all times */
1396         case MeterMarkerItem:
1397         case TempoMarkerItem:
1398         case MeterBarItem:
1399         case TempoBarItem:
1400         case MarkerItem:
1401         case MarkerBarItem:
1402         case RangeMarkerBarItem:
1403         case CdMarkerBarItem:
1404         case VideoBarItem:
1405         case TransportMarkerBarItem:
1406         case DropZoneItem:
1407                 cursor = _cursors->grabber;
1408                 break;
1409
1410         default:
1411                 break;
1412         }
1413
1414         return cursor;
1415 }
1416
1417 void
1418 Editor::choose_canvas_cursor_on_entry (ItemType type)
1419 {
1420         if (_drags->active()) {
1421                 return;
1422         }
1423
1424         Gdk::Cursor* cursor = which_canvas_cursor(type);
1425
1426         if (!_cursors->is_invalid (cursor)) {
1427                 // Push a new enter context
1428                 const EnterContext ctx = { type, CursorContext::create(*this, cursor) };
1429                 _enter_stack.push_back(ctx);
1430         }
1431 }
1432
1433 void
1434 Editor::update_all_enter_cursors ()
1435 {
1436         for (std::vector<EnterContext>::iterator i = _enter_stack.begin(); i != _enter_stack.end(); ++i) {
1437                 i->cursor_ctx->change(which_canvas_cursor(i->item_type));
1438         }
1439 }
1440
1441 double
1442 Editor::trackviews_height() const
1443 {
1444         if (!_trackview_group) {
1445                 return 0;
1446         }
1447
1448         return _visible_canvas_height - _trackview_group->canvas_origin().y;
1449 }