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