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