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