working compilation for new color code
[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()->get_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()->get_RulerBase();
875         ArdourCanvas::Color text = ARDOUR_UI::config()->get_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()->get_PlayHead());
886
887         meter_bar->set_fill_color (ARDOUR_UI::config()->get_MeterBar());
888         meter_bar->set_outline_color (ARDOUR_UI::config()->get_MarkerBarSeparator());
889
890         tempo_bar->set_fill_color (ARDOUR_UI::config()->get_TempoBar());
891         tempo_bar->set_outline_color (ARDOUR_UI::config()->get_MarkerBarSeparator());
892
893         marker_bar->set_fill_color (ARDOUR_UI::config()->get_MarkerBar());
894         marker_bar->set_outline_color (ARDOUR_UI::config()->get_MarkerBarSeparator());
895
896         cd_marker_bar->set_fill_color (ARDOUR_UI::config()->get_CDMarkerBar());
897         cd_marker_bar->set_outline_color (ARDOUR_UI::config()->get_MarkerBarSeparator());
898
899         range_marker_bar->set_fill_color (ARDOUR_UI::config()->get_RangeMarkerBar());
900         range_marker_bar->set_outline_color (ARDOUR_UI::config()->get_MarkerBarSeparator());
901
902         transport_marker_bar->set_fill_color (ARDOUR_UI::config()->get_TransportMarkerBar());
903         transport_marker_bar->set_outline_color (ARDOUR_UI::config()->get_MarkerBarSeparator());
904
905         cd_marker_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_RangeDragBarRect());
906         cd_marker_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_RangeDragBarRect());
907
908         range_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_RangeDragBarRect());
909         range_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_RangeDragBarRect());
910
911         transport_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_TransportDragRect());
912         transport_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_TransportDragRect());
913
914         transport_loop_range_rect->set_fill_color (ARDOUR_UI::config()->get_TransportLoopRect());
915         transport_loop_range_rect->set_outline_color (ARDOUR_UI::config()->get_TransportLoopRect());
916
917         transport_punch_range_rect->set_fill_color (ARDOUR_UI::config()->get_TransportPunchRect());
918         transport_punch_range_rect->set_outline_color (ARDOUR_UI::config()->get_TransportPunchRect());
919
920         transport_punchin_line->set_outline_color (ARDOUR_UI::config()->get_PunchLine());
921         transport_punchout_line->set_outline_color (ARDOUR_UI::config()->get_PunchLine());
922
923         rubberband_rect->set_outline_color (ARDOUR_UI::config()->get_RubberBandRect());
924         rubberband_rect->set_fill_color ((guint32) ARDOUR_UI::config()->get_RubberBandRect());
925
926         location_marker_color = ARDOUR_UI::config()->get_LocationMarker();
927         location_range_color = ARDOUR_UI::config()->get_LocationRange();
928         location_cd_marker_color = ARDOUR_UI::config()->get_LocationCDMarker();
929         location_loop_color = ARDOUR_UI::config()->get_LocationLoop();
930         location_punch_color = ARDOUR_UI::config()->get_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 MouseGain:
1117                 mode_cursor = _cursors->cross_hair;
1118                 break;
1119
1120         case MouseTimeFX:
1121                 mode_cursor = _cursors->time_fx; // just use playhead
1122                 break;
1123
1124         case MouseAudition:
1125                 mode_cursor = _cursors->speaker;
1126                 break;
1127         }
1128
1129         /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
1130         if (!_internal_editing && get_smart_mode() ) {
1131
1132                 double x, y;
1133                 get_pointer_position (x, y);
1134
1135                 if (x >= 0 && y >= 0) {
1136                         
1137                         vector<ArdourCanvas::Item const *> items;
1138
1139                         /* Note how we choose a specific scroll group to get
1140                          * items from. This could be problematic.
1141                          */
1142                         
1143                         hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
1144                         
1145                         // first item will be the upper most 
1146                         
1147                         if (!items.empty()) {
1148                                 const ArdourCanvas::Item* i = items.front();
1149                                 
1150                                 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
1151                                         pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
1152                                         if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
1153                                                 mode_cursor = _cursors->up_down;
1154                                         }
1155                                 }
1156                         }
1157                 }
1158         }
1159
1160         return mode_cursor;
1161 }
1162
1163 Gdk::Cursor*
1164 Editor::which_track_cursor () const
1165 {
1166         Gdk::Cursor* cursor = 0;
1167
1168         assert (mouse_mode == MouseObject || get_smart_mode());
1169
1170         if (!_internal_editing) {
1171                 switch (_join_object_range_state) {
1172                 case JOIN_OBJECT_RANGE_NONE:
1173                 case JOIN_OBJECT_RANGE_OBJECT:
1174                         cursor = which_grabber_cursor ();
1175                         break;
1176                 case JOIN_OBJECT_RANGE_RANGE:
1177                         cursor = _cursors->selector;
1178                         break;
1179                 }
1180         }
1181
1182         return cursor;
1183 }
1184
1185 bool
1186 Editor::reset_canvas_cursor ()
1187 {
1188         if (!is_drawable()) {
1189                 return false;
1190         }
1191
1192         Gdk::Cursor* cursor = which_mode_cursor ();
1193
1194         if (!cursor) {
1195                 cursor = which_grabber_cursor ();
1196         }
1197                 
1198         if (cursor) {
1199                 set_canvas_cursor (cursor);
1200                 return true;
1201         }
1202
1203         return false;
1204 }
1205
1206 void
1207 Editor::choose_canvas_cursor_on_entry (GdkEventCrossing* /*event*/, ItemType type)
1208 {
1209         Gdk::Cursor* cursor = 0;
1210
1211         if (_drags->active()) {
1212                 return;
1213         }
1214
1215         cursor = which_mode_cursor ();
1216
1217         if (mouse_mode == MouseObject || get_smart_mode ()) {
1218
1219                 /* find correct cursor to use in object/smart mode */
1220
1221                 switch (type) {
1222                 case RegionItem:
1223                 case RegionViewNameHighlight:
1224                 case RegionViewName:
1225                 case WaveItem:
1226                 case StreamItem:
1227                 case AutomationTrackItem:
1228                         cursor = which_track_cursor ();
1229                         break;
1230                 case PlayheadCursorItem:
1231                         switch (_edit_point) {
1232                         case EditAtMouse:
1233                                 cursor = _cursors->grabber_edit_point;
1234                                 break;
1235                         default:
1236                                 cursor = _cursors->grabber;
1237                                 break;
1238                         }
1239                         break;
1240                 case SelectionItem:
1241                         cursor = _cursors->selector;
1242                         break;
1243                 case ControlPointItem:
1244                         cursor = _cursors->fader;
1245                         break;
1246                 case GainLineItem:
1247                         cursor = which_track_cursor ();
1248                         break;
1249                 case AutomationLineItem:
1250                         cursor = _cursors->cross_hair;
1251                         break;
1252                 case StartSelectionTrimItem:
1253                         cursor = _cursors->left_side_trim;
1254                         break;
1255                 case EndSelectionTrimItem:
1256                         cursor = _cursors->right_side_trim;
1257                         break;
1258                 case FadeInItem:
1259                         cursor = _cursors->fade_in;
1260                         break;
1261                 case FadeInHandleItem:
1262                         cursor = _cursors->fade_in;
1263                         break;
1264                 case FadeInTrimHandleItem:
1265                         cursor = _cursors->fade_in;
1266                         break;
1267                 case FadeOutItem:
1268                         cursor = _cursors->fade_out;
1269                         break;
1270                 case FadeOutHandleItem:
1271                         cursor = _cursors->fade_out;
1272                         break;
1273                 case FadeOutTrimHandleItem:
1274                         cursor = _cursors->fade_out;
1275                         break;
1276                 case NoteItem:
1277                         cursor = which_grabber_cursor();
1278                         break;
1279                 case FeatureLineItem:
1280                         cursor = _cursors->cross_hair;
1281                         break;
1282                 case LeftFrameHandle:
1283                         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
1284                                 cursor = which_trim_cursor (true);  //alternatively, one could argue that we _should_ allow trims here, and disallow range selection
1285                         break;
1286                 case RightFrameHandle:
1287                         if ( effective_mouse_mode() == MouseObject )  //see above
1288                                 cursor = which_trim_cursor (false);
1289                         break;
1290                 case StartCrossFadeItem:
1291                         cursor = _cursors->fade_in;
1292                         break;
1293                 case EndCrossFadeItem:
1294                         cursor = _cursors->fade_out;
1295                         break;
1296                 case CrossfadeViewItem:
1297                         cursor = _cursors->cross_hair;
1298                         break;
1299                 default:
1300                         break;
1301                 }
1302
1303         } else if (mouse_mode == MouseGain) {
1304                 
1305                 /* ControlPointItem is not really specific to region gain mode
1306                    but it is the same cursor so don't worry about this for now.
1307                    The result is that we'll see the fader cursor if we enter
1308                    non-region-gain-line control points while in MouseGain
1309                    mode, even though we can't edit them in this mode.
1310                 */
1311
1312                 switch (type) {
1313                 case GainLineItem:
1314                 case ControlPointItem:
1315                         cursor = _cursors->fader;
1316                         break;
1317                 default:
1318                         break;
1319                 }
1320         }
1321
1322         switch (type) {
1323                 /* These items use the timebar cursor at all times */
1324         case TimecodeRulerItem:
1325         case MinsecRulerItem:
1326         case BBTRulerItem:
1327         case SamplesRulerItem:
1328                 cursor = _cursors->timebar;
1329                 break;
1330
1331                 /* These items use the grabber cursor at all times */
1332         case MeterMarkerItem:
1333         case TempoMarkerItem:
1334         case MeterBarItem:
1335         case TempoBarItem:
1336         case MarkerItem:
1337         case MarkerBarItem:
1338         case RangeMarkerBarItem:
1339         case CdMarkerBarItem:
1340         case VideoBarItem:
1341         case TransportMarkerBarItem:
1342         case DropZoneItem:
1343                 cursor = which_grabber_cursor();
1344                 break;
1345
1346         default:
1347                 break;
1348         }
1349
1350         if (cursor) {
1351                 set_canvas_cursor (cursor, true);
1352         }
1353 }
1354
1355 double
1356 Editor::trackviews_height() const
1357 {
1358         if (!_trackview_group) {
1359                 return 0;
1360         }
1361
1362         return _visible_canvas_height - _trackview_group->canvas_origin().y;
1363 }