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