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