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