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