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