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