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