use leave/enter from track canvas to be the primary driver of region action sensitivity
[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, SMFTrackName, SMFTempoIgnore, frame, is.selected_instrument());
415
416                 if (UIConfiguration::instance().get_only_copy_imported_files() || copy) {
417                         do_import (audio_paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack,
418                                    SrcBest, SMFTrackName, SMFTempoIgnore, frame);
419                 } else {
420                         do_embed (audio_paths, Editing::ImportDistinctFiles, ImportAsTrack, frame);
421                 }
422
423         } else if ((tv = dynamic_cast<RouteTimeAxisView*> (tvp.first)) != 0) {
424
425                 /* check that its a track, not a bus */
426
427                 if (tv->track()) {
428                         /* select the track, then embed/import */
429                         selection->set (tv);
430
431                         do_import (midi_paths, Editing::ImportSerializeFiles, ImportToTrack,
432                                    SrcBest, SMFTrackName, SMFTempoIgnore, frame);
433
434                         if (UIConfiguration::instance().get_only_copy_imported_files() || copy) {
435                                 do_import (audio_paths, Editing::ImportSerializeFiles, Editing::ImportToTrack,
436                                            SrcBest, SMFTrackName, SMFTempoIgnore, frame);
437                         } else {
438                                 do_embed (audio_paths, Editing::ImportSerializeFiles, ImportToTrack, frame);
439                         }
440                 }
441         }
442 }
443
444 void
445 Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
446                     int x, int y,
447                     const SelectionData& data,
448                     guint info, guint time)
449 {
450         vector<string> paths;
451         GdkEvent ev;
452         double cy;
453
454         if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
455
456                 /* D-n-D coordinates are window-relative, so convert to canvas coordinates
457                  */
458
459                 ev.type = GDK_BUTTON_RELEASE;
460                 ev.button.x = x;
461                 ev.button.y = y;
462
463                 MusicFrame when (window_event_sample (&ev, 0, &cy), 0);
464                 snap_to (when);
465
466                 bool copy = ((context->get_actions() & (Gdk::ACTION_COPY | Gdk::ACTION_LINK | Gdk::ACTION_MOVE)) == Gdk::ACTION_COPY);
467 #ifdef __APPLE__
468                 /* We are not allowed to call recursive main event loops from within
469                    the main event loop with GTK/Quartz. Since import/embed wants
470                    to push up a progress dialog, defer all this till we go idle.
471                 */
472                 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun (*this, &Editor::idle_drop_paths), paths, when.frame, cy, copy));
473 #else
474                 drop_paths_part_two (paths, when.frame, cy, copy);
475 #endif
476         }
477
478         context->drag_finish (true, false, time);
479 }
480
481 /** @param allow_horiz true to allow horizontal autoscroll, otherwise false.
482  *
483  *  @param allow_vert true to allow vertical autoscroll, otherwise false.
484  *
485  */
486 void
487 Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
488 {
489         Gtk::Window* toplevel = dynamic_cast<Gtk::Window*>(contents().get_toplevel());
490
491         if (!toplevel) {
492                 return;
493         }
494
495         if (!UIConfiguration::instance().get_autoscroll_editor () || autoscroll_active ()) {
496                 return;
497         }
498
499         /* define a rectangular boundary for scrolling. If the mouse moves
500          * outside of this area and/or continue to be outside of this area,
501          * then we will continuously auto-scroll the canvas in the appropriate
502          * direction(s)
503          *
504          * the boundary is defined in coordinates relative to the toplevel
505          * window since that is what we're going to call ::get_pointer() on
506          * during autoscrolling to determine if we're still outside the
507          * boundary or not.
508          */
509
510         ArdourCanvas::Rect scrolling_boundary;
511         Gtk::Allocation alloc;
512
513         if (from_headers) {
514                 alloc = controls_layout.get_allocation ();
515
516                 int wx, wy;
517
518                 controls_layout.get_parent()->translate_coordinates (*toplevel,
519                                                                      alloc.get_x(), alloc.get_y(),
520                                                                      wx, wy);
521
522                 scrolling_boundary = ArdourCanvas::Rect (wx, wy, wx + alloc.get_width(), wy + alloc.get_height());
523
524
525         } else {
526                 alloc = _track_canvas_viewport->get_allocation ();
527
528                 /* reduce height by the height of the timebars, which happens
529                    to correspond to the position of the hv_scroll_group.
530                 */
531
532                 alloc.set_height (alloc.get_height() - hv_scroll_group->position().y);
533                 alloc.set_y (alloc.get_y() + hv_scroll_group->position().y);
534
535                 /* now reduce it again so that we start autoscrolling before we
536                  * move off the top or bottom of the canvas
537                  */
538
539                 alloc.set_height (alloc.get_height() - 20);
540                 alloc.set_y (alloc.get_y() + 10);
541
542                 /* the effective width of the autoscroll boundary so
543                    that we start scrolling before we hit the edge.
544
545                    this helps when the window is slammed up against the
546                    right edge of the screen, making it hard to scroll
547                    effectively.
548                 */
549
550                 if (alloc.get_width() > 20) {
551                         alloc.set_width (alloc.get_width() - 20);
552                         alloc.set_x (alloc.get_x() + 10);
553                 }
554
555                 int wx, wy;
556
557                 _track_canvas_viewport->get_parent()->translate_coordinates (*toplevel,
558                                                                              alloc.get_x(), alloc.get_y(),
559                                                                              wx, wy);
560
561                 scrolling_boundary = ArdourCanvas::Rect (wx, wy, wx + alloc.get_width(), wy + alloc.get_height());
562         }
563
564         int x, y;
565         Gdk::ModifierType mask;
566
567         toplevel->get_window()->get_pointer (x, y, mask);
568
569         if ((allow_horiz && ((x < scrolling_boundary.x0 && leftmost_frame > 0) || x >= scrolling_boundary.x1)) ||
570             (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) {
571                 start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary);
572         }
573 }
574
575 bool
576 Editor::autoscroll_active () const
577 {
578         return autoscroll_connection.connected ();
579 }
580
581 bool
582 Editor::autoscroll_canvas ()
583 {
584         int x, y;
585         Gdk::ModifierType mask;
586         frameoffset_t dx = 0;
587         bool no_stop = false;
588         Gtk::Window* toplevel = dynamic_cast<Gtk::Window*>(contents().get_toplevel());
589
590         if (!toplevel) {
591                 return false;
592         }
593
594         toplevel->get_window()->get_pointer (x, y, mask);
595
596         VisualChange vc;
597         bool vertical_motion = false;
598
599         if (autoscroll_horizontal_allowed) {
600
601                 framepos_t new_frame = leftmost_frame;
602
603                 /* horizontal */
604
605                 if (x > autoscroll_boundary.x1) {
606
607                         /* bring it back into view */
608                         dx = x - autoscroll_boundary.x1;
609                         dx += 10 + (2 * (autoscroll_cnt/2));
610
611                         dx = pixel_to_sample (dx);
612
613                         if (leftmost_frame < max_framepos - dx) {
614                                 new_frame = leftmost_frame + dx;
615                         } else {
616                                 new_frame = max_framepos;
617                         }
618
619                         no_stop = true;
620
621                 } else if (x < autoscroll_boundary.x0) {
622
623                         dx = autoscroll_boundary.x0 - x;
624                         dx += 10 + (2 * (autoscroll_cnt/2));
625
626                         dx = pixel_to_sample (dx);
627
628                         if (leftmost_frame >= dx) {
629                                 new_frame = leftmost_frame - dx;
630                         } else {
631                                 new_frame = 0;
632                         }
633
634                         no_stop = true;
635                 }
636
637                 if (new_frame != leftmost_frame) {
638                         vc.time_origin = new_frame;
639                         vc.add (VisualChange::TimeOrigin);
640                 }
641         }
642
643         if (autoscroll_vertical_allowed) {
644
645                 // const double vertical_pos = vertical_adjustment.get_value();
646                 const int speed_factor = 10;
647
648                 /* vertical */
649
650                 if (y < autoscroll_boundary.y0) {
651
652                         /* scroll to make higher tracks visible */
653
654                         if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
655                                 scroll_up_one_track ();
656                                 vertical_motion = true;
657                         }
658                         no_stop = true;
659
660                 } else if (y > autoscroll_boundary.y1) {
661
662                         if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
663                                 scroll_down_one_track ();
664                                 vertical_motion = true;
665                         }
666                         no_stop = true;
667                 }
668
669         }
670
671         if (vc.pending || vertical_motion) {
672
673                 /* change horizontal first */
674
675                 if (vc.pending) {
676                         visual_changer (vc);
677                 }
678
679                 /* now send a motion event to notify anyone who cares
680                    that we have moved to a new location (because we scrolled)
681                 */
682
683                 GdkEventMotion ev;
684
685                 ev.type = GDK_MOTION_NOTIFY;
686                 ev.state = Gdk::BUTTON1_MASK;
687
688                 /* the motion handler expects events in canvas coordinate space */
689
690                 /* we asked for the mouse position above (::get_pointer()) via
691                  * our own top level window (we being the Editor). Convert into
692                  * coordinates within the canvas window.
693                  */
694
695                 int cx;
696                 int cy;
697
698                 toplevel->translate_coordinates (*_track_canvas, x, y, cx, cy);
699
700                 /* clamp x and y to remain within the autoscroll boundary,
701                  * which is defined in window coordinates
702                  */
703
704                 x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1);
705                 y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1);
706
707                 /* now convert from Editor window coordinates to canvas
708                  * window coordinates
709                  */
710
711                 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
712                 ev.x = d.x;
713                 ev.y = d.y;
714                 ev.state = mask;
715
716                 motion_handler (0, (GdkEvent*) &ev, true);
717
718         } else if (no_stop) {
719
720                 /* not changing visual state but pointer is outside the scrolling boundary
721                  * so we still need to deliver a fake motion event
722                  */
723
724                 GdkEventMotion ev;
725
726                 ev.type = GDK_MOTION_NOTIFY;
727                 ev.state = Gdk::BUTTON1_MASK;
728
729                 /* the motion handler expects events in canvas coordinate space */
730
731                 /* first convert from Editor window coordinates to canvas
732                  * window coordinates
733                  */
734
735                 int cx;
736                 int cy;
737
738                 /* clamp x and y to remain within the visible area. except
739                  * .. if horizontal scrolling is allowed, always allow us to
740                  * move back to zero
741                  */
742
743                 if (autoscroll_horizontal_allowed) {
744                         x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1);
745                 } else {
746                         x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
747                 }
748                 y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
749
750                 toplevel->translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
751
752                 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
753                 ev.x = d.x;
754                 ev.y = d.y;
755                 ev.state = mask;
756
757                 motion_handler (0, (GdkEvent*) &ev, true);
758
759         } else {
760                 stop_canvas_autoscroll ();
761                 return false;
762         }
763
764         autoscroll_cnt++;
765
766         return true; /* call me again */
767 }
768
769 void
770 Editor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary)
771 {
772         if (!_session) {
773                 return;
774         }
775
776         stop_canvas_autoscroll ();
777
778         autoscroll_horizontal_allowed = allow_horiz;
779         autoscroll_vertical_allowed = allow_vert;
780         autoscroll_boundary = boundary;
781
782         /* do the first scroll right now
783         */
784
785         autoscroll_canvas ();
786
787         /* scroll again at very very roughly 30FPS */
788
789         autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::autoscroll_canvas), 30);
790 }
791
792 void
793 Editor::stop_canvas_autoscroll ()
794 {
795         autoscroll_connection.disconnect ();
796         autoscroll_cnt = 0;
797 }
798
799 Editor::EnterContext*
800 Editor::get_enter_context(ItemType type)
801 {
802         for (ssize_t i = _enter_stack.size() - 1; i >= 0; --i) {
803                 if (_enter_stack[i].item_type == type) {
804                         return &_enter_stack[i];
805                 }
806         }
807         return NULL;
808 }
809
810 bool
811 Editor::left_track_canvas (GdkEventCrossing* ev)
812 {
813         const bool was_within = within_track_canvas;
814         DropDownKeys ();
815         within_track_canvas = false;
816         set_entered_track (0);
817         set_entered_regionview (0);
818         reset_canvas_action_sensitivity (false);
819
820         if (was_within) {
821                 if (ev->detail == GDK_NOTIFY_NONLINEAR ||
822                     ev->detail == GDK_NOTIFY_NONLINEAR_VIRTUAL) {
823                         /* context menu or something similar */
824                         sensitize_the_right_region_actions (true, false);
825                 } else {
826                         sensitize_the_right_region_actions (false, true);
827                 }
828         }
829
830         return false;
831 }
832
833 bool
834 Editor::entered_track_canvas (GdkEventCrossing* ev)
835 {
836         const bool was_within = within_track_canvas;
837         within_track_canvas = true;
838         reset_canvas_action_sensitivity (true);
839
840         if (!was_within) {
841                 if (ev->detail == GDK_NOTIFY_NONLINEAR ||
842                     ev->detail == GDK_NOTIFY_NONLINEAR_VIRTUAL) {
843                         /* context menu or something similar */
844                         sensitize_the_right_region_actions (true, false);
845                 } else {
846                         sensitize_the_right_region_actions (false, true);
847                 }
848         }
849
850         return false;
851 }
852
853 void
854 Editor::ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top)
855 {
856         if (track.hidden()) {
857                 return;
858         }
859
860         /* compute visible area of trackview group, as offsets from top of
861          * trackview group.
862          */
863
864         double const current_view_min_y = vertical_adjustment.get_value();
865         double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size();
866
867         double const track_min_y = track.y_position ();
868         double const track_max_y = track.y_position () + track.effective_height ();
869
870         if (!at_top &&
871             (track_min_y >= current_view_min_y &&
872              track_max_y < current_view_max_y)) {
873                 /* already visible, and caller did not ask to place it at the
874                  * top of the track canvas
875                  */
876                 return;
877         }
878
879         double new_value;
880
881         if (at_top) {
882                 new_value = track_min_y;
883         } else {
884                 if (track_min_y < current_view_min_y) {
885                         // Track is above the current view
886                         new_value = track_min_y;
887                 } else if (track_max_y > current_view_max_y) {
888                         // Track is below the current view
889                         new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size();
890                 } else {
891                         new_value = track_min_y;
892                 }
893         }
894
895         vertical_adjustment.set_value(new_value);
896 }
897
898 /** Called when the main vertical_adjustment has changed */
899 void
900 Editor::tie_vertical_scrolling ()
901 {
902         if (pending_visual_change.idle_handler_id < 0) {
903                 _summary->set_overlays_dirty ();
904         }
905 }
906
907 void
908 Editor::set_horizontal_position (double p)
909 {
910         horizontal_adjustment.set_value (p);
911
912         leftmost_frame = (framepos_t) floor (p * samples_per_pixel);
913
914         update_fixed_rulers ();
915         redisplay_tempo (true);
916
917         if (pending_visual_change.idle_handler_id < 0) {
918                 _summary->set_overlays_dirty ();
919         }
920
921         update_video_timeline();
922 }
923
924 void
925 Editor::color_handler()
926 {
927         ArdourCanvas::Color base = UIConfiguration::instance().color ("ruler base");
928         ArdourCanvas::Color text = UIConfiguration::instance().color ("ruler text");
929         timecode_ruler->set_fill_color (base);
930         timecode_ruler->set_outline_color (text);
931         minsec_ruler->set_fill_color (base);
932         minsec_ruler->set_outline_color (text);
933         samples_ruler->set_fill_color (base);
934         samples_ruler->set_outline_color (text);
935         bbt_ruler->set_fill_color (base);
936         bbt_ruler->set_outline_color (text);
937
938         playhead_cursor->set_color (UIConfiguration::instance().color ("play head"));
939
940         meter_bar->set_fill_color (UIConfiguration::instance().color_mod ("meter bar", "marker bar"));
941         meter_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
942
943         tempo_bar->set_fill_color (UIConfiguration::instance().color_mod ("tempo bar", "marker bar"));
944         tempo_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
945
946         marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("marker bar", "marker bar"));
947         marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
948
949         cd_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("cd marker bar", "marker bar"));
950         cd_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
951
952         range_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("range marker bar", "marker bar"));
953         range_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
954
955         transport_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("transport marker bar", "marker bar"));
956         transport_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
957
958         cd_marker_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect"));
959         cd_marker_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect"));
960
961         range_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect"));
962         range_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect"));
963
964         transport_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("transport drag rect"));
965         transport_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("transport drag rect"));
966
967         transport_loop_range_rect->set_fill_color (UIConfiguration::instance().color_mod ("transport loop rect", "loop rectangle"));
968         transport_loop_range_rect->set_outline_color (UIConfiguration::instance().color ("transport loop rect"));
969
970         transport_punch_range_rect->set_fill_color (UIConfiguration::instance().color ("transport punch rect"));
971         transport_punch_range_rect->set_outline_color (UIConfiguration::instance().color ("transport punch rect"));
972
973         transport_punchin_line->set_outline_color (UIConfiguration::instance().color ("punch line"));
974         transport_punchout_line->set_outline_color (UIConfiguration::instance().color ("punch line"));
975
976         rubberband_rect->set_outline_color (UIConfiguration::instance().color ("rubber band rect"));
977         rubberband_rect->set_fill_color (UIConfiguration::instance().color_mod ("rubber band rect", "selection rect"));
978
979         location_marker_color = UIConfiguration::instance().color ("location marker");
980         location_range_color = UIConfiguration::instance().color ("location range");
981         location_cd_marker_color = UIConfiguration::instance().color ("location cd marker");
982         location_loop_color = UIConfiguration::instance().color ("location loop");
983         location_punch_color = UIConfiguration::instance().color ("location punch");
984
985         refresh_location_display ();
986
987         NoteBase::set_colors ();
988
989         /* redraw the whole thing */
990         _track_canvas->set_background_color (UIConfiguration::instance().color ("arrange base"));
991         _track_canvas->queue_draw ();
992
993 /*
994         redisplay_tempo (true);
995
996         if (_session)
997               _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
998 */
999 }
1000
1001 double
1002 Editor::horizontal_position () const
1003 {
1004         return sample_to_pixel (leftmost_frame);
1005 }
1006
1007 bool
1008 Editor::track_canvas_key_press (GdkEventKey*)
1009 {
1010         return false;
1011 }
1012
1013 bool
1014 Editor::track_canvas_key_release (GdkEventKey*)
1015 {
1016         return false;
1017 }
1018
1019 double
1020 Editor::clamp_verbose_cursor_x (double x)
1021 {
1022         if (x < 0) {
1023                 x = 0;
1024         } else {
1025                 x = min (_visible_canvas_width - 200.0, x);
1026         }
1027         return x;
1028 }
1029
1030 double
1031 Editor::clamp_verbose_cursor_y (double y)
1032 {
1033         y = max (0.0, y);
1034         y = min (_visible_canvas_height - 50, y);
1035         return y;
1036 }
1037
1038 ArdourCanvas::GtkCanvasViewport*
1039 Editor::get_track_canvas() const
1040 {
1041         return _track_canvas_viewport;
1042 }
1043
1044 Gdk::Cursor*
1045 Editor::get_canvas_cursor () const
1046 {
1047         /* The top of the cursor stack is always the currently visible cursor. */
1048         return _cursor_stack.back();
1049 }
1050
1051 void
1052 Editor::set_canvas_cursor (Gdk::Cursor* cursor)
1053 {
1054         Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
1055
1056         if (win && !_cursors->is_invalid (cursor)) {
1057                 /* glibmm 2.4 doesn't allow null cursor pointer because it uses
1058                    a Gdk::Cursor& as the argument to Gdk::Window::set_cursor().
1059                    But a null pointer just means "use parent window cursor",
1060                    and so should be allowed. Gtkmm 3.x has fixed this API.
1061
1062                    For now, drop down and use C API
1063                 */
1064                 gdk_window_set_cursor (win->gobj(), cursor ? cursor->gobj() : 0);
1065         }
1066 }
1067
1068 size_t
1069 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
1070 {
1071         if (!_cursors->is_invalid (cursor)) {
1072                 _cursor_stack.push_back (cursor);
1073                 set_canvas_cursor (cursor);
1074         }
1075         return _cursor_stack.size() - 1;
1076 }
1077
1078 void
1079 Editor::pop_canvas_cursor ()
1080 {
1081         while (true) {
1082                 if (_cursor_stack.size() <= 1) {
1083                         PBD::error << "attempt to pop default cursor" << endmsg;
1084                         return;
1085                 }
1086
1087                 _cursor_stack.pop_back();
1088                 if (_cursor_stack.back()) {
1089                         /* Popped to an existing cursor, we're done.  Otherwise, the
1090                            context that created this cursor has been destroyed, so we need
1091                            to skip to the next down the stack. */
1092                         set_canvas_cursor (_cursor_stack.back());
1093                         return;
1094                 }
1095         }
1096 }
1097
1098 Gdk::Cursor*
1099 Editor::which_grabber_cursor () const
1100 {
1101         Gdk::Cursor* c = _cursors->grabber;
1102
1103         switch (_edit_point) {
1104         case EditAtMouse:
1105                 c = _cursors->grabber_edit_point;
1106                 break;
1107         default:
1108                 boost::shared_ptr<Movable> m = _movable.lock();
1109                 if (m && m->locked()) {
1110                         c = _cursors->speaker;
1111                 }
1112                 break;
1113         }
1114
1115         return c;
1116 }
1117
1118 Gdk::Cursor*
1119 Editor::which_trim_cursor (bool left) const
1120 {
1121         if (!entered_regionview) {
1122                 return 0;
1123         }
1124
1125         Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
1126
1127         if (left) {
1128
1129                 if (ct & Trimmable::FrontTrimEarlier) {
1130                         return _cursors->left_side_trim;
1131                 } else {
1132                         return _cursors->left_side_trim_right_only;
1133                 }
1134         } else {
1135                 if (ct & Trimmable::EndTrimLater) {
1136                         return _cursors->right_side_trim;
1137                 } else {
1138                         return _cursors->right_side_trim_left_only;
1139                 }
1140         }
1141 }
1142
1143 Gdk::Cursor*
1144 Editor::which_mode_cursor () const
1145 {
1146         Gdk::Cursor* mode_cursor = MouseCursors::invalid_cursor ();
1147
1148         switch (mouse_mode) {
1149         case MouseRange:
1150                 mode_cursor = _cursors->selector;
1151                 break;
1152
1153         case MouseCut:
1154                 mode_cursor = _cursors->scissors;
1155                 break;
1156
1157         case MouseObject:
1158         case MouseContent:
1159                 /* don't use mode cursor, pick a grabber cursor based on the item */
1160                 break;
1161
1162         case MouseDraw:
1163                 mode_cursor = _cursors->midi_pencil;
1164                 break;
1165
1166         case MouseTimeFX:
1167                 mode_cursor = _cursors->time_fx; // just use playhead
1168                 break;
1169
1170         case MouseAudition:
1171                 mode_cursor = _cursors->speaker;
1172                 break;
1173         }
1174
1175         /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
1176         if (get_smart_mode()) {
1177
1178                 double x, y;
1179                 get_pointer_position (x, y);
1180
1181                 if (x >= 0 && y >= 0) {
1182
1183                         vector<ArdourCanvas::Item const *> items;
1184
1185                         /* Note how we choose a specific scroll group to get
1186                          * items from. This could be problematic.
1187                          */
1188
1189                         hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
1190
1191                         // first item will be the upper most
1192
1193                         if (!items.empty()) {
1194                                 const ArdourCanvas::Item* i = items.front();
1195
1196                                 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
1197                                         pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
1198                                         if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
1199                                                 mode_cursor = _cursors->up_down;
1200                                         }
1201                                 }
1202                         }
1203                 }
1204         }
1205
1206         return mode_cursor;
1207 }
1208
1209 Gdk::Cursor*
1210 Editor::which_track_cursor () const
1211 {
1212         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
1213
1214         switch (_join_object_range_state) {
1215         case JOIN_OBJECT_RANGE_NONE:
1216         case JOIN_OBJECT_RANGE_OBJECT:
1217                 cursor = which_grabber_cursor ();
1218                 break;
1219         case JOIN_OBJECT_RANGE_RANGE:
1220                 cursor = _cursors->selector;
1221                 break;
1222         }
1223
1224         return cursor;
1225 }
1226
1227 Gdk::Cursor*
1228 Editor::which_canvas_cursor(ItemType type) const
1229 {
1230         Gdk::Cursor* cursor = which_mode_cursor ();
1231
1232         if (mouse_mode == MouseRange) {
1233                 switch (type) {
1234                 case StartSelectionTrimItem:
1235                         cursor = _cursors->left_side_trim;
1236                         break;
1237                 case EndSelectionTrimItem:
1238                         cursor = _cursors->right_side_trim;
1239                         break;
1240                 default:
1241                         break;
1242                 }
1243         }
1244
1245         if ((mouse_mode == MouseObject || get_smart_mode ()) ||
1246             mouse_mode == MouseContent) {
1247
1248                 /* find correct cursor to use in object/smart mode */
1249
1250                 switch (type) {
1251                 case RegionItem:
1252                 /* We don't choose a cursor for these items on top of a region view,
1253                    because this would push a new context on the enter stack which
1254                    means switching the region context for things like smart mode
1255                    won't actualy change the cursor. */
1256                 // case RegionViewNameHighlight:
1257                 // case RegionViewName:
1258                 // case WaveItem:
1259                 case StreamItem:
1260                 case AutomationTrackItem:
1261                         cursor = which_track_cursor ();
1262                         break;
1263                 case PlayheadCursorItem:
1264                         switch (_edit_point) {
1265                         case EditAtMouse:
1266                                 cursor = _cursors->grabber_edit_point;
1267                                 break;
1268                         default:
1269                                 cursor = _cursors->grabber;
1270                                 break;
1271                         }
1272                         break;
1273                 case SelectionItem:
1274                         cursor = _cursors->selector;
1275                         break;
1276                 case ControlPointItem:
1277                         cursor = _cursors->fader;
1278                         break;
1279                 case GainLineItem:
1280                         cursor = _cursors->cross_hair;
1281                         break;
1282                 case AutomationLineItem:
1283                         cursor = _cursors->cross_hair;
1284                         break;
1285                 case StartSelectionTrimItem:
1286                         cursor = _cursors->left_side_trim;
1287                         break;
1288                 case EndSelectionTrimItem:
1289                         cursor = _cursors->right_side_trim;
1290                         break;
1291                 case FadeInItem:
1292                         cursor = _cursors->fade_in;
1293                         break;
1294                 case FadeInHandleItem:
1295                         cursor = _cursors->fade_in;
1296                         break;
1297                 case FadeInTrimHandleItem:
1298                         cursor = _cursors->fade_in;
1299                         break;
1300                 case FadeOutItem:
1301                         cursor = _cursors->fade_out;
1302                         break;
1303                 case FadeOutHandleItem:
1304                         cursor = _cursors->fade_out;
1305                         break;
1306                 case FadeOutTrimHandleItem:
1307                         cursor = _cursors->fade_out;
1308                         break;
1309                 case FeatureLineItem:
1310                         cursor = _cursors->cross_hair;
1311                         break;
1312                 case LeftFrameHandle:
1313                         if ( effective_mouse_mode() == MouseObject )  // (smart mode): if the user is in the btm half, show the trim cursor
1314                                 cursor = which_trim_cursor (true);
1315                         else
1316                                 cursor = _cursors->selector;  // (smart mode): in the top half, just show the selection (range) cursor
1317                         break;
1318                 case RightFrameHandle:
1319                         if ( effective_mouse_mode() == MouseObject )  //see above
1320                                 cursor = which_trim_cursor (false);
1321                         else
1322                                 cursor = _cursors->selector;
1323                         break;
1324                 case StartCrossFadeItem:
1325                         cursor = _cursors->fade_in;
1326                         break;
1327                 case EndCrossFadeItem:
1328                         cursor = _cursors->fade_out;
1329                         break;
1330                 case CrossfadeViewItem:
1331                         cursor = _cursors->cross_hair;
1332                         break;
1333                 case NoteItem:
1334                         cursor = _cursors->grabber_note;
1335                 default:
1336                         break;
1337                 }
1338
1339         } else if (mouse_mode == MouseDraw) {
1340
1341                 /* ControlPointItem is not really specific to region gain mode
1342                    but it is the same cursor so don't worry about this for now.
1343                    The result is that we'll see the fader cursor if we enter
1344                    non-region-gain-line control points while in MouseDraw
1345                    mode, even though we can't edit them in this mode.
1346                 */
1347
1348                 switch (type) {
1349                 case GainLineItem:
1350                 case ControlPointItem:
1351                         cursor = _cursors->fader;
1352                         break;
1353                 case NoteItem:
1354                         cursor = _cursors->grabber_note;
1355                 default:
1356                         break;
1357                 }
1358         }
1359
1360         switch (type) {
1361                 /* These items use the timebar cursor at all times */
1362         case TimecodeRulerItem:
1363         case MinsecRulerItem:
1364         case BBTRulerItem:
1365         case SamplesRulerItem:
1366                 cursor = _cursors->timebar;
1367                 break;
1368
1369                 /* These items use the grabber cursor at all times */
1370         case MeterMarkerItem:
1371         case TempoMarkerItem:
1372         case MeterBarItem:
1373         case TempoBarItem:
1374         case MarkerItem:
1375         case MarkerBarItem:
1376         case RangeMarkerBarItem:
1377         case CdMarkerBarItem:
1378         case VideoBarItem:
1379         case TransportMarkerBarItem:
1380         case DropZoneItem:
1381                 cursor = which_grabber_cursor();
1382                 break;
1383
1384         default:
1385                 break;
1386         }
1387
1388         return cursor;
1389 }
1390
1391 void
1392 Editor::choose_canvas_cursor_on_entry (ItemType type)
1393 {
1394         if (_drags->active()) {
1395                 return;
1396         }
1397
1398         Gdk::Cursor* cursor = which_canvas_cursor(type);
1399
1400         if (!_cursors->is_invalid (cursor)) {
1401                 // Push a new enter context
1402                 const EnterContext ctx = { type, CursorContext::create(*this, cursor) };
1403                 _enter_stack.push_back(ctx);
1404         }
1405 }
1406
1407 void
1408 Editor::update_all_enter_cursors ()
1409 {
1410         for (std::vector<EnterContext>::iterator i = _enter_stack.begin(); i != _enter_stack.end(); ++i) {
1411                 i->cursor_ctx->change(which_canvas_cursor(i->item_type));
1412         }
1413 }
1414
1415 double
1416 Editor::trackviews_height() const
1417 {
1418         if (!_trackview_group) {
1419                 return 0;
1420         }
1421
1422         return _visible_canvas_height - _trackview_group->canvas_origin().y;
1423 }