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