Show tempo lines under marker bars.
[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 (h_scroll_group);
127         CANVAS_DEBUG_NAME (time_line_group, "time line group");
128
129         _trackview_group = new ArdourCanvas::Container (hv_scroll_group);
130         CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
131         
132         // used as rubberband rect
133         rubberband_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
134         rubberband_rect->hide();
135
136         /* a group to hold stuff while it gets dragged around. Must be the
137          * uppermost (last) group with hv_scroll_group as a parent
138          */
139         _drag_motion_group = new ArdourCanvas::Container (hv_scroll_group);                                                                                                                                     
140         CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion");
141
142         /* TIME BAR CANVAS */
143         
144         _time_markers_group = new ArdourCanvas::Container (h_scroll_group);
145         CANVAS_DEBUG_NAME (_time_markers_group, "time bars");
146
147         cd_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, 0.0));
148         CANVAS_DEBUG_NAME (cd_marker_group, "cd marker group");
149         /* the vide is temporarily placed a the same location as the
150            cd_marker_group, but is moved later.
151         */
152         videotl_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0));
153         CANVAS_DEBUG_NAME (videotl_group, "videotl group");
154         marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, timebar_height + 1.0));
155         CANVAS_DEBUG_NAME (marker_group, "marker group");
156         transport_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 2.0) + 1.0));
157         CANVAS_DEBUG_NAME (transport_marker_group, "transport marker group");
158         range_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 3.0) + 1.0));
159         CANVAS_DEBUG_NAME (range_marker_group, "range marker group");
160         tempo_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 4.0) + 1.0));
161         CANVAS_DEBUG_NAME (tempo_group, "tempo group");
162         meter_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0));
163         CANVAS_DEBUG_NAME (meter_group, "meter group");
164
165         meter_bar = new ArdourCanvas::Rectangle (meter_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
166         CANVAS_DEBUG_NAME (meter_bar, "meter Bar");
167         meter_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
168
169         tempo_bar = new ArdourCanvas::Rectangle (tempo_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
170         CANVAS_DEBUG_NAME (tempo_bar, "Tempo  Bar");
171         tempo_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
172
173         range_marker_bar = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
174         CANVAS_DEBUG_NAME (range_marker_bar, "Range Marker Bar");
175         range_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
176
177         transport_marker_bar = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
178         CANVAS_DEBUG_NAME (transport_marker_bar, "transport Marker Bar");
179         transport_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
180
181         marker_bar = new ArdourCanvas::Rectangle (marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
182         CANVAS_DEBUG_NAME (marker_bar, "Marker Bar");
183         marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
184
185         cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
186         CANVAS_DEBUG_NAME (cd_marker_bar, "CD Marker Bar");
187         cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
188
189         ARDOUR_UI::instance()->video_timeline = new VideoTimeLine(this, videotl_group, (timebar_height * videotl_bar_height));
190         
191         cd_marker_bar_drag_rect = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
192         CANVAS_DEBUG_NAME (cd_marker_bar_drag_rect, "cd marker drag");
193         cd_marker_bar_drag_rect->set_outline (false);
194         cd_marker_bar_drag_rect->hide ();
195
196         range_bar_drag_rect = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
197         CANVAS_DEBUG_NAME (range_bar_drag_rect, "range drag");
198         range_bar_drag_rect->set_outline (false);
199         range_bar_drag_rect->hide ();
200
201         transport_bar_drag_rect = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
202         CANVAS_DEBUG_NAME (transport_bar_drag_rect, "transport drag");
203         transport_bar_drag_rect->set_outline (false);
204         transport_bar_drag_rect->hide ();
205
206         transport_punchin_line = new ArdourCanvas::Line (hv_scroll_group);
207         transport_punchin_line->set_x0 (0);
208         transport_punchin_line->set_y0 (0);
209         transport_punchin_line->set_x1 (0);
210         transport_punchin_line->set_y1 (ArdourCanvas::COORD_MAX);
211         transport_punchin_line->hide ();
212
213         transport_punchout_line  = new ArdourCanvas::Line (hv_scroll_group);
214         transport_punchout_line->set_x0 (0);
215         transport_punchout_line->set_y0 (0);
216         transport_punchout_line->set_x1 (0);
217         transport_punchout_line->set_y1 (ArdourCanvas::COORD_MAX);
218         transport_punchout_line->hide();
219
220         tempo_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_tempo_bar_event), tempo_bar));
221         meter_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_meter_bar_event), meter_bar));
222         marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_marker_bar_event), marker_bar));
223         cd_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_cd_marker_bar_event), cd_marker_bar));
224         videotl_group->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_videotl_bar_event), videotl_group));
225         range_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_range_marker_bar_event), range_marker_bar));
226         transport_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_transport_marker_bar_event), transport_marker_bar));
227
228         playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event);
229
230         if (logo_item) {
231                 logo_item->lower_to_bottom ();
232         }
233
234
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 Editor::EnterContext*
786 Editor::get_enter_context(ItemType type)
787 {
788         for (ssize_t i = _enter_stack.size() - 1; i >= 0; --i) {
789                 if (_enter_stack[i].item_type == type) {
790                         return &_enter_stack[i];
791                 }
792         }
793         return NULL;
794 }
795
796 bool
797 Editor::left_track_canvas (GdkEventCrossing */*ev*/)
798 {
799         DropDownKeys ();
800         within_track_canvas = false;
801         set_entered_track (0);
802         set_entered_regionview (0);
803         reset_canvas_action_sensitivity (false);
804         return false;
805 }
806
807 bool
808 Editor::entered_track_canvas (GdkEventCrossing */*ev*/)
809 {
810         within_track_canvas = true;
811         reset_canvas_action_sensitivity (true);
812         return FALSE;
813 }
814
815 void
816 Editor::ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top)
817 {
818         if (track.hidden()) {
819                 return;
820         }
821
822         /* compute visible area of trackview group, as offsets from top of
823          * trackview group.
824          */
825
826         double const current_view_min_y = vertical_adjustment.get_value();
827         double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size();
828
829         double const track_min_y = track.y_position ();
830         double const track_max_y = track.y_position () + track.effective_height ();
831
832         if (!at_top && 
833             (track_min_y >= current_view_min_y &&
834              track_max_y < current_view_max_y)) {
835                 /* already visible, and caller did not ask to place it at the
836                  * top of the track canvas
837                  */
838                 return;
839         }
840
841         double new_value;
842
843         if (at_top) {
844                 new_value = track_min_y;
845         } else {
846                 if (track_min_y < current_view_min_y) {
847                         // Track is above the current view
848                         new_value = track_min_y;
849                 } else if (track_max_y > current_view_max_y) {
850                         // Track is below the current view
851                         new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size();
852                 } else {
853                         new_value = track_min_y;
854                 }
855         }
856
857         vertical_adjustment.set_value(new_value);
858 }
859
860 /** Called when the main vertical_adjustment has changed */
861 void
862 Editor::tie_vertical_scrolling ()
863 {
864         if (pending_visual_change.idle_handler_id < 0) {
865                 _summary->set_overlays_dirty ();
866         }
867 }
868
869 void
870 Editor::set_horizontal_position (double p)
871 {
872         horizontal_adjustment.set_value (p);
873
874         leftmost_frame = (framepos_t) floor (p * samples_per_pixel);
875
876         update_fixed_rulers ();
877         redisplay_tempo (true);
878
879         if (pending_visual_change.idle_handler_id < 0) {
880                 _summary->set_overlays_dirty ();
881         }
882
883         update_video_timeline();
884 }
885
886 void
887 Editor::color_handler()
888 {
889         ArdourCanvas::Color base = ARDOUR_UI::config()->color ("ruler base");
890         ArdourCanvas::Color text = ARDOUR_UI::config()->color ("ruler text");
891         timecode_ruler->set_fill_color (base);
892         timecode_ruler->set_outline_color (text);
893         minsec_ruler->set_fill_color (base);
894         minsec_ruler->set_outline_color (text);
895         samples_ruler->set_fill_color (base);
896         samples_ruler->set_outline_color (text);
897         bbt_ruler->set_fill_color (base);
898         bbt_ruler->set_outline_color (text);
899         
900         playhead_cursor->set_color (ARDOUR_UI::config()->color ("play head"));
901
902         meter_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("meter bar", "marker bar"));
903         meter_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
904
905         tempo_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("tempo bar", "marker bar"));
906         tempo_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
907
908         marker_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("marker bar", "marker bar"));
909         marker_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
910
911         cd_marker_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("cd marker bar", "marker bar"));
912         cd_marker_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
913
914         range_marker_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("range marker bar", "marker bar"));
915         range_marker_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
916
917         transport_marker_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("transport marker bar", "marker bar"));
918         transport_marker_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
919
920         cd_marker_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag bar rect"));
921         cd_marker_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag bar rect"));
922
923         range_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag bar rect"));
924         range_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag bar rect"));
925
926         transport_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->color ("transport drag rect"));
927         transport_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->color ("transport drag rect"));
928
929         transport_loop_range_rect->set_fill_color (ARDOUR_UI::config()->color_mod ("transport loop rect", "loop rectangle"));
930         transport_loop_range_rect->set_outline_color (ARDOUR_UI::config()->color ("transport loop rect"));
931
932         transport_punch_range_rect->set_fill_color (ARDOUR_UI::config()->color ("transport punch rect"));
933         transport_punch_range_rect->set_outline_color (ARDOUR_UI::config()->color ("transport punch rect"));
934
935         transport_punchin_line->set_outline_color (ARDOUR_UI::config()->color ("punch line"));
936         transport_punchout_line->set_outline_color (ARDOUR_UI::config()->color ("punch line"));
937
938         rubberband_rect->set_outline_color (ARDOUR_UI::config()->color ("rubber band rect"));
939         rubberband_rect->set_fill_color (ARDOUR_UI::config()->color_mod ("rubber band rect", "selection rect"));
940
941         location_marker_color = ARDOUR_UI::config()->color ("location marker");
942         location_range_color = ARDOUR_UI::config()->color ("location range");
943         location_cd_marker_color = ARDOUR_UI::config()->color ("location cd marker");
944         location_loop_color = ARDOUR_UI::config()->color ("location loop");
945         location_punch_color = ARDOUR_UI::config()->color ("location punch");
946
947         refresh_location_display ();
948
949         /* redraw the whole thing */
950         _track_canvas->set_background_color (ARDOUR_UI::config()->color ("arrange base"));
951         _track_canvas->queue_draw ();
952         
953 /*
954         redisplay_tempo (true);
955
956         if (_session)
957               _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
958 */
959 }
960
961 double
962 Editor::horizontal_position () const
963 {
964         return sample_to_pixel (leftmost_frame);
965 }
966
967 bool
968 Editor::track_canvas_key_press (GdkEventKey*)
969 {
970         return false;
971 }
972
973 bool
974 Editor::track_canvas_key_release (GdkEventKey*)
975 {
976         return false;
977 }
978
979 double
980 Editor::clamp_verbose_cursor_x (double x)
981 {
982         if (x < 0) {
983                 x = 0;
984         } else {
985                 x = min (_visible_canvas_width - 200.0, x);
986         }
987         return x;
988 }
989
990 double
991 Editor::clamp_verbose_cursor_y (double y)
992 {
993         y = max (0.0, y);
994         y = min (_visible_canvas_height - 50, y);
995         return y;
996 }
997
998 ArdourCanvas::GtkCanvasViewport*
999 Editor::get_track_canvas() const
1000 {
1001         return _track_canvas_viewport;
1002 }
1003
1004 Gdk::Cursor*
1005 Editor::get_canvas_cursor () const
1006 {
1007         /* The top of the cursor stack is always the currently visible cursor. */
1008         return _cursor_stack.back();
1009 }
1010
1011 void
1012 Editor::set_canvas_cursor (Gdk::Cursor* cursor)
1013 {
1014         Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
1015
1016         if (win && cursor) {
1017                 win->set_cursor (*cursor);
1018         }
1019 }
1020
1021 size_t
1022 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
1023 {
1024         if (cursor) {
1025                 _cursor_stack.push_back (cursor);
1026                 set_canvas_cursor (cursor);
1027         }
1028         return _cursor_stack.size() - 1;
1029 }
1030
1031 void
1032 Editor::pop_canvas_cursor ()
1033 {
1034         while (true) {
1035                 if (_cursor_stack.size() <= 1) {
1036                         PBD::error << "attempt to pop default cursor" << endmsg;
1037                         return;
1038                 }
1039
1040                 _cursor_stack.pop_back();
1041                 if (_cursor_stack.back()) {
1042                         /* Popped to an existing cursor, we're done.  Otherwise, the
1043                            context that created this cursor has been destroyed, so we need
1044                            to skip to the next down the stack. */
1045                         set_canvas_cursor (_cursor_stack.back());
1046                         return;
1047                 }
1048         }
1049 }
1050
1051 Gdk::Cursor*
1052 Editor::which_grabber_cursor () const
1053 {
1054         Gdk::Cursor* c = _cursors->grabber;
1055
1056         switch (_edit_point) {
1057         case EditAtMouse:
1058                 c = _cursors->grabber_edit_point;
1059                 break;
1060         default:
1061                 boost::shared_ptr<Movable> m = _movable.lock();
1062                 if (m && m->locked()) {
1063                         c = _cursors->speaker;
1064                 }
1065                 break;
1066         }
1067
1068         return c;
1069 }
1070
1071 Gdk::Cursor*
1072 Editor::which_trim_cursor (bool left) const
1073 {
1074         if (!entered_regionview) {
1075                 return 0;
1076         }
1077
1078         Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
1079                 
1080         if (left) {
1081                 
1082                 if (ct & Trimmable::FrontTrimEarlier) {
1083                         return _cursors->left_side_trim;
1084                 } else {
1085                         return _cursors->left_side_trim_right_only;
1086                 }
1087         } else {
1088                 if (ct & Trimmable::EndTrimLater) {
1089                         return _cursors->right_side_trim;
1090                 } else {
1091                         return _cursors->right_side_trim_left_only;
1092                 }
1093         }
1094 }
1095
1096 Gdk::Cursor*
1097 Editor::which_mode_cursor () const
1098 {
1099         Gdk::Cursor* mode_cursor = 0;
1100
1101         switch (mouse_mode) {
1102         case MouseRange:
1103                 mode_cursor = _cursors->selector;
1104                 break;
1105
1106         case MouseCut:
1107                 mode_cursor = _cursors->scissors;
1108                 break;
1109                         
1110         case MouseObject:
1111         case MouseContent:
1112                 /* don't use mode cursor, pick a grabber cursor based on the item */
1113                 break;
1114
1115         case MouseDraw:
1116                 mode_cursor = _cursors->midi_pencil;
1117                 break;
1118
1119         case MouseTimeFX:
1120                 mode_cursor = _cursors->time_fx; // just use playhead
1121                 break;
1122
1123         case MouseAudition:
1124                 mode_cursor = _cursors->speaker;
1125                 break;
1126         }
1127
1128         /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
1129         if (get_smart_mode()) {
1130
1131                 double x, y;
1132                 get_pointer_position (x, y);
1133
1134                 if (x >= 0 && y >= 0) {
1135                         
1136                         vector<ArdourCanvas::Item const *> items;
1137
1138                         /* Note how we choose a specific scroll group to get
1139                          * items from. This could be problematic.
1140                          */
1141                         
1142                         hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
1143                         
1144                         // first item will be the upper most 
1145                         
1146                         if (!items.empty()) {
1147                                 const ArdourCanvas::Item* i = items.front();
1148                                 
1149                                 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
1150                                         pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
1151                                         if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
1152                                                 mode_cursor = _cursors->up_down;
1153                                         }
1154                                 }
1155                         }
1156                 }
1157         }
1158
1159         return mode_cursor;
1160 }
1161
1162 Gdk::Cursor*
1163 Editor::which_track_cursor () const
1164 {
1165         Gdk::Cursor* cursor = 0;
1166
1167         switch (_join_object_range_state) {
1168         case JOIN_OBJECT_RANGE_NONE:
1169         case JOIN_OBJECT_RANGE_OBJECT:
1170                 cursor = which_grabber_cursor ();
1171                 break;
1172         case JOIN_OBJECT_RANGE_RANGE:
1173                 cursor = _cursors->selector;
1174                 break;
1175         }
1176
1177         return cursor;
1178 }
1179
1180 Gdk::Cursor*
1181 Editor::which_canvas_cursor(ItemType type) const
1182 {
1183         Gdk::Cursor* cursor = which_mode_cursor ();
1184
1185         if ((mouse_mode == MouseObject || get_smart_mode ()) ||
1186             mouse_mode == MouseContent) {
1187
1188                 /* find correct cursor to use in object/smart mode */
1189
1190                 switch (type) {
1191                 case RegionItem:
1192                 /* We don't choose a cursor for these items on top of a region view,
1193                    because this would push a new context on the enter stack which
1194                    means switching the region context for things like smart mode
1195                    won't actualy change the cursor. */
1196                 // case RegionViewNameHighlight:
1197                 // case RegionViewName:
1198                 // case WaveItem:
1199                 case StreamItem:
1200                 case AutomationTrackItem:
1201                         cursor = which_track_cursor ();
1202                         break;
1203                 case PlayheadCursorItem:
1204                         switch (_edit_point) {
1205                         case EditAtMouse:
1206                                 cursor = _cursors->grabber_edit_point;
1207                                 break;
1208                         default:
1209                                 cursor = _cursors->grabber;
1210                                 break;
1211                         }
1212                         break;
1213                 case SelectionItem:
1214                         cursor = _cursors->selector;
1215                         break;
1216                 case ControlPointItem:
1217                         cursor = _cursors->fader;
1218                         break;
1219                 case GainLineItem:
1220                         cursor = which_track_cursor ();
1221                         break;
1222                 case AutomationLineItem:
1223                         cursor = _cursors->cross_hair;
1224                         break;
1225                 case StartSelectionTrimItem:
1226                         cursor = _cursors->left_side_trim;
1227                         break;
1228                 case EndSelectionTrimItem:
1229                         cursor = _cursors->right_side_trim;
1230                         break;
1231                 case FadeInItem:
1232                         cursor = _cursors->fade_in;
1233                         break;
1234                 case FadeInHandleItem:
1235                         cursor = _cursors->fade_in;
1236                         break;
1237                 case FadeInTrimHandleItem:
1238                         cursor = _cursors->fade_in;
1239                         break;
1240                 case FadeOutItem:
1241                         cursor = _cursors->fade_out;
1242                         break;
1243                 case FadeOutHandleItem:
1244                         cursor = _cursors->fade_out;
1245                         break;
1246                 case FadeOutTrimHandleItem:
1247                         cursor = _cursors->fade_out;
1248                         break;
1249                 case FeatureLineItem:
1250                         cursor = _cursors->cross_hair;
1251                         break;
1252                 case LeftFrameHandle:
1253                         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
1254                                 cursor = which_trim_cursor (true);  //alternatively, one could argue that we _should_ allow trims here, and disallow range selection
1255                         break;
1256                 case RightFrameHandle:
1257                         if ( effective_mouse_mode() == MouseObject )  //see above
1258                                 cursor = which_trim_cursor (false);
1259                         break;
1260                 case StartCrossFadeItem:
1261                         cursor = _cursors->fade_in;
1262                         break;
1263                 case EndCrossFadeItem:
1264                         cursor = _cursors->fade_out;
1265                         break;
1266                 case CrossfadeViewItem:
1267                         cursor = _cursors->cross_hair;
1268                         break;
1269                 case NoteItem:
1270                         cursor = _cursors->grabber_note;
1271                 default:
1272                         break;
1273                 }
1274
1275         } else if (mouse_mode == MouseDraw) {
1276                 
1277                 /* ControlPointItem is not really specific to region gain mode
1278                    but it is the same cursor so don't worry about this for now.
1279                    The result is that we'll see the fader cursor if we enter
1280                    non-region-gain-line control points while in MouseDraw
1281                    mode, even though we can't edit them in this mode.
1282                 */
1283
1284                 switch (type) {
1285                 case GainLineItem:
1286                 case ControlPointItem:
1287                         cursor = _cursors->fader;
1288                         break;
1289                 case NoteItem:
1290                         cursor = _cursors->grabber_note;
1291                 default:
1292                         break;
1293                 }
1294         }
1295
1296         switch (type) {
1297                 /* These items use the timebar cursor at all times */
1298         case TimecodeRulerItem:
1299         case MinsecRulerItem:
1300         case BBTRulerItem:
1301         case SamplesRulerItem:
1302                 cursor = _cursors->timebar;
1303                 break;
1304
1305                 /* These items use the grabber cursor at all times */
1306         case MeterMarkerItem:
1307         case TempoMarkerItem:
1308         case MeterBarItem:
1309         case TempoBarItem:
1310         case MarkerItem:
1311         case MarkerBarItem:
1312         case RangeMarkerBarItem:
1313         case CdMarkerBarItem:
1314         case VideoBarItem:
1315         case TransportMarkerBarItem:
1316         case DropZoneItem:
1317                 cursor = which_grabber_cursor();
1318                 break;
1319
1320         default:
1321                 break;
1322         }
1323
1324         return cursor;
1325 }
1326
1327 void
1328 Editor::choose_canvas_cursor_on_entry (ItemType type)
1329 {
1330         if (_drags->active()) {
1331                 return;
1332         }
1333
1334         Gdk::Cursor* cursor = which_canvas_cursor(type);
1335
1336         if (cursor) {
1337                 // Push a new enter context
1338                 const EnterContext ctx = { type, CursorContext::create(*this, cursor) };
1339                 _enter_stack.push_back(ctx);
1340         }
1341 }
1342
1343 void
1344 Editor::update_all_enter_cursors ()
1345 {
1346         for (std::vector<EnterContext>::iterator i = _enter_stack.begin(); i != _enter_stack.end(); ++i) {
1347                 i->cursor_ctx->change(which_canvas_cursor(i->item_type));
1348         }
1349 }
1350
1351 double
1352 Editor::trackviews_height() const
1353 {
1354         if (!_trackview_group) {
1355                 return 0;
1356         }
1357
1358         return _visible_canvas_height - _trackview_group->canvas_origin().y;
1359 }