43b97a36f7fb928244b017ccad8aaea66f5cb310
[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 () const
586 {
587         framecnt_t session_extent_start = _session->current_start_frame();
588         framecnt_t session_extent_end = _session->current_end_frame();
589
590         //calculate the extents of all regions in every playlist
591         //NOTE:  we should listen to playlists, and cache these values so we don't calculate them every time.
592         {
593                 boost::shared_ptr<RouteList> rl = _session->get_routes();
594                 for (RouteList::iterator r = rl->begin(); r != rl->end(); ++r) {
595                         boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*r);
596                         if (tr) {
597                                 boost::shared_ptr<Playlist> pl = tr->playlist();
598                                 if ( pl && !pl->all_regions_empty() ) {
599                                         pair<framepos_t, framepos_t> e;
600                                         e = pl->get_extent();
601                                         if (e.first < session_extent_start) {
602                                                 session_extent_start = e.first;
603                                         }
604                                         if (e.second > session_extent_end) {
605                                                 session_extent_end = e.second;
606                                         }
607                                 }
608                         }
609                 }
610         }
611
612         //ToDo: also incorporate automation regions (in case the session has no audio/midi but is just used for automating plugins or the like)
613
614         //if all else fails, give us 2 minutes
615         framecnt_t const min_length = _session->nominal_frame_rate()*60*2;
616         if ( session_extent_end < min_length )
617                 session_extent_end = min_length;
618         
619         std::pair <framepos_t,framepos_t> ret (session_extent_start, session_extent_end);
620         return ret;
621 }
622
623 bool
624 Editor::autoscroll_canvas ()
625 {
626         int x, y;
627         Gdk::ModifierType mask;
628         frameoffset_t dx = 0;
629         bool no_stop = false;
630         Gtk::Window* toplevel = dynamic_cast<Gtk::Window*>(contents().get_toplevel());
631
632         if (!toplevel) {
633                 return false;
634         }
635
636         toplevel->get_window()->get_pointer (x, y, mask);
637
638         VisualChange vc;
639         bool vertical_motion = false;
640
641         if (autoscroll_horizontal_allowed) {
642
643                 framepos_t new_frame = leftmost_frame;
644
645                 /* horizontal */
646
647                 if (x > autoscroll_boundary.x1) {
648
649                         /* bring it back into view */
650                         dx = x - autoscroll_boundary.x1;
651                         dx += 10 + (2 * (autoscroll_cnt/2));
652
653                         dx = pixel_to_sample (dx);
654                         
655                         dx /= 10;  //ToDo:  make a config variable for scroll speed  zoom-behavior-tweaks
656
657                         if (leftmost_frame < max_framepos - dx) {
658                                 new_frame = leftmost_frame + dx;
659                         } else {
660                                 new_frame = max_framepos;
661                         }
662
663                         no_stop = true;
664
665                 } else if (x < autoscroll_boundary.x0) {
666
667                         dx = autoscroll_boundary.x0 - x;
668                         dx += 10 + (2 * (autoscroll_cnt/2));
669
670                         dx = pixel_to_sample (dx);
671
672                         dx /= 10;  //ToDo:  make a config variable for scroll speed  zoom-behavior-tweaks
673
674                         if (leftmost_frame >= dx) {
675                                 new_frame = leftmost_frame - dx;
676                         } else {
677                                 new_frame = 0;
678                         }
679
680                         no_stop = true;
681                 }
682
683                 if (new_frame != leftmost_frame) {
684                         vc.time_origin = new_frame;
685                         vc.add (VisualChange::TimeOrigin);
686                 }
687         }
688
689         if (autoscroll_vertical_allowed) {
690
691                 // const double vertical_pos = vertical_adjustment.get_value();
692                 const int speed_factor = 10;
693
694                 /* vertical */
695
696                 if (y < autoscroll_boundary.y0) {
697
698                         /* scroll to make higher tracks visible */
699
700                         if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
701                                 scroll_up_one_track ();
702                                 vertical_motion = true;
703                         }
704                         no_stop = true;
705
706                 } else if (y > autoscroll_boundary.y1) {
707
708                         if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
709                                 scroll_down_one_track ();
710                                 vertical_motion = true;
711                         }
712                         no_stop = true;
713                 }
714
715         }
716
717         if (vc.pending || vertical_motion) {
718
719                 /* change horizontal first */
720
721                 if (vc.pending) {
722                         visual_changer (vc);
723                 }
724
725                 /* now send a motion event to notify anyone who cares
726                    that we have moved to a new location (because we scrolled)
727                 */
728
729                 GdkEventMotion ev;
730
731                 ev.type = GDK_MOTION_NOTIFY;
732                 ev.state = Gdk::BUTTON1_MASK;
733
734                 /* the motion handler expects events in canvas coordinate space */
735
736                 /* we asked for the mouse position above (::get_pointer()) via
737                  * our own top level window (we being the Editor). Convert into
738                  * coordinates within the canvas window.
739                  */
740
741                 int cx;
742                 int cy;
743
744                 toplevel->translate_coordinates (*_track_canvas, x, y, cx, cy);
745
746                 /* clamp x and y to remain within the autoscroll boundary,
747                  * which is defined in window coordinates
748                  */
749
750                 x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1);
751                 y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1);
752
753                 /* now convert from Editor window coordinates to canvas
754                  * window coordinates
755                  */
756
757                 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
758                 ev.x = d.x;
759                 ev.y = d.y;
760                 ev.state = mask;
761
762                 motion_handler (0, (GdkEvent*) &ev, true);
763
764         } else if (no_stop) {
765
766                 /* not changing visual state but pointer is outside the scrolling boundary
767                  * so we still need to deliver a fake motion event
768                  */
769
770                 GdkEventMotion ev;
771
772                 ev.type = GDK_MOTION_NOTIFY;
773                 ev.state = Gdk::BUTTON1_MASK;
774
775                 /* the motion handler expects events in canvas coordinate space */
776
777                 /* first convert from Editor window coordinates to canvas
778                  * window coordinates
779                  */
780
781                 int cx;
782                 int cy;
783
784                 /* clamp x and y to remain within the visible area. except
785                  * .. if horizontal scrolling is allowed, always allow us to
786                  * move back to zero
787                  */
788
789                 if (autoscroll_horizontal_allowed) {
790                         x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1);
791                 } else {
792                         x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
793                 }
794                 y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
795
796                 toplevel->translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
797
798                 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
799                 ev.x = d.x;
800                 ev.y = d.y;
801                 ev.state = mask;
802
803                 motion_handler (0, (GdkEvent*) &ev, true);
804
805         } else {
806                 stop_canvas_autoscroll ();
807                 return false;
808         }
809
810         autoscroll_cnt++;
811
812         return true; /* call me again */
813 }
814
815 void
816 Editor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary)
817 {
818         if (!_session) {
819                 return;
820         }
821
822         stop_canvas_autoscroll ();
823
824         autoscroll_horizontal_allowed = allow_horiz;
825         autoscroll_vertical_allowed = allow_vert;
826         autoscroll_boundary = boundary;
827
828         /* do the first scroll right now
829         */
830
831         autoscroll_canvas ();
832
833         /* scroll again at very very roughly 30FPS */
834
835         autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::autoscroll_canvas), 30);
836 }
837
838 void
839 Editor::stop_canvas_autoscroll ()
840 {
841         autoscroll_connection.disconnect ();
842         autoscroll_cnt = 0;
843 }
844
845 Editor::EnterContext*
846 Editor::get_enter_context(ItemType type)
847 {
848         for (ssize_t i = _enter_stack.size() - 1; i >= 0; --i) {
849                 if (_enter_stack[i].item_type == type) {
850                         return &_enter_stack[i];
851                 }
852         }
853         return NULL;
854 }
855
856 bool
857 Editor::left_track_canvas (GdkEventCrossing* ev)
858 {
859         const bool was_within = within_track_canvas;
860         DropDownKeys ();
861         within_track_canvas = false;
862         set_entered_track (0);
863         set_entered_regionview (0);
864         reset_canvas_action_sensitivity (false);
865
866         if (was_within) {
867                 if (ev->detail == GDK_NOTIFY_NONLINEAR ||
868                     ev->detail == GDK_NOTIFY_NONLINEAR_VIRTUAL) {
869                         /* context menu or something similar */
870                         sensitize_the_right_region_actions (false);
871                 } else {
872                         sensitize_the_right_region_actions (true);
873                 }
874         }
875
876         return false;
877 }
878
879 bool
880 Editor::entered_track_canvas (GdkEventCrossing* ev)
881 {
882         const bool was_within = within_track_canvas;
883         within_track_canvas = true;
884         reset_canvas_action_sensitivity (true);
885
886         if (!was_within) {
887                 if (ev->detail == GDK_NOTIFY_NONLINEAR ||
888                     ev->detail == GDK_NOTIFY_NONLINEAR_VIRTUAL) {
889                         /* context menu or something similar */
890                         sensitize_the_right_region_actions (false);
891                 } else {
892                         sensitize_the_right_region_actions (true);
893                 }
894         }
895
896         return false;
897 }
898
899 void
900 Editor::ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top)
901 {
902         if (track.hidden()) {
903                 return;
904         }
905
906         /* compute visible area of trackview group, as offsets from top of
907          * trackview group.
908          */
909
910         double const current_view_min_y = vertical_adjustment.get_value();
911         double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size();
912
913         double const track_min_y = track.y_position ();
914         double const track_max_y = track.y_position () + track.effective_height ();
915
916         if (!at_top &&
917             (track_min_y >= current_view_min_y &&
918              track_max_y < current_view_max_y)) {
919                 /* already visible, and caller did not ask to place it at the
920                  * top of the track canvas
921                  */
922                 return;
923         }
924
925         double new_value;
926
927         if (at_top) {
928                 new_value = track_min_y;
929         } else {
930                 if (track_min_y < current_view_min_y) {
931                         // Track is above the current view
932                         new_value = track_min_y;
933                 } else if (track_max_y > current_view_max_y) {
934                         // Track is below the current view
935                         new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size();
936                 } else {
937                         new_value = track_min_y;
938                 }
939         }
940
941         vertical_adjustment.set_value(new_value);
942 }
943
944 /** Called when the main vertical_adjustment has changed */
945 void
946 Editor::tie_vertical_scrolling ()
947 {
948         if (pending_visual_change.idle_handler_id < 0) {
949                 _summary->set_overlays_dirty ();
950         }
951 }
952
953 void
954 Editor::set_horizontal_position (double p)
955 {
956         horizontal_adjustment.set_value (p);
957
958         leftmost_frame = (framepos_t) floor (p * samples_per_pixel);
959 }
960
961 void
962 Editor::color_handler()
963 {
964         Gtkmm2ext::Color base = UIConfiguration::instance().color ("ruler base");
965         Gtkmm2ext::Color text = UIConfiguration::instance().color ("ruler text");
966         timecode_ruler->set_fill_color (base);
967         timecode_ruler->set_outline_color (text);
968         minsec_ruler->set_fill_color (base);
969         minsec_ruler->set_outline_color (text);
970         samples_ruler->set_fill_color (base);
971         samples_ruler->set_outline_color (text);
972         bbt_ruler->set_fill_color (base);
973         bbt_ruler->set_outline_color (text);
974
975         playhead_cursor->set_color (UIConfiguration::instance().color ("play head"));
976
977         meter_bar->set_fill_color (UIConfiguration::instance().color_mod ("meter bar", "marker bar"));
978         meter_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
979
980         tempo_bar->set_fill_color (UIConfiguration::instance().color_mod ("tempo bar", "marker bar"));
981         tempo_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
982
983         marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("marker bar", "marker bar"));
984         marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
985
986         cd_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("cd marker bar", "marker bar"));
987         cd_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
988
989         range_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("range marker bar", "marker bar"));
990         range_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
991
992         transport_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("transport marker bar", "marker bar"));
993         transport_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
994
995         cd_marker_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect"));
996         cd_marker_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect"));
997
998         range_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect"));
999         range_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect"));
1000
1001         transport_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("transport drag rect"));
1002         transport_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("transport drag rect"));
1003
1004         transport_loop_range_rect->set_fill_color (UIConfiguration::instance().color_mod ("transport loop rect", "loop rectangle"));
1005         transport_loop_range_rect->set_outline_color (UIConfiguration::instance().color ("transport loop rect"));
1006
1007         transport_punch_range_rect->set_fill_color (UIConfiguration::instance().color ("transport punch rect"));
1008         transport_punch_range_rect->set_outline_color (UIConfiguration::instance().color ("transport punch rect"));
1009
1010         transport_punchin_line->set_outline_color (UIConfiguration::instance().color ("punch line"));
1011         transport_punchout_line->set_outline_color (UIConfiguration::instance().color ("punch line"));
1012
1013         rubberband_rect->set_outline_color (UIConfiguration::instance().color ("rubber band rect"));
1014         rubberband_rect->set_fill_color (UIConfiguration::instance().color_mod ("rubber band rect", "selection rect"));
1015
1016         location_marker_color = UIConfiguration::instance().color ("location marker");
1017         location_range_color = UIConfiguration::instance().color ("location range");
1018         location_cd_marker_color = UIConfiguration::instance().color ("location cd marker");
1019         location_loop_color = UIConfiguration::instance().color ("location loop");
1020         location_punch_color = UIConfiguration::instance().color ("location punch");
1021
1022         refresh_location_display ();
1023
1024         NoteBase::set_colors ();
1025
1026         /* redraw the whole thing */
1027         _track_canvas->set_background_color (UIConfiguration::instance().color ("arrange base"));
1028         _track_canvas->queue_draw ();
1029
1030 /*
1031         redisplay_tempo (true);
1032
1033         if (_session)
1034               _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
1035 */
1036 }
1037
1038 double
1039 Editor::horizontal_position () const
1040 {
1041         return sample_to_pixel (leftmost_frame);
1042 }
1043
1044 bool
1045 Editor::track_canvas_key_press (GdkEventKey*)
1046 {
1047         return false;
1048 }
1049
1050 bool
1051 Editor::track_canvas_key_release (GdkEventKey*)
1052 {
1053         return false;
1054 }
1055
1056 double
1057 Editor::clamp_verbose_cursor_x (double x)
1058 {
1059         if (x < 0) {
1060                 x = 0;
1061         } else {
1062                 x = min (_visible_canvas_width - 200.0, x);
1063         }
1064         return x;
1065 }
1066
1067 double
1068 Editor::clamp_verbose_cursor_y (double y)
1069 {
1070         y = max (0.0, y);
1071         y = min (_visible_canvas_height - 50, y);
1072         return y;
1073 }
1074
1075 ArdourCanvas::GtkCanvasViewport*
1076 Editor::get_track_canvas() const
1077 {
1078         return _track_canvas_viewport;
1079 }
1080
1081 Gdk::Cursor*
1082 Editor::get_canvas_cursor () const
1083 {
1084         /* The top of the cursor stack is always the currently visible cursor. */
1085         return _cursor_stack.back();
1086 }
1087
1088 void
1089 Editor::set_canvas_cursor (Gdk::Cursor* cursor)
1090 {
1091         Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
1092
1093         if (win && !_cursors->is_invalid (cursor)) {
1094                 /* glibmm 2.4 doesn't allow null cursor pointer because it uses
1095                    a Gdk::Cursor& as the argument to Gdk::Window::set_cursor().
1096                    But a null pointer just means "use parent window cursor",
1097                    and so should be allowed. Gtkmm 3.x has fixed this API.
1098
1099                    For now, drop down and use C API
1100                 */
1101                 gdk_window_set_cursor (win->gobj(), cursor ? cursor->gobj() : 0);
1102         }
1103 }
1104
1105 size_t
1106 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
1107 {
1108         if (!_cursors->is_invalid (cursor)) {
1109                 _cursor_stack.push_back (cursor);
1110                 set_canvas_cursor (cursor);
1111         }
1112         return _cursor_stack.size() - 1;
1113 }
1114
1115 void
1116 Editor::pop_canvas_cursor ()
1117 {
1118         while (true) {
1119                 if (_cursor_stack.size() <= 1) {
1120                         PBD::error << "attempt to pop default cursor" << endmsg;
1121                         return;
1122                 }
1123
1124                 _cursor_stack.pop_back();
1125                 if (_cursor_stack.back()) {
1126                         /* Popped to an existing cursor, we're done.  Otherwise, the
1127                            context that created this cursor has been destroyed, so we need
1128                            to skip to the next down the stack. */
1129                         set_canvas_cursor (_cursor_stack.back());
1130                         return;
1131                 }
1132         }
1133 }
1134
1135 Gdk::Cursor*
1136 Editor::which_grabber_cursor () const
1137 {
1138         Gdk::Cursor* c = _cursors->grabber;
1139
1140         switch (_edit_point) {
1141         case EditAtMouse:
1142                 c = _cursors->grabber_edit_point;
1143                 break;
1144         default:
1145                 boost::shared_ptr<Movable> m = _movable.lock();
1146                 if (m && m->locked()) {
1147                         c = _cursors->speaker;
1148                 }
1149                 break;
1150         }
1151
1152         return c;
1153 }
1154
1155 Gdk::Cursor*
1156 Editor::which_trim_cursor (bool left) const
1157 {
1158         if (!entered_regionview) {
1159                 return 0;
1160         }
1161
1162         Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
1163
1164         if (left) {
1165
1166                 if (ct & Trimmable::FrontTrimEarlier) {
1167                         return _cursors->left_side_trim;
1168                 } else {
1169                         return _cursors->left_side_trim_right_only;
1170                 }
1171         } else {
1172                 if (ct & Trimmable::EndTrimLater) {
1173                         return _cursors->right_side_trim;
1174                 } else {
1175                         return _cursors->right_side_trim_left_only;
1176                 }
1177         }
1178 }
1179
1180 Gdk::Cursor*
1181 Editor::which_mode_cursor () const
1182 {
1183         Gdk::Cursor* mode_cursor = MouseCursors::invalid_cursor ();
1184
1185         switch (mouse_mode) {
1186         case MouseRange:
1187                 mode_cursor = _cursors->selector;
1188                 break;
1189
1190         case MouseCut:
1191                 mode_cursor = _cursors->scissors;
1192                 break;
1193
1194         case MouseObject:
1195         case MouseContent:
1196                 /* don't use mode cursor, pick a grabber cursor based on the item */
1197                 break;
1198
1199         case MouseDraw:
1200                 mode_cursor = _cursors->midi_pencil;
1201                 break;
1202
1203         case MouseTimeFX:
1204                 mode_cursor = _cursors->time_fx; // just use playhead
1205                 break;
1206
1207         case MouseAudition:
1208                 mode_cursor = _cursors->speaker;
1209                 break;
1210         }
1211
1212         /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
1213         if (get_smart_mode()) {
1214
1215                 double x, y;
1216                 get_pointer_position (x, y);
1217
1218                 if (x >= 0 && y >= 0) {
1219
1220                         vector<ArdourCanvas::Item const *> items;
1221
1222                         /* Note how we choose a specific scroll group to get
1223                          * items from. This could be problematic.
1224                          */
1225
1226                         hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
1227
1228                         // first item will be the upper most
1229
1230                         if (!items.empty()) {
1231                                 const ArdourCanvas::Item* i = items.front();
1232
1233                                 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
1234                                         pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
1235                                         if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
1236                                                 mode_cursor = _cursors->up_down;
1237                                         }
1238                                 }
1239                         }
1240                 }
1241         }
1242
1243         return mode_cursor;
1244 }
1245
1246 Gdk::Cursor*
1247 Editor::which_track_cursor () const
1248 {
1249         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
1250
1251         switch (_join_object_range_state) {
1252         case JOIN_OBJECT_RANGE_NONE:
1253         case JOIN_OBJECT_RANGE_OBJECT:
1254                 cursor = which_grabber_cursor ();
1255                 break;
1256         case JOIN_OBJECT_RANGE_RANGE:
1257                 cursor = _cursors->selector;
1258                 break;
1259         }
1260
1261         return cursor;
1262 }
1263
1264 Gdk::Cursor*
1265 Editor::which_canvas_cursor(ItemType type) const
1266 {
1267         Gdk::Cursor* cursor = which_mode_cursor ();
1268
1269         if (mouse_mode == MouseRange) {
1270                 switch (type) {
1271                 case StartSelectionTrimItem:
1272                         cursor = _cursors->left_side_trim;
1273                         break;
1274                 case EndSelectionTrimItem:
1275                         cursor = _cursors->right_side_trim;
1276                         break;
1277                 default:
1278                         break;
1279                 }
1280         }
1281
1282         if ((mouse_mode == MouseObject || get_smart_mode ()) ||
1283             mouse_mode == MouseContent) {
1284
1285                 /* find correct cursor to use in object/smart mode */
1286
1287                 switch (type) {
1288                 case RegionItem:
1289                 /* We don't choose a cursor for these items on top of a region view,
1290                    because this would push a new context on the enter stack which
1291                    means switching the region context for things like smart mode
1292                    won't actualy change the cursor. */
1293                 // case RegionViewNameHighlight:
1294                 // case RegionViewName:
1295                 // case WaveItem:
1296                 case StreamItem:
1297                 case AutomationTrackItem:
1298                         cursor = which_track_cursor ();
1299                         break;
1300                 case PlayheadCursorItem:
1301                         switch (_edit_point) {
1302                         case EditAtMouse:
1303                                 cursor = _cursors->grabber_edit_point;
1304                                 break;
1305                         default:
1306                                 cursor = _cursors->grabber;
1307                                 break;
1308                         }
1309                         break;
1310                 case SelectionItem:
1311                         cursor = _cursors->selector;
1312                         break;
1313                 case ControlPointItem:
1314                         cursor = _cursors->fader;
1315                         break;
1316                 case GainLineItem:
1317                         cursor = _cursors->cross_hair;
1318                         break;
1319                 case AutomationLineItem:
1320                         cursor = _cursors->cross_hair;
1321                         break;
1322                 case StartSelectionTrimItem:
1323                         cursor = _cursors->left_side_trim;
1324                         break;
1325                 case EndSelectionTrimItem:
1326                         cursor = _cursors->right_side_trim;
1327                         break;
1328                 case FadeInItem:
1329                         cursor = _cursors->fade_in;
1330                         break;
1331                 case FadeInHandleItem:
1332                         cursor = _cursors->fade_in;
1333                         break;
1334                 case FadeInTrimHandleItem:
1335                         cursor = _cursors->fade_in;
1336                         break;
1337                 case FadeOutItem:
1338                         cursor = _cursors->fade_out;
1339                         break;
1340                 case FadeOutHandleItem:
1341                         cursor = _cursors->fade_out;
1342                         break;
1343                 case FadeOutTrimHandleItem:
1344                         cursor = _cursors->fade_out;
1345                         break;
1346                 case FeatureLineItem:
1347                         cursor = _cursors->cross_hair;
1348                         break;
1349                 case LeftFrameHandle:
1350                         if ( effective_mouse_mode() == MouseObject )  // (smart mode): if the user is in the btm half, show the trim cursor
1351                                 cursor = which_trim_cursor (true);
1352                         else
1353                                 cursor = _cursors->selector;  // (smart mode): in the top half, just show the selection (range) cursor
1354                         break;
1355                 case RightFrameHandle:
1356                         if ( effective_mouse_mode() == MouseObject )  //see above
1357                                 cursor = which_trim_cursor (false);
1358                         else
1359                                 cursor = _cursors->selector;
1360                         break;
1361                 case StartCrossFadeItem:
1362                         cursor = _cursors->fade_in;
1363                         break;
1364                 case EndCrossFadeItem:
1365                         cursor = _cursors->fade_out;
1366                         break;
1367                 case CrossfadeViewItem:
1368                         cursor = _cursors->cross_hair;
1369                         break;
1370                 case NoteItem:
1371                         cursor = _cursors->grabber_note;
1372                 default:
1373                         break;
1374                 }
1375
1376         } else if (mouse_mode == MouseDraw) {
1377
1378                 /* ControlPointItem is not really specific to region gain mode
1379                    but it is the same cursor so don't worry about this for now.
1380                    The result is that we'll see the fader cursor if we enter
1381                    non-region-gain-line control points while in MouseDraw
1382                    mode, even though we can't edit them in this mode.
1383                 */
1384
1385                 switch (type) {
1386                 case GainLineItem:
1387                 case ControlPointItem:
1388                         cursor = _cursors->fader;
1389                         break;
1390                 case NoteItem:
1391                         cursor = _cursors->grabber_note;
1392                 default:
1393                         break;
1394                 }
1395         }
1396
1397         switch (type) {
1398                 /* These items use the timebar cursor at all times */
1399         case TimecodeRulerItem:
1400         case MinsecRulerItem:
1401         case BBTRulerItem:
1402         case SamplesRulerItem:
1403                 cursor = _cursors->timebar;
1404                 break;
1405
1406                 /* These items use the grabber cursor at all times */
1407         case MeterMarkerItem:
1408         case TempoMarkerItem:
1409         case MeterBarItem:
1410         case TempoBarItem:
1411         case MarkerItem:
1412         case MarkerBarItem:
1413         case RangeMarkerBarItem:
1414         case CdMarkerBarItem:
1415         case VideoBarItem:
1416         case TransportMarkerBarItem:
1417         case DropZoneItem:
1418                 cursor = which_grabber_cursor();
1419                 break;
1420
1421         default:
1422                 break;
1423         }
1424
1425         return cursor;
1426 }
1427
1428 void
1429 Editor::choose_canvas_cursor_on_entry (ItemType type)
1430 {
1431         if (_drags->active()) {
1432                 return;
1433         }
1434
1435         Gdk::Cursor* cursor = which_canvas_cursor(type);
1436
1437         if (!_cursors->is_invalid (cursor)) {
1438                 // Push a new enter context
1439                 const EnterContext ctx = { type, CursorContext::create(*this, cursor) };
1440                 _enter_stack.push_back(ctx);
1441         }
1442 }
1443
1444 void
1445 Editor::update_all_enter_cursors ()
1446 {
1447         for (std::vector<EnterContext>::iterator i = _enter_stack.begin(); i != _enter_stack.end(); ++i) {
1448                 i->cursor_ctx->change(which_canvas_cursor(i->item_type));
1449         }
1450 }
1451
1452 double
1453 Editor::trackviews_height() const
1454 {
1455         if (!_trackview_group) {
1456                 return 0;
1457         }
1458
1459         return _visible_canvas_height - _trackview_group->canvas_origin().y;
1460 }