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