NO-OP: whitespace conversion
[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         DropDownKeys ();
814         within_track_canvas = false;
815         set_entered_track (0);
816         set_entered_regionview (0);
817         reset_canvas_action_sensitivity (false);
818         return false;
819 }
820
821 bool
822 Editor::entered_track_canvas (GdkEventCrossing */*ev*/)
823 {
824         within_track_canvas = true;
825         reset_canvas_action_sensitivity (true);
826         return FALSE;
827 }
828
829 void
830 Editor::ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top)
831 {
832         if (track.hidden()) {
833                 return;
834         }
835
836         /* compute visible area of trackview group, as offsets from top of
837          * trackview group.
838          */
839
840         double const current_view_min_y = vertical_adjustment.get_value();
841         double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size();
842
843         double const track_min_y = track.y_position ();
844         double const track_max_y = track.y_position () + track.effective_height ();
845
846         if (!at_top &&
847             (track_min_y >= current_view_min_y &&
848              track_max_y < current_view_max_y)) {
849                 /* already visible, and caller did not ask to place it at the
850                  * top of the track canvas
851                  */
852                 return;
853         }
854
855         double new_value;
856
857         if (at_top) {
858                 new_value = track_min_y;
859         } else {
860                 if (track_min_y < current_view_min_y) {
861                         // Track is above the current view
862                         new_value = track_min_y;
863                 } else if (track_max_y > current_view_max_y) {
864                         // Track is below the current view
865                         new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size();
866                 } else {
867                         new_value = track_min_y;
868                 }
869         }
870
871         vertical_adjustment.set_value(new_value);
872 }
873
874 /** Called when the main vertical_adjustment has changed */
875 void
876 Editor::tie_vertical_scrolling ()
877 {
878         if (pending_visual_change.idle_handler_id < 0) {
879                 _summary->set_overlays_dirty ();
880         }
881 }
882
883 void
884 Editor::set_horizontal_position (double p)
885 {
886         horizontal_adjustment.set_value (p);
887
888         leftmost_frame = (framepos_t) floor (p * samples_per_pixel);
889
890         update_fixed_rulers ();
891         redisplay_tempo (true);
892
893         if (pending_visual_change.idle_handler_id < 0) {
894                 _summary->set_overlays_dirty ();
895         }
896
897         update_video_timeline();
898 }
899
900 void
901 Editor::color_handler()
902 {
903         ArdourCanvas::Color base = UIConfiguration::instance().color ("ruler base");
904         ArdourCanvas::Color text = UIConfiguration::instance().color ("ruler text");
905         timecode_ruler->set_fill_color (base);
906         timecode_ruler->set_outline_color (text);
907         minsec_ruler->set_fill_color (base);
908         minsec_ruler->set_outline_color (text);
909         samples_ruler->set_fill_color (base);
910         samples_ruler->set_outline_color (text);
911         bbt_ruler->set_fill_color (base);
912         bbt_ruler->set_outline_color (text);
913
914         playhead_cursor->set_color (UIConfiguration::instance().color ("play head"));
915
916         meter_bar->set_fill_color (UIConfiguration::instance().color_mod ("meter bar", "marker bar"));
917         meter_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
918
919         tempo_bar->set_fill_color (UIConfiguration::instance().color_mod ("tempo bar", "marker bar"));
920         tempo_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
921
922         marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("marker bar", "marker bar"));
923         marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
924
925         cd_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("cd marker bar", "marker bar"));
926         cd_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
927
928         range_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("range marker bar", "marker bar"));
929         range_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
930
931         transport_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("transport marker bar", "marker bar"));
932         transport_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
933
934         cd_marker_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect"));
935         cd_marker_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect"));
936
937         range_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect"));
938         range_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect"));
939
940         transport_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("transport drag rect"));
941         transport_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("transport drag rect"));
942
943         transport_loop_range_rect->set_fill_color (UIConfiguration::instance().color_mod ("transport loop rect", "loop rectangle"));
944         transport_loop_range_rect->set_outline_color (UIConfiguration::instance().color ("transport loop rect"));
945
946         transport_punch_range_rect->set_fill_color (UIConfiguration::instance().color ("transport punch rect"));
947         transport_punch_range_rect->set_outline_color (UIConfiguration::instance().color ("transport punch rect"));
948
949         transport_punchin_line->set_outline_color (UIConfiguration::instance().color ("punch line"));
950         transport_punchout_line->set_outline_color (UIConfiguration::instance().color ("punch line"));
951
952         rubberband_rect->set_outline_color (UIConfiguration::instance().color ("rubber band rect"));
953         rubberband_rect->set_fill_color (UIConfiguration::instance().color_mod ("rubber band rect", "selection rect"));
954
955         location_marker_color = UIConfiguration::instance().color ("location marker");
956         location_range_color = UIConfiguration::instance().color ("location range");
957         location_cd_marker_color = UIConfiguration::instance().color ("location cd marker");
958         location_loop_color = UIConfiguration::instance().color ("location loop");
959         location_punch_color = UIConfiguration::instance().color ("location punch");
960
961         refresh_location_display ();
962
963         NoteBase::set_colors ();
964
965         /* redraw the whole thing */
966         _track_canvas->set_background_color (UIConfiguration::instance().color ("arrange base"));
967         _track_canvas->queue_draw ();
968
969 /*
970         redisplay_tempo (true);
971
972         if (_session)
973               _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
974 */
975 }
976
977 double
978 Editor::horizontal_position () const
979 {
980         return sample_to_pixel (leftmost_frame);
981 }
982
983 bool
984 Editor::track_canvas_key_press (GdkEventKey*)
985 {
986         return false;
987 }
988
989 bool
990 Editor::track_canvas_key_release (GdkEventKey*)
991 {
992         return false;
993 }
994
995 double
996 Editor::clamp_verbose_cursor_x (double x)
997 {
998         if (x < 0) {
999                 x = 0;
1000         } else {
1001                 x = min (_visible_canvas_width - 200.0, x);
1002         }
1003         return x;
1004 }
1005
1006 double
1007 Editor::clamp_verbose_cursor_y (double y)
1008 {
1009         y = max (0.0, y);
1010         y = min (_visible_canvas_height - 50, y);
1011         return y;
1012 }
1013
1014 ArdourCanvas::GtkCanvasViewport*
1015 Editor::get_track_canvas() const
1016 {
1017         return _track_canvas_viewport;
1018 }
1019
1020 Gdk::Cursor*
1021 Editor::get_canvas_cursor () const
1022 {
1023         /* The top of the cursor stack is always the currently visible cursor. */
1024         return _cursor_stack.back();
1025 }
1026
1027 void
1028 Editor::set_canvas_cursor (Gdk::Cursor* cursor)
1029 {
1030         Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
1031
1032         if (win && !_cursors->is_invalid (cursor)) {
1033                 /* glibmm 2.4 doesn't allow null cursor pointer because it uses
1034                    a Gdk::Cursor& as the argument to Gdk::Window::set_cursor().
1035                    But a null pointer just means "use parent window cursor",
1036                    and so should be allowed. Gtkmm 3.x has fixed this API.
1037
1038                    For now, drop down and use C API
1039                 */
1040                 gdk_window_set_cursor (win->gobj(), cursor ? cursor->gobj() : 0);
1041         }
1042 }
1043
1044 size_t
1045 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
1046 {
1047         if (!_cursors->is_invalid (cursor)) {
1048                 _cursor_stack.push_back (cursor);
1049                 set_canvas_cursor (cursor);
1050         }
1051         return _cursor_stack.size() - 1;
1052 }
1053
1054 void
1055 Editor::pop_canvas_cursor ()
1056 {
1057         while (true) {
1058                 if (_cursor_stack.size() <= 1) {
1059                         PBD::error << "attempt to pop default cursor" << endmsg;
1060                         return;
1061                 }
1062
1063                 _cursor_stack.pop_back();
1064                 if (_cursor_stack.back()) {
1065                         /* Popped to an existing cursor, we're done.  Otherwise, the
1066                            context that created this cursor has been destroyed, so we need
1067                            to skip to the next down the stack. */
1068                         set_canvas_cursor (_cursor_stack.back());
1069                         return;
1070                 }
1071         }
1072 }
1073
1074 Gdk::Cursor*
1075 Editor::which_grabber_cursor () const
1076 {
1077         Gdk::Cursor* c = _cursors->grabber;
1078
1079         switch (_edit_point) {
1080         case EditAtMouse:
1081                 c = _cursors->grabber_edit_point;
1082                 break;
1083         default:
1084                 boost::shared_ptr<Movable> m = _movable.lock();
1085                 if (m && m->locked()) {
1086                         c = _cursors->speaker;
1087                 }
1088                 break;
1089         }
1090
1091         return c;
1092 }
1093
1094 Gdk::Cursor*
1095 Editor::which_trim_cursor (bool left) const
1096 {
1097         if (!entered_regionview) {
1098                 return 0;
1099         }
1100
1101         Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
1102
1103         if (left) {
1104
1105                 if (ct & Trimmable::FrontTrimEarlier) {
1106                         return _cursors->left_side_trim;
1107                 } else {
1108                         return _cursors->left_side_trim_right_only;
1109                 }
1110         } else {
1111                 if (ct & Trimmable::EndTrimLater) {
1112                         return _cursors->right_side_trim;
1113                 } else {
1114                         return _cursors->right_side_trim_left_only;
1115                 }
1116         }
1117 }
1118
1119 Gdk::Cursor*
1120 Editor::which_mode_cursor () const
1121 {
1122         Gdk::Cursor* mode_cursor = MouseCursors::invalid_cursor ();
1123
1124         switch (mouse_mode) {
1125         case MouseRange:
1126                 mode_cursor = _cursors->selector;
1127                 break;
1128
1129         case MouseCut:
1130                 mode_cursor = _cursors->scissors;
1131                 break;
1132
1133         case MouseObject:
1134         case MouseContent:
1135                 /* don't use mode cursor, pick a grabber cursor based on the item */
1136                 break;
1137
1138         case MouseDraw:
1139                 mode_cursor = _cursors->midi_pencil;
1140                 break;
1141
1142         case MouseTimeFX:
1143                 mode_cursor = _cursors->time_fx; // just use playhead
1144                 break;
1145
1146         case MouseAudition:
1147                 mode_cursor = _cursors->speaker;
1148                 break;
1149         }
1150
1151         /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
1152         if (get_smart_mode()) {
1153
1154                 double x, y;
1155                 get_pointer_position (x, y);
1156
1157                 if (x >= 0 && y >= 0) {
1158
1159                         vector<ArdourCanvas::Item const *> items;
1160
1161                         /* Note how we choose a specific scroll group to get
1162                          * items from. This could be problematic.
1163                          */
1164
1165                         hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
1166
1167                         // first item will be the upper most
1168
1169                         if (!items.empty()) {
1170                                 const ArdourCanvas::Item* i = items.front();
1171
1172                                 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
1173                                         pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
1174                                         if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
1175                                                 mode_cursor = _cursors->up_down;
1176                                         }
1177                                 }
1178                         }
1179                 }
1180         }
1181
1182         return mode_cursor;
1183 }
1184
1185 Gdk::Cursor*
1186 Editor::which_track_cursor () const
1187 {
1188         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
1189
1190         switch (_join_object_range_state) {
1191         case JOIN_OBJECT_RANGE_NONE:
1192         case JOIN_OBJECT_RANGE_OBJECT:
1193                 cursor = which_grabber_cursor ();
1194                 break;
1195         case JOIN_OBJECT_RANGE_RANGE:
1196                 cursor = _cursors->selector;
1197                 break;
1198         }
1199
1200         return cursor;
1201 }
1202
1203 Gdk::Cursor*
1204 Editor::which_canvas_cursor(ItemType type) const
1205 {
1206         Gdk::Cursor* cursor = which_mode_cursor ();
1207
1208         if (mouse_mode == MouseRange) {
1209                 switch (type) {
1210                 case StartSelectionTrimItem:
1211                         cursor = _cursors->left_side_trim;
1212                         break;
1213                 case EndSelectionTrimItem:
1214                         cursor = _cursors->right_side_trim;
1215                         break;
1216                 default:
1217                         break;
1218                 }
1219         }
1220
1221         if ((mouse_mode == MouseObject || get_smart_mode ()) ||
1222             mouse_mode == MouseContent) {
1223
1224                 /* find correct cursor to use in object/smart mode */
1225
1226                 switch (type) {
1227                 case RegionItem:
1228                 /* We don't choose a cursor for these items on top of a region view,
1229                    because this would push a new context on the enter stack which
1230                    means switching the region context for things like smart mode
1231                    won't actualy change the cursor. */
1232                 // case RegionViewNameHighlight:
1233                 // case RegionViewName:
1234                 // case WaveItem:
1235                 case StreamItem:
1236                 case AutomationTrackItem:
1237                         cursor = which_track_cursor ();
1238                         break;
1239                 case PlayheadCursorItem:
1240                         switch (_edit_point) {
1241                         case EditAtMouse:
1242                                 cursor = _cursors->grabber_edit_point;
1243                                 break;
1244                         default:
1245                                 cursor = _cursors->grabber;
1246                                 break;
1247                         }
1248                         break;
1249                 case SelectionItem:
1250                         cursor = _cursors->selector;
1251                         break;
1252                 case ControlPointItem:
1253                         cursor = _cursors->fader;
1254                         break;
1255                 case GainLineItem:
1256                         cursor = _cursors->cross_hair;
1257                         break;
1258                 case AutomationLineItem:
1259                         cursor = _cursors->cross_hair;
1260                         break;
1261                 case StartSelectionTrimItem:
1262                         cursor = _cursors->left_side_trim;
1263                         break;
1264                 case EndSelectionTrimItem:
1265                         cursor = _cursors->right_side_trim;
1266                         break;
1267                 case FadeInItem:
1268                         cursor = _cursors->fade_in;
1269                         break;
1270                 case FadeInHandleItem:
1271                         cursor = _cursors->fade_in;
1272                         break;
1273                 case FadeInTrimHandleItem:
1274                         cursor = _cursors->fade_in;
1275                         break;
1276                 case FadeOutItem:
1277                         cursor = _cursors->fade_out;
1278                         break;
1279                 case FadeOutHandleItem:
1280                         cursor = _cursors->fade_out;
1281                         break;
1282                 case FadeOutTrimHandleItem:
1283                         cursor = _cursors->fade_out;
1284                         break;
1285                 case FeatureLineItem:
1286                         cursor = _cursors->cross_hair;
1287                         break;
1288                 case LeftFrameHandle:
1289                         if ( effective_mouse_mode() == MouseObject )  // (smart mode): if the user is in the btm half, show the trim cursor
1290                                 cursor = which_trim_cursor (true);
1291                         else
1292                                 cursor = _cursors->selector;  // (smart mode): in the top half, just show the selection (range) cursor
1293                         break;
1294                 case RightFrameHandle:
1295                         if ( effective_mouse_mode() == MouseObject )  //see above
1296                                 cursor = which_trim_cursor (false);
1297                         else
1298                                 cursor = _cursors->selector;
1299                         break;
1300                 case StartCrossFadeItem:
1301                         cursor = _cursors->fade_in;
1302                         break;
1303                 case EndCrossFadeItem:
1304                         cursor = _cursors->fade_out;
1305                         break;
1306                 case CrossfadeViewItem:
1307                         cursor = _cursors->cross_hair;
1308                         break;
1309                 case NoteItem:
1310                         cursor = _cursors->grabber_note;
1311                 default:
1312                         break;
1313                 }
1314
1315         } else if (mouse_mode == MouseDraw) {
1316
1317                 /* ControlPointItem is not really specific to region gain mode
1318                    but it is the same cursor so don't worry about this for now.
1319                    The result is that we'll see the fader cursor if we enter
1320                    non-region-gain-line control points while in MouseDraw
1321                    mode, even though we can't edit them in this mode.
1322                 */
1323
1324                 switch (type) {
1325                 case GainLineItem:
1326                 case ControlPointItem:
1327                         cursor = _cursors->fader;
1328                         break;
1329                 case NoteItem:
1330                         cursor = _cursors->grabber_note;
1331                 default:
1332                         break;
1333                 }
1334         }
1335
1336         switch (type) {
1337                 /* These items use the timebar cursor at all times */
1338         case TimecodeRulerItem:
1339         case MinsecRulerItem:
1340         case BBTRulerItem:
1341         case SamplesRulerItem:
1342                 cursor = _cursors->timebar;
1343                 break;
1344
1345                 /* These items use the grabber cursor at all times */
1346         case MeterMarkerItem:
1347         case TempoMarkerItem:
1348         case MeterBarItem:
1349         case TempoBarItem:
1350         case MarkerItem:
1351         case MarkerBarItem:
1352         case RangeMarkerBarItem:
1353         case CdMarkerBarItem:
1354         case VideoBarItem:
1355         case TransportMarkerBarItem:
1356         case DropZoneItem:
1357                 cursor = which_grabber_cursor();
1358                 break;
1359
1360         default:
1361                 break;
1362         }
1363
1364         return cursor;
1365 }
1366
1367 void
1368 Editor::choose_canvas_cursor_on_entry (ItemType type)
1369 {
1370         if (_drags->active()) {
1371                 return;
1372         }
1373
1374         Gdk::Cursor* cursor = which_canvas_cursor(type);
1375
1376         if (!_cursors->is_invalid (cursor)) {
1377                 // Push a new enter context
1378                 const EnterContext ctx = { type, CursorContext::create(*this, cursor) };
1379                 _enter_stack.push_back(ctx);
1380         }
1381 }
1382
1383 void
1384 Editor::update_all_enter_cursors ()
1385 {
1386         for (std::vector<EnterContext>::iterator i = _enter_stack.begin(); i != _enter_stack.end(); ++i) {
1387                 i->cursor_ctx->change(which_canvas_cursor(i->item_type));
1388         }
1389 }
1390
1391 double
1392 Editor::trackviews_height() const
1393 {
1394         if (!_trackview_group) {
1395                 return 0;
1396         }
1397
1398         return _visible_canvas_height - _trackview_group->canvas_origin().y;
1399 }