Remove unused vertical scroll group.
[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 "pbd/error.h"
31
32 #include "canvas/canvas.h"
33 #include "canvas/rectangle.h"
34 #include "canvas/pixbuf.h"
35 #include "canvas/scroll_group.h"
36 #include "canvas/text.h"
37 #include "canvas/debug.h"
38
39 #include "ardour_ui.h"
40 #include "automation_time_axis.h"
41 #include "editor.h"
42 #include "global_signals.h"
43 #include "editing.h"
44 #include "rgb_macros.h"
45 #include "utils.h"
46 #include "audio_time_axis.h"
47 #include "editor_drag.h"
48 #include "region_view.h"
49 #include "editor_group_tabs.h"
50 #include "editor_summary.h"
51 #include "video_timeline.h"
52 #include "keyboard.h"
53 #include "editor_cursors.h"
54 #include "mouse_cursors.h"
55 #include "verbose_cursor.h"
56
57 #include "i18n.h"
58
59 using namespace std;
60 using namespace ARDOUR;
61 using namespace ARDOUR_UI_UTILS;
62 using namespace PBD;
63 using namespace Gtk;
64 using namespace Glib;
65 using namespace Gtkmm2ext;
66 using namespace Editing;
67
68 void
69 Editor::initialize_canvas ()
70 {
71         _track_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, vertical_adjustment);
72         _track_canvas = _track_canvas_viewport->canvas ();
73
74         _track_canvas->set_background_color (ARDOUR_UI::config()->color ("arrange base"));
75
76         /* scroll group for items that should not automatically scroll
77          *  (e.g verbose cursor). It shares the canvas coordinate space.
78         */
79         no_scroll_group = new ArdourCanvas::Container (_track_canvas->root());
80
81         ArdourCanvas::ScrollGroup* hsg; 
82         ArdourCanvas::ScrollGroup* hg;
83
84         hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), 
85                                                                ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically|
86                                                                                                              ArdourCanvas::ScrollGroup::ScrollsHorizontally));
87         CANVAS_DEBUG_NAME (hv_scroll_group, "canvas hv scroll");
88         _track_canvas->add_scroller (*hsg);
89
90         h_scroll_group = hg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
91         CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll");
92         _track_canvas->add_scroller (*hg);
93
94         _verbose_cursor = new VerboseCursor (this);
95
96         /* on the bottom, an image */
97
98         if (Profile->get_sae()) {
99                 Image img (::get_icon (X_("saelogo")));
100                 // logo_item = new ArdourCanvas::Pixbuf (_track_canvas->root(), 0.0, 0.0, img.get_pixbuf());
101                 // logo_item->property_height_in_pixels() = true;
102                 // logo_item->property_width_in_pixels() = true;
103                 // logo_item->property_height_set() = true;
104                 // logo_item->property_width_set() = true;
105                 // logo_item->show ();
106         }
107
108         /*a group to hold global rects like punch/loop indicators */
109         global_rect_group = new ArdourCanvas::Container (hv_scroll_group);
110         CANVAS_DEBUG_NAME (global_rect_group, "global rect group");
111
112         transport_loop_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
113         CANVAS_DEBUG_NAME (transport_loop_range_rect, "loop rect");
114         transport_loop_range_rect->hide();
115
116         transport_punch_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
117         CANVAS_DEBUG_NAME (transport_punch_range_rect, "punch rect");
118         transport_punch_range_rect->hide();
119
120         /*a group to hold time (measure) lines */
121         time_line_group = new ArdourCanvas::Container (h_scroll_group);
122         CANVAS_DEBUG_NAME (time_line_group, "time line group");
123
124         _trackview_group = new ArdourCanvas::Container (hv_scroll_group);
125         CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
126         
127         // used as rubberband rect
128         rubberband_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
129         rubberband_rect->hide();
130
131         /* a group to hold stuff while it gets dragged around. Must be the
132          * uppermost (last) group with hv_scroll_group as a parent
133          */
134         _drag_motion_group = new ArdourCanvas::Container (hv_scroll_group);                                                                                                                                     
135         CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion");
136
137         /* TIME BAR CANVAS */
138         
139         _time_markers_group = new ArdourCanvas::Container (h_scroll_group);
140         CANVAS_DEBUG_NAME (_time_markers_group, "time bars");
141
142         cd_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, 0.0));
143         CANVAS_DEBUG_NAME (cd_marker_group, "cd marker group");
144         /* the vide is temporarily placed a the same location as the
145            cd_marker_group, but is moved later.
146         */
147         videotl_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0));
148         CANVAS_DEBUG_NAME (videotl_group, "videotl group");
149         marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, timebar_height + 1.0));
150         CANVAS_DEBUG_NAME (marker_group, "marker group");
151         transport_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 2.0) + 1.0));
152         CANVAS_DEBUG_NAME (transport_marker_group, "transport marker group");
153         range_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 3.0) + 1.0));
154         CANVAS_DEBUG_NAME (range_marker_group, "range marker group");
155         tempo_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 4.0) + 1.0));
156         CANVAS_DEBUG_NAME (tempo_group, "tempo group");
157         meter_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0));
158         CANVAS_DEBUG_NAME (meter_group, "meter group");
159
160         meter_bar = new ArdourCanvas::Rectangle (meter_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
161         CANVAS_DEBUG_NAME (meter_bar, "meter Bar");
162         meter_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
163
164         tempo_bar = new ArdourCanvas::Rectangle (tempo_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
165         CANVAS_DEBUG_NAME (tempo_bar, "Tempo  Bar");
166         tempo_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
167
168         range_marker_bar = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
169         CANVAS_DEBUG_NAME (range_marker_bar, "Range Marker Bar");
170         range_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
171
172         transport_marker_bar = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
173         CANVAS_DEBUG_NAME (transport_marker_bar, "transport Marker Bar");
174         transport_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
175
176         marker_bar = new ArdourCanvas::Rectangle (marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
177         CANVAS_DEBUG_NAME (marker_bar, "Marker Bar");
178         marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
179
180         cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
181         CANVAS_DEBUG_NAME (cd_marker_bar, "CD Marker Bar");
182         cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
183
184         ARDOUR_UI::instance()->video_timeline = new VideoTimeLine(this, videotl_group, (timebar_height * videotl_bar_height));
185         
186         cd_marker_bar_drag_rect = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
187         CANVAS_DEBUG_NAME (cd_marker_bar_drag_rect, "cd marker drag");
188         cd_marker_bar_drag_rect->set_outline (false);
189         cd_marker_bar_drag_rect->hide ();
190
191         range_bar_drag_rect = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
192         CANVAS_DEBUG_NAME (range_bar_drag_rect, "range drag");
193         range_bar_drag_rect->set_outline (false);
194         range_bar_drag_rect->hide ();
195
196         transport_bar_drag_rect = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
197         CANVAS_DEBUG_NAME (transport_bar_drag_rect, "transport drag");
198         transport_bar_drag_rect->set_outline (false);
199         transport_bar_drag_rect->hide ();
200
201         transport_punchin_line = new ArdourCanvas::Line (hv_scroll_group);
202         transport_punchin_line->set_x0 (0);
203         transport_punchin_line->set_y0 (0);
204         transport_punchin_line->set_x1 (0);
205         transport_punchin_line->set_y1 (ArdourCanvas::COORD_MAX);
206         transport_punchin_line->hide ();
207
208         transport_punchout_line  = new ArdourCanvas::Line (hv_scroll_group);
209         transport_punchout_line->set_x0 (0);
210         transport_punchout_line->set_y0 (0);
211         transport_punchout_line->set_x1 (0);
212         transport_punchout_line->set_y1 (ArdourCanvas::COORD_MAX);
213         transport_punchout_line->hide();
214
215         tempo_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_tempo_bar_event), tempo_bar));
216         meter_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_meter_bar_event), meter_bar));
217         marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_marker_bar_event), marker_bar));
218         cd_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_cd_marker_bar_event), cd_marker_bar));
219         videotl_group->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_videotl_bar_event), videotl_group));
220         range_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_range_marker_bar_event), range_marker_bar));
221         transport_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_transport_marker_bar_event), transport_marker_bar));
222
223         playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event);
224
225         if (logo_item) {
226                 logo_item->lower_to_bottom ();
227         }
228
229         hv_scroll_group->raise_to_top ();
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 = { 0, 0 };
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 (!_cursor_stack.empty()) {
369                 set_canvas_cursor (get_canvas_cursor());
370         } else {
371                 PBD::error << "cursor stack is empty" << endmsg;
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() || ARDOUR_UI::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() || ARDOUR_UI::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 (!ARDOUR_UI::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 Editor::EnterContext*
782 Editor::get_enter_context(ItemType type)
783 {
784         for (ssize_t i = _enter_stack.size() - 1; i >= 0; --i) {
785                 if (_enter_stack[i].item_type == type) {
786                         return &_enter_stack[i];
787                 }
788         }
789         return NULL;
790 }
791
792 bool
793 Editor::left_track_canvas (GdkEventCrossing */*ev*/)
794 {
795         DropDownKeys ();
796         within_track_canvas = false;
797         set_entered_track (0);
798         set_entered_regionview (0);
799         reset_canvas_action_sensitivity (false);
800         return false;
801 }
802
803 bool
804 Editor::entered_track_canvas (GdkEventCrossing */*ev*/)
805 {
806         within_track_canvas = true;
807         reset_canvas_action_sensitivity (true);
808         return FALSE;
809 }
810
811 void
812 Editor::ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top)
813 {
814         if (track.hidden()) {
815                 return;
816         }
817
818         /* compute visible area of trackview group, as offsets from top of
819          * trackview group.
820          */
821
822         double const current_view_min_y = vertical_adjustment.get_value();
823         double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size();
824
825         double const track_min_y = track.y_position ();
826         double const track_max_y = track.y_position () + track.effective_height ();
827
828         if (!at_top && 
829             (track_min_y >= current_view_min_y &&
830              track_max_y < current_view_max_y)) {
831                 /* already visible, and caller did not ask to place it at the
832                  * top of the track canvas
833                  */
834                 return;
835         }
836
837         double new_value;
838
839         if (at_top) {
840                 new_value = track_min_y;
841         } else {
842                 if (track_min_y < current_view_min_y) {
843                         // Track is above the current view
844                         new_value = track_min_y;
845                 } else if (track_max_y > current_view_max_y) {
846                         // Track is below the current view
847                         new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size();
848                 } else {
849                         new_value = track_min_y;
850                 }
851         }
852
853         vertical_adjustment.set_value(new_value);
854 }
855
856 /** Called when the main vertical_adjustment has changed */
857 void
858 Editor::tie_vertical_scrolling ()
859 {
860         if (pending_visual_change.idle_handler_id < 0) {
861                 _summary->set_overlays_dirty ();
862         }
863 }
864
865 void
866 Editor::set_horizontal_position (double p)
867 {
868         horizontal_adjustment.set_value (p);
869
870         leftmost_frame = (framepos_t) floor (p * samples_per_pixel);
871
872         update_fixed_rulers ();
873         redisplay_tempo (true);
874
875         if (pending_visual_change.idle_handler_id < 0) {
876                 _summary->set_overlays_dirty ();
877         }
878
879         update_video_timeline();
880 }
881
882 void
883 Editor::color_handler()
884 {
885         ArdourCanvas::Color base = ARDOUR_UI::config()->color ("ruler base");
886         ArdourCanvas::Color text = ARDOUR_UI::config()->color ("ruler text");
887         timecode_ruler->set_fill_color (base);
888         timecode_ruler->set_outline_color (text);
889         minsec_ruler->set_fill_color (base);
890         minsec_ruler->set_outline_color (text);
891         samples_ruler->set_fill_color (base);
892         samples_ruler->set_outline_color (text);
893         bbt_ruler->set_fill_color (base);
894         bbt_ruler->set_outline_color (text);
895         
896         playhead_cursor->set_color (ARDOUR_UI::config()->color ("play head"));
897
898         meter_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("meter bar", "marker bar"));
899         meter_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
900
901         tempo_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("tempo bar", "marker bar"));
902         tempo_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
903
904         marker_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("marker bar", "marker bar"));
905         marker_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
906
907         cd_marker_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("cd marker bar", "marker bar"));
908         cd_marker_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
909
910         range_marker_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("range marker bar", "marker bar"));
911         range_marker_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
912
913         transport_marker_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("transport marker bar", "marker bar"));
914         transport_marker_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
915
916         cd_marker_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag bar rect"));
917         cd_marker_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag bar rect"));
918
919         range_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag bar rect"));
920         range_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag bar rect"));
921
922         transport_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->color ("transport drag rect"));
923         transport_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->color ("transport drag rect"));
924
925         transport_loop_range_rect->set_fill_color (ARDOUR_UI::config()->color_mod ("transport loop rect", "loop rectangle"));
926         transport_loop_range_rect->set_outline_color (ARDOUR_UI::config()->color ("transport loop rect"));
927
928         transport_punch_range_rect->set_fill_color (ARDOUR_UI::config()->color ("transport punch rect"));
929         transport_punch_range_rect->set_outline_color (ARDOUR_UI::config()->color ("transport punch rect"));
930
931         transport_punchin_line->set_outline_color (ARDOUR_UI::config()->color ("punch line"));
932         transport_punchout_line->set_outline_color (ARDOUR_UI::config()->color ("punch line"));
933
934         rubberband_rect->set_outline_color (ARDOUR_UI::config()->color ("rubber band rect"));
935         rubberband_rect->set_fill_color (ARDOUR_UI::config()->color_mod ("rubber band rect", "selection rect"));
936
937         location_marker_color = ARDOUR_UI::config()->color ("location marker");
938         location_range_color = ARDOUR_UI::config()->color ("location range");
939         location_cd_marker_color = ARDOUR_UI::config()->color ("location cd marker");
940         location_loop_color = ARDOUR_UI::config()->color ("location loop");
941         location_punch_color = ARDOUR_UI::config()->color ("location punch");
942
943         refresh_location_display ();
944
945         /* redraw the whole thing */
946         _track_canvas->set_background_color (ARDOUR_UI::config()->color ("arrange base"));
947         _track_canvas->queue_draw ();
948         
949 /*
950         redisplay_tempo (true);
951
952         if (_session)
953               _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
954 */
955 }
956
957 double
958 Editor::horizontal_position () const
959 {
960         return sample_to_pixel (leftmost_frame);
961 }
962
963 bool
964 Editor::track_canvas_key_press (GdkEventKey*)
965 {
966         return false;
967 }
968
969 bool
970 Editor::track_canvas_key_release (GdkEventKey*)
971 {
972         return false;
973 }
974
975 double
976 Editor::clamp_verbose_cursor_x (double x)
977 {
978         if (x < 0) {
979                 x = 0;
980         } else {
981                 x = min (_visible_canvas_width - 200.0, x);
982         }
983         return x;
984 }
985
986 double
987 Editor::clamp_verbose_cursor_y (double y)
988 {
989         y = max (0.0, y);
990         y = min (_visible_canvas_height - 50, y);
991         return y;
992 }
993
994 ArdourCanvas::GtkCanvasViewport*
995 Editor::get_track_canvas() const
996 {
997         return _track_canvas_viewport;
998 }
999
1000 Gdk::Cursor*
1001 Editor::get_canvas_cursor () const
1002 {
1003         /* The top of the cursor stack is always the currently visible cursor. */
1004         return _cursor_stack.back();
1005 }
1006
1007 void
1008 Editor::set_canvas_cursor (Gdk::Cursor* cursor)
1009 {
1010         Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
1011
1012         if (win && cursor) {
1013                 win->set_cursor (*cursor);
1014         }
1015 }
1016
1017 size_t
1018 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
1019 {
1020         if (cursor) {
1021                 _cursor_stack.push_back (cursor);
1022                 set_canvas_cursor (cursor);
1023         }
1024         return _cursor_stack.size() - 1;
1025 }
1026
1027 void
1028 Editor::pop_canvas_cursor ()
1029 {
1030         while (true) {
1031                 if (_cursor_stack.size() <= 1) {
1032                         PBD::error << "attempt to pop default cursor" << endmsg;
1033                         return;
1034                 }
1035
1036                 _cursor_stack.pop_back();
1037                 if (_cursor_stack.back()) {
1038                         /* Popped to an existing cursor, we're done.  Otherwise, the
1039                            context that created this cursor has been destroyed, so we need
1040                            to skip to the next down the stack. */
1041                         set_canvas_cursor (_cursor_stack.back());
1042                         return;
1043                 }
1044         }
1045 }
1046
1047 Gdk::Cursor*
1048 Editor::which_grabber_cursor () const
1049 {
1050         Gdk::Cursor* c = _cursors->grabber;
1051
1052         switch (_edit_point) {
1053         case EditAtMouse:
1054                 c = _cursors->grabber_edit_point;
1055                 break;
1056         default:
1057                 boost::shared_ptr<Movable> m = _movable.lock();
1058                 if (m && m->locked()) {
1059                         c = _cursors->speaker;
1060                 }
1061                 break;
1062         }
1063
1064         return c;
1065 }
1066
1067 Gdk::Cursor*
1068 Editor::which_trim_cursor (bool left) const
1069 {
1070         if (!entered_regionview) {
1071                 return 0;
1072         }
1073
1074         Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
1075                 
1076         if (left) {
1077                 
1078                 if (ct & Trimmable::FrontTrimEarlier) {
1079                         return _cursors->left_side_trim;
1080                 } else {
1081                         return _cursors->left_side_trim_right_only;
1082                 }
1083         } else {
1084                 if (ct & Trimmable::EndTrimLater) {
1085                         return _cursors->right_side_trim;
1086                 } else {
1087                         return _cursors->right_side_trim_left_only;
1088                 }
1089         }
1090 }
1091
1092 Gdk::Cursor*
1093 Editor::which_mode_cursor () const
1094 {
1095         Gdk::Cursor* mode_cursor = 0;
1096
1097         switch (mouse_mode) {
1098         case MouseRange:
1099                 mode_cursor = _cursors->selector;
1100                 break;
1101
1102         case MouseCut:
1103                 mode_cursor = _cursors->scissors;
1104                 break;
1105                         
1106         case MouseObject:
1107         case MouseContent:
1108                 /* don't use mode cursor, pick a grabber cursor based on the item */
1109                 break;
1110
1111         case MouseDraw:
1112                 mode_cursor = _cursors->midi_pencil;
1113                 break;
1114
1115         case MouseTimeFX:
1116                 mode_cursor = _cursors->time_fx; // just use playhead
1117                 break;
1118
1119         case MouseAudition:
1120                 mode_cursor = _cursors->speaker;
1121                 break;
1122         }
1123
1124         /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
1125         if (get_smart_mode()) {
1126
1127                 double x, y;
1128                 get_pointer_position (x, y);
1129
1130                 if (x >= 0 && y >= 0) {
1131                         
1132                         vector<ArdourCanvas::Item const *> items;
1133
1134                         /* Note how we choose a specific scroll group to get
1135                          * items from. This could be problematic.
1136                          */
1137                         
1138                         hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
1139                         
1140                         // first item will be the upper most 
1141                         
1142                         if (!items.empty()) {
1143                                 const ArdourCanvas::Item* i = items.front();
1144                                 
1145                                 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
1146                                         pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
1147                                         if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
1148                                                 mode_cursor = _cursors->up_down;
1149                                         }
1150                                 }
1151                         }
1152                 }
1153         }
1154
1155         return mode_cursor;
1156 }
1157
1158 Gdk::Cursor*
1159 Editor::which_track_cursor () const
1160 {
1161         Gdk::Cursor* cursor = 0;
1162
1163         switch (_join_object_range_state) {
1164         case JOIN_OBJECT_RANGE_NONE:
1165         case JOIN_OBJECT_RANGE_OBJECT:
1166                 cursor = which_grabber_cursor ();
1167                 break;
1168         case JOIN_OBJECT_RANGE_RANGE:
1169                 cursor = _cursors->selector;
1170                 break;
1171         }
1172
1173         return cursor;
1174 }
1175
1176 Gdk::Cursor*
1177 Editor::which_canvas_cursor(ItemType type) const
1178 {
1179         Gdk::Cursor* cursor = which_mode_cursor ();
1180
1181         if ((mouse_mode == MouseObject || get_smart_mode ()) ||
1182             mouse_mode == MouseContent) {
1183
1184                 /* find correct cursor to use in object/smart mode */
1185
1186                 switch (type) {
1187                 case RegionItem:
1188                 /* We don't choose a cursor for these items on top of a region view,
1189                    because this would push a new context on the enter stack which
1190                    means switching the region context for things like smart mode
1191                    won't actualy change the cursor. */
1192                 // case RegionViewNameHighlight:
1193                 // case RegionViewName:
1194                 // case WaveItem:
1195                 case StreamItem:
1196                 case AutomationTrackItem:
1197                         cursor = which_track_cursor ();
1198                         break;
1199                 case PlayheadCursorItem:
1200                         switch (_edit_point) {
1201                         case EditAtMouse:
1202                                 cursor = _cursors->grabber_edit_point;
1203                                 break;
1204                         default:
1205                                 cursor = _cursors->grabber;
1206                                 break;
1207                         }
1208                         break;
1209                 case SelectionItem:
1210                         cursor = _cursors->selector;
1211                         break;
1212                 case ControlPointItem:
1213                         cursor = _cursors->fader;
1214                         break;
1215                 case GainLineItem:
1216                         cursor = which_track_cursor ();
1217                         break;
1218                 case AutomationLineItem:
1219                         cursor = _cursors->cross_hair;
1220                         break;
1221                 case StartSelectionTrimItem:
1222                         cursor = _cursors->left_side_trim;
1223                         break;
1224                 case EndSelectionTrimItem:
1225                         cursor = _cursors->right_side_trim;
1226                         break;
1227                 case FadeInItem:
1228                         cursor = _cursors->fade_in;
1229                         break;
1230                 case FadeInHandleItem:
1231                         cursor = _cursors->fade_in;
1232                         break;
1233                 case FadeInTrimHandleItem:
1234                         cursor = _cursors->fade_in;
1235                         break;
1236                 case FadeOutItem:
1237                         cursor = _cursors->fade_out;
1238                         break;
1239                 case FadeOutHandleItem:
1240                         cursor = _cursors->fade_out;
1241                         break;
1242                 case FadeOutTrimHandleItem:
1243                         cursor = _cursors->fade_out;
1244                         break;
1245                 case FeatureLineItem:
1246                         cursor = _cursors->cross_hair;
1247                         break;
1248                 case LeftFrameHandle:
1249                         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
1250                                 cursor = which_trim_cursor (true);  //alternatively, one could argue that we _should_ allow trims here, and disallow range selection
1251                         break;
1252                 case RightFrameHandle:
1253                         if ( effective_mouse_mode() == MouseObject )  //see above
1254                                 cursor = which_trim_cursor (false);
1255                         break;
1256                 case StartCrossFadeItem:
1257                         cursor = _cursors->fade_in;
1258                         break;
1259                 case EndCrossFadeItem:
1260                         cursor = _cursors->fade_out;
1261                         break;
1262                 case CrossfadeViewItem:
1263                         cursor = _cursors->cross_hair;
1264                         break;
1265                 case NoteItem:
1266                         cursor = _cursors->grabber_note;
1267                 default:
1268                         break;
1269                 }
1270
1271         } else if (mouse_mode == MouseDraw) {
1272                 
1273                 /* ControlPointItem is not really specific to region gain mode
1274                    but it is the same cursor so don't worry about this for now.
1275                    The result is that we'll see the fader cursor if we enter
1276                    non-region-gain-line control points while in MouseDraw
1277                    mode, even though we can't edit them in this mode.
1278                 */
1279
1280                 switch (type) {
1281                 case GainLineItem:
1282                 case ControlPointItem:
1283                         cursor = _cursors->fader;
1284                         break;
1285                 case NoteItem:
1286                         cursor = _cursors->grabber_note;
1287                 default:
1288                         break;
1289                 }
1290         }
1291
1292         switch (type) {
1293                 /* These items use the timebar cursor at all times */
1294         case TimecodeRulerItem:
1295         case MinsecRulerItem:
1296         case BBTRulerItem:
1297         case SamplesRulerItem:
1298                 cursor = _cursors->timebar;
1299                 break;
1300
1301                 /* These items use the grabber cursor at all times */
1302         case MeterMarkerItem:
1303         case TempoMarkerItem:
1304         case MeterBarItem:
1305         case TempoBarItem:
1306         case MarkerItem:
1307         case MarkerBarItem:
1308         case RangeMarkerBarItem:
1309         case CdMarkerBarItem:
1310         case VideoBarItem:
1311         case TransportMarkerBarItem:
1312         case DropZoneItem:
1313                 cursor = which_grabber_cursor();
1314                 break;
1315
1316         default:
1317                 break;
1318         }
1319
1320         return cursor;
1321 }
1322
1323 void
1324 Editor::choose_canvas_cursor_on_entry (ItemType type)
1325 {
1326         if (_drags->active()) {
1327                 return;
1328         }
1329
1330         Gdk::Cursor* cursor = which_canvas_cursor(type);
1331
1332         if (cursor) {
1333                 // Push a new enter context
1334                 const EnterContext ctx = { type, CursorContext::create(*this, cursor) };
1335                 _enter_stack.push_back(ctx);
1336         }
1337 }
1338
1339 void
1340 Editor::update_all_enter_cursors ()
1341 {
1342         for (std::vector<EnterContext>::iterator i = _enter_stack.begin(); i != _enter_stack.end(); ++i) {
1343                 i->cursor_ctx->change(which_canvas_cursor(i->item_type));
1344         }
1345 }
1346
1347 double
1348 Editor::trackviews_height() const
1349 {
1350         if (!_trackview_group) {
1351                 return 0;
1352         }
1353
1354         return _visible_canvas_height - _trackview_group->canvas_origin().y;
1355 }