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