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