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