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