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