NO-OP: whitespace and some guaranteed assertion removal
[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 (!ARDOUR_UI_UTILS::engine_is_running ()) {
377                 return;
378         }
379         if (data.get_target() == "regions") {
380                 drop_regions (context, x, y, data, info, time);
381         } else {
382                 drop_paths (context, x, y, data, info, time);
383         }
384 }
385
386 bool
387 Editor::idle_drop_paths (vector<string> paths, samplepos_t sample, double ypos, bool copy)
388 {
389         drop_paths_part_two (paths, sample, ypos, copy);
390         return false;
391 }
392
393 void
394 Editor::drop_paths_part_two (const vector<string>& paths, samplepos_t sample, double ypos, bool copy)
395 {
396         RouteTimeAxisView* tv;
397
398         /* MIDI files must always be imported, because we consider them
399          * writable. So split paths into two vectors, and follow the import
400          * path on the MIDI part.
401          */
402
403         vector<string> midi_paths;
404         vector<string> audio_paths;
405
406         for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
407                 if (SMFSource::safe_midi_file_extension (*i)) {
408                         midi_paths.push_back (*i);
409                 } else {
410                         audio_paths.push_back (*i);
411                 }
412         }
413
414
415         std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (ypos, false);
416         if (tvp.first == 0) {
417
418                 /* drop onto canvas background: create new tracks */
419
420                 sample = 0;
421                 InstrumentSelector is; // instantiation builds instrument-list and sets default.
422                 do_import (midi_paths, Editing::ImportDistinctFiles, ImportAsTrack, SrcBest, SMFTrackName, SMFTempoIgnore, sample, is.selected_instrument());
423
424                 if (UIConfiguration::instance().get_only_copy_imported_files() || copy) {
425                         do_import (audio_paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack,
426                                    SrcBest, SMFTrackName, SMFTempoIgnore, sample);
427                 } else {
428                         do_embed (audio_paths, Editing::ImportDistinctFiles, ImportAsTrack, sample);
429                 }
430
431         } else if ((tv = dynamic_cast<RouteTimeAxisView*> (tvp.first)) != 0) {
432
433                 /* check that its a track, not a bus */
434
435                 if (tv->track()) {
436                         /* select the track, then embed/import */
437                         selection->set (tv);
438
439                         do_import (midi_paths, Editing::ImportSerializeFiles, ImportToTrack,
440                                    SrcBest, SMFTrackName, SMFTempoIgnore, sample);
441
442                         if (UIConfiguration::instance().get_only_copy_imported_files() || copy) {
443                                 do_import (audio_paths, Editing::ImportSerializeFiles, Editing::ImportToTrack,
444                                            SrcBest, SMFTrackName, SMFTempoIgnore, sample);
445                         } else {
446                                 do_embed (audio_paths, Editing::ImportSerializeFiles, ImportToTrack, sample);
447                         }
448                 }
449         }
450 }
451
452 void
453 Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
454                     int x, int y,
455                     const SelectionData& data,
456                     guint info, guint time)
457 {
458         vector<string> paths;
459         GdkEvent ev;
460         double cy;
461
462         if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
463
464                 /* D-n-D coordinates are window-relative, so convert to canvas coordinates
465                  */
466
467                 ev.type = GDK_BUTTON_RELEASE;
468                 ev.button.x = x;
469                 ev.button.y = y;
470
471                 MusicSample when (window_event_sample (&ev, 0, &cy), 0);
472                 snap_to (when);
473
474                 bool copy = ((context->get_actions() & (Gdk::ACTION_COPY | Gdk::ACTION_LINK | Gdk::ACTION_MOVE)) == Gdk::ACTION_COPY);
475 #ifdef __APPLE__
476                 /* We are not allowed to call recursive main event loops from within
477                    the main event loop with GTK/Quartz. Since import/embed wants
478                    to push up a progress dialog, defer all this till we go idle.
479                 */
480                 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun (*this, &Editor::idle_drop_paths), paths, when.sample, cy, copy));
481 #else
482                 drop_paths_part_two (paths, when.sample, cy, copy);
483 #endif
484         }
485
486         context->drag_finish (true, false, time);
487 }
488
489 /** @param allow_horiz true to allow horizontal autoscroll, otherwise false.
490  *
491  *  @param allow_vert true to allow vertical autoscroll, otherwise false.
492  *
493  */
494 void
495 Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
496 {
497         Gtk::Window* toplevel = dynamic_cast<Gtk::Window*>(contents().get_toplevel());
498
499         if (!toplevel) {
500                 return;
501         }
502
503         if (!UIConfiguration::instance().get_autoscroll_editor () || autoscroll_active ()) {
504                 return;
505         }
506
507         /* define a rectangular boundary for scrolling. If the mouse moves
508          * outside of this area and/or continue to be outside of this area,
509          * then we will continuously auto-scroll the canvas in the appropriate
510          * direction(s)
511          *
512          * the boundary is defined in coordinates relative to the toplevel
513          * window since that is what we're going to call ::get_pointer() on
514          * during autoscrolling to determine if we're still outside the
515          * boundary or not.
516          */
517
518         ArdourCanvas::Rect scrolling_boundary;
519         Gtk::Allocation alloc;
520
521         if (from_headers) {
522                 alloc = controls_layout.get_allocation ();
523
524                 int wx, wy;
525
526                 controls_layout.get_parent()->translate_coordinates (*toplevel,
527                                                                      alloc.get_x(), alloc.get_y(),
528                                                                      wx, wy);
529
530                 scrolling_boundary = ArdourCanvas::Rect (wx, wy, wx + alloc.get_width(), wy + alloc.get_height());
531
532
533         } else {
534                 alloc = _track_canvas_viewport->get_allocation ();
535
536                 /* reduce height by the height of the timebars, which happens
537                    to correspond to the position of the hv_scroll_group.
538                 */
539
540                 alloc.set_height (alloc.get_height() - hv_scroll_group->position().y);
541                 alloc.set_y (alloc.get_y() + hv_scroll_group->position().y);
542
543                 /* now reduce it again so that we start autoscrolling before we
544                  * move off the top or bottom of the canvas
545                  */
546
547                 alloc.set_height (alloc.get_height() - 20);
548                 alloc.set_y (alloc.get_y() + 10);
549
550                 /* the effective width of the autoscroll boundary so
551                    that we start scrolling before we hit the edge.
552
553                    this helps when the window is slammed up against the
554                    right edge of the screen, making it hard to scroll
555                    effectively.
556                 */
557
558                 if (alloc.get_width() > 20) {
559                         alloc.set_width (alloc.get_width() - 20);
560                         alloc.set_x (alloc.get_x() + 10);
561                 }
562
563                 int wx, wy;
564
565                 _track_canvas_viewport->get_parent()->translate_coordinates (*toplevel,
566                                                                              alloc.get_x(), alloc.get_y(),
567                                                                              wx, wy);
568
569                 scrolling_boundary = ArdourCanvas::Rect (wx, wy, wx + alloc.get_width(), wy + alloc.get_height());
570         }
571
572         int x, y;
573         Gdk::ModifierType mask;
574
575         toplevel->get_window()->get_pointer (x, y, mask);
576
577         if ((allow_horiz && ((x < scrolling_boundary.x0 && _leftmost_sample > 0) || x >= scrolling_boundary.x1)) ||
578             (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) {
579                 start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary);
580         }
581 }
582
583 bool
584 Editor::autoscroll_active () const
585 {
586         return autoscroll_connection.connected ();
587 }
588
589 std::pair <samplepos_t,samplepos_t>
590 Editor::session_gui_extents (bool use_extra) const
591 {
592         if (!_session) {
593                 return std::pair <samplepos_t,samplepos_t>(max_samplepos,0);
594         }
595
596         samplecnt_t session_extent_start = _session->current_start_sample();
597         samplecnt_t session_extent_end = _session->current_end_sample();
598
599         /* calculate the extents of all regions in every playlist
600          * NOTE: we should listen to playlists, and cache these values so we don't calculate them every time.
601          */
602         {
603                 boost::shared_ptr<RouteList> rl = _session->get_routes();
604                 for (RouteList::iterator r = rl->begin(); r != rl->end(); ++r) {
605                         boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*r);
606                         if (tr) {
607                                 boost::shared_ptr<Playlist> pl = tr->playlist();
608                                 if (pl && !pl->all_regions_empty()) {
609                                         pair<samplepos_t, samplepos_t> e;
610                                         e = pl->get_extent();
611                                         if (e.first < session_extent_start) {
612                                                 session_extent_start = e.first;
613                                         }
614                                         if (e.second > session_extent_end) {
615                                                 session_extent_end = e.second;
616                                         }
617                                 }
618                         }
619                 }
620         }
621
622         /* ToDo: also incorporate automation regions (in case the session has no audio/midi but is just used for automating plugins or the like) */
623
624         /* add additional time to the ui extents (user-defined in config) */
625         if (use_extra) {
626                 samplecnt_t const extra = UIConfiguration::instance().get_extra_ui_extents_time() * 60 * _session->nominal_sample_rate();
627                 session_extent_end += extra;
628                 session_extent_start -= extra;
629         }
630
631         /* range-check */
632         if (session_extent_end > max_samplepos) {
633                 session_extent_end = max_samplepos;
634         }
635         if (session_extent_start < 0) {
636                 session_extent_start = 0;
637         }
638
639         std::pair <samplepos_t,samplepos_t> ret (session_extent_start, session_extent_end);
640         return ret;
641 }
642
643 bool
644 Editor::autoscroll_canvas ()
645 {
646         int x, y;
647         Gdk::ModifierType mask;
648         sampleoffset_t dx = 0;
649         bool no_stop = false;
650         Gtk::Window* toplevel = dynamic_cast<Gtk::Window*>(contents().get_toplevel());
651
652         if (!toplevel) {
653                 return false;
654         }
655
656         toplevel->get_window()->get_pointer (x, y, mask);
657
658         VisualChange vc;
659         bool vertical_motion = false;
660
661         if (autoscroll_horizontal_allowed) {
662
663                 samplepos_t new_sample = _leftmost_sample;
664
665                 /* horizontal */
666
667                 if (x > autoscroll_boundary.x1) {
668
669                         /* bring it back into view */
670                         dx = x - autoscroll_boundary.x1;
671                         dx += 10 + (2 * (autoscroll_cnt/2));
672
673                         dx = pixel_to_sample (dx);
674
675                         dx *= UIConfiguration::instance().get_draggable_playhead_speed();
676
677                         if (_leftmost_sample < max_samplepos - dx) {
678                                 new_sample = _leftmost_sample + dx;
679                         } else {
680                                 new_sample = max_samplepos;
681                         }
682
683                         no_stop = true;
684
685                 } else if (x < autoscroll_boundary.x0) {
686
687                         dx = autoscroll_boundary.x0 - x;
688                         dx += 10 + (2 * (autoscroll_cnt/2));
689
690                         dx = pixel_to_sample (dx);
691
692                         dx *= UIConfiguration::instance().get_draggable_playhead_speed();
693
694                         if (_leftmost_sample >= dx) {
695                                 new_sample = _leftmost_sample - dx;
696                         } else {
697                                 new_sample = 0;
698                         }
699
700                         no_stop = true;
701                 }
702
703                 if (new_sample != _leftmost_sample) {
704                         vc.time_origin = new_sample;
705                         vc.add (VisualChange::TimeOrigin);
706                 }
707         }
708
709         if (autoscroll_vertical_allowed) {
710
711                 // const double vertical_pos = vertical_adjustment.get_value();
712                 const int speed_factor = 10;
713
714                 /* vertical */
715
716                 if (y < autoscroll_boundary.y0) {
717
718                         /* scroll to make higher tracks visible */
719
720                         if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
721                                 scroll_up_one_track ();
722                                 vertical_motion = true;
723                         }
724                         no_stop = true;
725
726                 } else if (y > autoscroll_boundary.y1) {
727
728                         if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
729                                 scroll_down_one_track ();
730                                 vertical_motion = true;
731                         }
732                         no_stop = true;
733                 }
734
735         }
736
737         if (vc.pending || vertical_motion) {
738
739                 /* change horizontal first */
740
741                 if (vc.pending) {
742                         visual_changer (vc);
743                 }
744
745                 /* now send a motion event to notify anyone who cares
746                    that we have moved to a new location (because we scrolled)
747                 */
748
749                 GdkEventMotion ev;
750
751                 ev.type = GDK_MOTION_NOTIFY;
752                 ev.state = Gdk::BUTTON1_MASK;
753
754                 /* the motion handler expects events in canvas coordinate space */
755
756                 /* we asked for the mouse position above (::get_pointer()) via
757                  * our own top level window (we being the Editor). Convert into
758                  * coordinates within the canvas window.
759                  */
760
761                 int cx;
762                 int cy;
763
764                 toplevel->translate_coordinates (*_track_canvas, x, y, cx, cy);
765
766                 /* clamp x and y to remain within the autoscroll boundary,
767                  * which is defined in window coordinates
768                  */
769
770                 x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1);
771                 y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1);
772
773                 /* now convert from Editor window coordinates to canvas
774                  * window coordinates
775                  */
776
777                 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
778                 ev.x = d.x;
779                 ev.y = d.y;
780                 ev.state = mask;
781
782                 motion_handler (0, (GdkEvent*) &ev, true);
783
784         } else if (no_stop) {
785
786                 /* not changing visual state but pointer is outside the scrolling boundary
787                  * so we still need to deliver a fake motion event
788                  */
789
790                 GdkEventMotion ev;
791
792                 ev.type = GDK_MOTION_NOTIFY;
793                 ev.state = Gdk::BUTTON1_MASK;
794
795                 /* the motion handler expects events in canvas coordinate space */
796
797                 /* first convert from Editor window coordinates to canvas
798                  * window coordinates
799                  */
800
801                 int cx;
802                 int cy;
803
804                 /* clamp x and y to remain within the visible area. except
805                  * .. if horizontal scrolling is allowed, always allow us to
806                  * move back to zero
807                  */
808
809                 if (autoscroll_horizontal_allowed) {
810                         x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1);
811                 } else {
812                         x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
813                 }
814                 y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
815
816                 toplevel->translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
817
818                 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
819                 ev.x = d.x;
820                 ev.y = d.y;
821                 ev.state = mask;
822
823                 motion_handler (0, (GdkEvent*) &ev, true);
824
825         } else {
826                 stop_canvas_autoscroll ();
827                 return false;
828         }
829
830         autoscroll_cnt++;
831
832         return true; /* call me again */
833 }
834
835 void
836 Editor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary)
837 {
838         if (!_session) {
839                 return;
840         }
841
842         stop_canvas_autoscroll ();
843
844         autoscroll_horizontal_allowed = allow_horiz;
845         autoscroll_vertical_allowed = allow_vert;
846         autoscroll_boundary = boundary;
847
848         /* do the first scroll right now
849         */
850
851         autoscroll_canvas ();
852
853         /* scroll again at very very roughly 30FPS */
854
855         autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::autoscroll_canvas), 30);
856 }
857
858 void
859 Editor::stop_canvas_autoscroll ()
860 {
861         autoscroll_connection.disconnect ();
862         autoscroll_cnt = 0;
863 }
864
865 Editor::EnterContext*
866 Editor::get_enter_context(ItemType type)
867 {
868         for (ssize_t i = _enter_stack.size() - 1; i >= 0; --i) {
869                 if (_enter_stack[i].item_type == type) {
870                         return &_enter_stack[i];
871                 }
872         }
873         return NULL;
874 }
875
876 bool
877 Editor::left_track_canvas (GdkEventCrossing* ev)
878 {
879         const bool was_within = within_track_canvas;
880         DropDownKeys ();
881         within_track_canvas = false;
882         set_entered_track (0);
883         set_entered_regionview (0);
884         reset_canvas_action_sensitivity (false);
885
886         if (was_within) {
887                 if (ev->detail == GDK_NOTIFY_NONLINEAR ||
888                     ev->detail == GDK_NOTIFY_NONLINEAR_VIRTUAL) {
889                         /* context menu or something similar */
890                         sensitize_the_right_region_actions (false);
891                 } else {
892                         sensitize_the_right_region_actions (true);
893                 }
894         }
895
896         return false;
897 }
898
899 bool
900 Editor::entered_track_canvas (GdkEventCrossing* ev)
901 {
902         const bool was_within = within_track_canvas;
903         within_track_canvas = true;
904         reset_canvas_action_sensitivity (true);
905
906         if (!was_within) {
907                 if (ev->detail == GDK_NOTIFY_NONLINEAR ||
908                     ev->detail == GDK_NOTIFY_NONLINEAR_VIRTUAL) {
909                         /* context menu or something similar */
910                         sensitize_the_right_region_actions (false);
911                 } else {
912                         sensitize_the_right_region_actions (true);
913                 }
914         }
915
916         return false;
917 }
918
919 void
920 Editor::ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top)
921 {
922         if (track.hidden()) {
923                 return;
924         }
925
926         /* compute visible area of trackview group, as offsets from top of
927          * trackview group.
928          */
929
930         double const current_view_min_y = vertical_adjustment.get_value();
931         double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size();
932
933         double const track_min_y = track.y_position ();
934         double const track_max_y = track.y_position () + track.effective_height ();
935
936         if (!at_top &&
937             (track_min_y >= current_view_min_y &&
938              track_max_y < current_view_max_y)) {
939                 /* already visible, and caller did not ask to place it at the
940                  * top of the track canvas
941                  */
942                 return;
943         }
944
945         double new_value;
946
947         if (at_top) {
948                 new_value = track_min_y;
949         } else {
950                 if (track_min_y < current_view_min_y) {
951                         // Track is above the current view
952                         new_value = track_min_y;
953                 } else if (track_max_y > current_view_max_y) {
954                         // Track is below the current view
955                         new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size();
956                 } else {
957                         new_value = track_min_y;
958                 }
959         }
960
961         vertical_adjustment.set_value(new_value);
962 }
963
964 /** Called when the main vertical_adjustment has changed */
965 void
966 Editor::tie_vertical_scrolling ()
967 {
968         if (pending_visual_change.idle_handler_id < 0) {
969                 _summary->set_overlays_dirty ();
970         }
971 }
972
973 void
974 Editor::set_horizontal_position (double p)
975 {
976         horizontal_adjustment.set_value (p);
977
978         _leftmost_sample = (samplepos_t) floor (p * samples_per_pixel);
979 }
980
981 void
982 Editor::color_handler()
983 {
984         Gtkmm2ext::Color base = UIConfiguration::instance().color ("ruler base");
985         Gtkmm2ext::Color text = UIConfiguration::instance().color ("ruler text");
986         timecode_ruler->set_fill_color (base);
987         timecode_ruler->set_outline_color (text);
988         minsec_ruler->set_fill_color (base);
989         minsec_ruler->set_outline_color (text);
990         samples_ruler->set_fill_color (base);
991         samples_ruler->set_outline_color (text);
992         bbt_ruler->set_fill_color (base);
993         bbt_ruler->set_outline_color (text);
994
995         playhead_cursor->set_color (UIConfiguration::instance().color ("play head"));
996
997         meter_bar->set_fill_color (UIConfiguration::instance().color_mod ("meter bar", "marker bar"));
998         meter_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
999
1000         tempo_bar->set_fill_color (UIConfiguration::instance().color_mod ("tempo bar", "marker bar"));
1001         tempo_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
1002
1003         marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("marker bar", "marker bar"));
1004         marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
1005
1006         cd_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("cd marker bar", "marker bar"));
1007         cd_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
1008
1009         range_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("range marker bar", "marker bar"));
1010         range_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
1011
1012         transport_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("transport marker bar", "marker bar"));
1013         transport_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
1014
1015         cd_marker_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect"));
1016         cd_marker_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect"));
1017
1018         range_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect"));
1019         range_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect"));
1020
1021         transport_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("transport drag rect"));
1022         transport_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("transport drag rect"));
1023
1024         transport_loop_range_rect->set_fill_color (UIConfiguration::instance().color_mod ("transport loop rect", "loop rectangle"));
1025         transport_loop_range_rect->set_outline_color (UIConfiguration::instance().color ("transport loop rect"));
1026
1027         transport_punch_range_rect->set_fill_color (UIConfiguration::instance().color ("transport punch rect"));
1028         transport_punch_range_rect->set_outline_color (UIConfiguration::instance().color ("transport punch rect"));
1029
1030         transport_punchin_line->set_outline_color (UIConfiguration::instance().color ("punch line"));
1031         transport_punchout_line->set_outline_color (UIConfiguration::instance().color ("punch line"));
1032
1033         rubberband_rect->set_outline_color (UIConfiguration::instance().color ("rubber band rect"));
1034         rubberband_rect->set_fill_color (UIConfiguration::instance().color_mod ("rubber band rect", "selection rect"));
1035
1036         location_marker_color = UIConfiguration::instance().color ("location marker");
1037         location_range_color = UIConfiguration::instance().color ("location range");
1038         location_cd_marker_color = UIConfiguration::instance().color ("location cd marker");
1039         location_loop_color = UIConfiguration::instance().color ("location loop");
1040         location_punch_color = UIConfiguration::instance().color ("location punch");
1041
1042         refresh_location_display ();
1043
1044         NoteBase::set_colors ();
1045
1046         /* redraw the whole thing */
1047         _track_canvas->set_background_color (UIConfiguration::instance().color ("arrange base"));
1048         _track_canvas->queue_draw ();
1049
1050 /*
1051         redisplay_grid (true);
1052
1053         if (_session)
1054               _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
1055 */
1056 }
1057
1058 double
1059 Editor::horizontal_position () const
1060 {
1061         return sample_to_pixel (_leftmost_sample);
1062 }
1063
1064 bool
1065 Editor::track_canvas_key_press (GdkEventKey*)
1066 {
1067         return false;
1068 }
1069
1070 bool
1071 Editor::track_canvas_key_release (GdkEventKey*)
1072 {
1073         return false;
1074 }
1075
1076 double
1077 Editor::clamp_verbose_cursor_x (double x)
1078 {
1079         if (x < 0) {
1080                 x = 0;
1081         } else {
1082                 x = min (_visible_canvas_width - 200.0, x);
1083         }
1084         return x;
1085 }
1086
1087 double
1088 Editor::clamp_verbose_cursor_y (double y)
1089 {
1090         y = max (0.0, y);
1091         y = min (_visible_canvas_height - 50, y);
1092         return y;
1093 }
1094
1095 ArdourCanvas::GtkCanvasViewport*
1096 Editor::get_track_canvas() const
1097 {
1098         return _track_canvas_viewport;
1099 }
1100
1101 Gdk::Cursor*
1102 Editor::get_canvas_cursor () const
1103 {
1104         /* The top of the cursor stack is always the currently visible cursor. */
1105         return _cursor_stack.back();
1106 }
1107
1108 void
1109 Editor::set_canvas_cursor (Gdk::Cursor* cursor)
1110 {
1111         Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
1112
1113         if (win && !_cursors->is_invalid (cursor)) {
1114                 /* glibmm 2.4 doesn't allow null cursor pointer because it uses
1115                    a Gdk::Cursor& as the argument to Gdk::Window::set_cursor().
1116                    But a null pointer just means "use parent window cursor",
1117                    and so should be allowed. Gtkmm 3.x has fixed this API.
1118
1119                    For now, drop down and use C API
1120                 */
1121                 gdk_window_set_cursor (win->gobj(), cursor ? cursor->gobj() : 0);
1122         }
1123 }
1124
1125 size_t
1126 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
1127 {
1128         if (!_cursors->is_invalid (cursor)) {
1129                 _cursor_stack.push_back (cursor);
1130                 set_canvas_cursor (cursor);
1131         }
1132         return _cursor_stack.size() - 1;
1133 }
1134
1135 void
1136 Editor::pop_canvas_cursor ()
1137 {
1138         while (true) {
1139                 if (_cursor_stack.size() <= 1) {
1140                         PBD::error << "attempt to pop default cursor" << endmsg;
1141                         return;
1142                 }
1143
1144                 _cursor_stack.pop_back();
1145                 if (_cursor_stack.back()) {
1146                         /* Popped to an existing cursor, we're done.  Otherwise, the
1147                            context that created this cursor has been destroyed, so we need
1148                            to skip to the next down the stack. */
1149                         set_canvas_cursor (_cursor_stack.back());
1150                         return;
1151                 }
1152         }
1153 }
1154
1155 Gdk::Cursor*
1156 Editor::which_trim_cursor (bool left) const
1157 {
1158         if (!entered_regionview) {
1159                 return 0;
1160         }
1161
1162         Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
1163
1164         if (left) {
1165
1166                 if (ct & Trimmable::FrontTrimEarlier) {
1167                         return _cursors->left_side_trim;
1168                 } else {
1169                         return _cursors->left_side_trim_right_only;
1170                 }
1171         } else {
1172                 if (ct & Trimmable::EndTrimLater) {
1173                         return _cursors->right_side_trim;
1174                 } else {
1175                         return _cursors->right_side_trim_left_only;
1176                 }
1177         }
1178 }
1179
1180 Gdk::Cursor*
1181 Editor::which_mode_cursor () const
1182 {
1183         Gdk::Cursor* mode_cursor = MouseCursors::invalid_cursor ();
1184
1185         switch (mouse_mode) {
1186         case MouseRange:
1187                 mode_cursor = _cursors->selector;
1188                 break;
1189
1190         case MouseCut:
1191                 mode_cursor = _cursors->scissors;
1192                 break;
1193
1194         case MouseObject:
1195         case MouseContent:
1196                 /* don't use mode cursor, pick a grabber cursor based on the item */
1197                 break;
1198
1199         case MouseDraw:
1200                 mode_cursor = _cursors->midi_pencil;
1201                 break;
1202
1203         case MouseTimeFX:
1204                 mode_cursor = _cursors->time_fx; // just use playhead
1205                 break;
1206
1207         case MouseAudition:
1208                 mode_cursor = _cursors->speaker;
1209                 break;
1210         }
1211
1212         /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
1213         if (get_smart_mode()) {
1214
1215                 double x, y;
1216                 get_pointer_position (x, y);
1217
1218                 if (x >= 0 && y >= 0) {
1219
1220                         vector<ArdourCanvas::Item const *> items;
1221
1222                         /* Note how we choose a specific scroll group to get
1223                          * items from. This could be problematic.
1224                          */
1225
1226                         hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
1227
1228                         // first item will be the upper most
1229
1230                         if (!items.empty()) {
1231                                 const ArdourCanvas::Item* i = items.front();
1232
1233                                 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
1234                                         pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
1235                                         if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
1236                                                 mode_cursor = _cursors->up_down;
1237                                         }
1238                                 }
1239                         }
1240                 }
1241         }
1242
1243         return mode_cursor;
1244 }
1245
1246 Gdk::Cursor*
1247 Editor::which_track_cursor () const
1248 {
1249         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
1250
1251         switch (_join_object_range_state) {
1252         case JOIN_OBJECT_RANGE_NONE:
1253         case JOIN_OBJECT_RANGE_OBJECT:
1254                 cursor = _cursors->grabber;
1255                 break;
1256         case JOIN_OBJECT_RANGE_RANGE:
1257                 cursor = _cursors->selector;
1258                 break;
1259         }
1260
1261         return cursor;
1262 }
1263
1264 Gdk::Cursor*
1265 Editor::which_canvas_cursor(ItemType type) const
1266 {
1267         Gdk::Cursor* cursor = which_mode_cursor ();
1268
1269         if (mouse_mode == MouseRange) {
1270                 switch (type) {
1271                 case StartSelectionTrimItem:
1272                         cursor = _cursors->left_side_trim;
1273                         break;
1274                 case EndSelectionTrimItem:
1275                         cursor = _cursors->right_side_trim;
1276                         break;
1277                 default:
1278                         break;
1279                 }
1280         }
1281
1282         if ((mouse_mode == MouseObject || get_smart_mode ()) ||
1283             mouse_mode == MouseContent) {
1284
1285                 /* find correct cursor to use in object/smart mode */
1286
1287                 switch (type) {
1288                 case RegionItem:
1289                 /* We don't choose a cursor for these items on top of a region view,
1290                    because this would push a new context on the enter stack which
1291                    means switching the region context for things like smart mode
1292                    won't actualy change the cursor. */
1293                 // case RegionViewNameHighlight:
1294                 // case RegionViewName:
1295                 // case WaveItem:
1296                 case StreamItem:
1297                 case AutomationTrackItem:
1298                         cursor = which_track_cursor ();
1299                         break;
1300                 case PlayheadCursorItem:
1301                         cursor = _cursors->grabber;
1302                         break;
1303                 case SelectionItem:
1304                         cursor = _cursors->selector;
1305                         break;
1306                 case ControlPointItem:
1307                         cursor = _cursors->fader;
1308                         break;
1309                 case GainLineItem:
1310                         cursor = _cursors->cross_hair;
1311                         break;
1312                 case AutomationLineItem:
1313                         cursor = _cursors->cross_hair;
1314                         break;
1315                 case StartSelectionTrimItem:
1316                         cursor = _cursors->left_side_trim;
1317                         break;
1318                 case EndSelectionTrimItem:
1319                         cursor = _cursors->right_side_trim;
1320                         break;
1321                 case FadeInItem:
1322                         cursor = _cursors->fade_in;
1323                         break;
1324                 case FadeInHandleItem:
1325                         cursor = _cursors->fade_in;
1326                         break;
1327                 case FadeInTrimHandleItem:
1328                         cursor = _cursors->fade_in;
1329                         break;
1330                 case FadeOutItem:
1331                         cursor = _cursors->fade_out;
1332                         break;
1333                 case FadeOutHandleItem:
1334                         cursor = _cursors->fade_out;
1335                         break;
1336                 case FadeOutTrimHandleItem:
1337                         cursor = _cursors->fade_out;
1338                         break;
1339                 case FeatureLineItem:
1340                         cursor = _cursors->cross_hair;
1341                         break;
1342                 case LeftFrameHandle:
1343                         if (effective_mouse_mode() == MouseObject) // (smart mode): if the user is in the btm half, show the trim cursor
1344                                 cursor = which_trim_cursor (true);
1345                         else
1346                                 cursor = _cursors->selector; // (smart mode): in the top half, just show the selection (range) cursor
1347                         break;
1348                 case RightFrameHandle:
1349                         if (effective_mouse_mode() == MouseObject) // see above
1350                                 cursor = which_trim_cursor (false);
1351                         else
1352                                 cursor = _cursors->selector;
1353                         break;
1354                 case StartCrossFadeItem:
1355                         cursor = _cursors->fade_in;
1356                         break;
1357                 case EndCrossFadeItem:
1358                         cursor = _cursors->fade_out;
1359                         break;
1360                 case CrossfadeViewItem:
1361                         cursor = _cursors->cross_hair;
1362                         break;
1363                 case NoteItem:
1364                         cursor = _cursors->grabber_note;
1365                 default:
1366                         break;
1367                 }
1368
1369         } else if (mouse_mode == MouseDraw) {
1370
1371                 /* ControlPointItem is not really specific to region gain mode
1372                    but it is the same cursor so don't worry about this for now.
1373                    The result is that we'll see the fader cursor if we enter
1374                    non-region-gain-line control points while in MouseDraw
1375                    mode, even though we can't edit them in this mode.
1376                 */
1377
1378                 switch (type) {
1379                 case GainLineItem:
1380                 case ControlPointItem:
1381                         cursor = _cursors->fader;
1382                         break;
1383                 case NoteItem:
1384                         cursor = _cursors->grabber_note;
1385                 default:
1386                         break;
1387                 }
1388         }
1389
1390         switch (type) {
1391                 /* These items use the timebar cursor at all times */
1392         case TimecodeRulerItem:
1393         case MinsecRulerItem:
1394         case BBTRulerItem:
1395         case SamplesRulerItem:
1396                 cursor = _cursors->timebar;
1397                 break;
1398
1399                 /* These items use the grabber cursor at all times */
1400         case MeterMarkerItem:
1401         case TempoMarkerItem:
1402         case MeterBarItem:
1403         case TempoBarItem:
1404         case MarkerItem:
1405         case MarkerBarItem:
1406         case RangeMarkerBarItem:
1407         case CdMarkerBarItem:
1408         case VideoBarItem:
1409         case TransportMarkerBarItem:
1410         case DropZoneItem:
1411                 cursor = _cursors->grabber;
1412                 break;
1413
1414         default:
1415                 break;
1416         }
1417
1418         return cursor;
1419 }
1420
1421 void
1422 Editor::choose_canvas_cursor_on_entry (ItemType type)
1423 {
1424         if (_drags->active()) {
1425                 return;
1426         }
1427
1428         Gdk::Cursor* cursor = which_canvas_cursor(type);
1429
1430         if (!_cursors->is_invalid (cursor)) {
1431                 // Push a new enter context
1432                 const EnterContext ctx = { type, CursorContext::create(*this, cursor) };
1433                 _enter_stack.push_back(ctx);
1434         }
1435 }
1436
1437 void
1438 Editor::update_all_enter_cursors ()
1439 {
1440         for (std::vector<EnterContext>::iterator i = _enter_stack.begin(); i != _enter_stack.end(); ++i) {
1441                 i->cursor_ctx->change(which_canvas_cursor(i->item_type));
1442         }
1443 }
1444
1445 double
1446 Editor::trackviews_height() const
1447 {
1448         if (!_trackview_group) {
1449                 return 0;
1450         }
1451
1452         return _visible_canvas_height - _trackview_group->canvas_origin().y;
1453 }