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