fix note 15897 on #5589 - only use fader cursor for region gain line when in mouse...
[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
521         if (from_headers) {
522                 alloc = controls_layout.get_allocation ();
523         } else {        
524                 alloc = _track_canvas_viewport->get_allocation ();
525
526                 /* reduce height by the height of the timebars, which happens
527                    to correspond to the position of the hv_scroll_group.
528                 */
529                 
530                 alloc.set_height (alloc.get_height() - hv_scroll_group->position().y);
531                 alloc.set_y (alloc.get_y() + hv_scroll_group->position().y);
532
533                 /* now reduce it again so that we start autoscrolling before we
534                  * move off the top or bottom of the canvas
535                  */
536
537                 alloc.set_height (alloc.get_height() - 20);
538                 alloc.set_y (alloc.get_y() + 10);
539
540                 /* the effective width of the autoscroll boundary so
541                    that we start scrolling before we hit the edge.
542                    
543                    this helps when the window is slammed up against the
544                    right edge of the screen, making it hard to scroll
545                    effectively.
546                 */
547                 
548                 if (alloc.get_width() > 20) { 
549                         alloc.set_width (alloc.get_width() - 20);
550                         alloc.set_x (alloc.get_x() + 10);
551                 } 
552
553         }
554         
555         scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), alloc.get_x() + alloc.get_width(), alloc.get_y() + alloc.get_height());
556         
557         int x, y;
558         Gdk::ModifierType mask;
559
560         get_window()->get_pointer (x, y, mask);
561
562         if ((allow_horiz && ((x < scrolling_boundary.x0 && leftmost_frame > 0) || x >= scrolling_boundary.x1)) ||
563             (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) {
564                 start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary);
565         }
566 }
567
568 bool
569 Editor::autoscroll_active () const
570 {
571         return autoscroll_connection.connected ();
572 }
573
574 bool
575 Editor::autoscroll_canvas ()
576 {
577         int x, y;
578         Gdk::ModifierType mask;
579         frameoffset_t dx = 0;
580         bool no_stop = false;
581
582         get_window()->get_pointer (x, y, mask);
583
584         VisualChange vc;
585         bool vertical_motion = false;
586         bool y_motion = false;
587
588         if (autoscroll_horizontal_allowed) {
589
590                 framepos_t new_frame = leftmost_frame;
591
592                 /* horizontal */
593
594                 if (x > autoscroll_boundary.x1) {
595
596                         /* bring it back into view */
597                         dx = x - autoscroll_boundary.x1;
598                         dx += 10 + (2 * (autoscroll_cnt/2));
599
600                         dx = pixel_to_sample (dx);
601
602                         if (leftmost_frame < max_framepos - dx) {
603                                 new_frame = leftmost_frame + dx;
604                         } else {
605                                 new_frame = max_framepos;
606                         }
607
608                         no_stop = true;
609
610                 } else if (x < autoscroll_boundary.x0) {
611                         
612                         dx = autoscroll_boundary.x0 - x;
613                         dx += 10 + (2 * (autoscroll_cnt/2));
614
615                         dx = pixel_to_sample (dx);
616
617                         if (leftmost_frame >= dx) {
618                                 new_frame = leftmost_frame - dx;
619                         } else {
620                                 new_frame = 0;
621                         }
622
623                         no_stop = true;
624                 }
625                 
626                 if (new_frame != leftmost_frame) {
627                         vc.time_origin = new_frame;
628                         vc.add (VisualChange::TimeOrigin);
629                 }
630         }
631
632         if (autoscroll_vertical_allowed) {
633                 
634                 // const double vertical_pos = vertical_adjustment.get_value();
635                 const int speed_factor = 10;
636
637                 /* vertical */ 
638                 
639                 if (y < autoscroll_boundary.y0) {
640
641                         /* scroll to make higher tracks visible */
642
643                         if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
644                                 scroll_up_one_track ();
645                                 vertical_motion = true;
646                         }
647
648                 } else if (y > autoscroll_boundary.y1) {
649
650                         if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
651                                 scroll_down_one_track ();
652                                 vertical_motion = true;
653                         }
654                 }
655
656                 no_stop = true;
657         }
658
659         if (vc.pending || vertical_motion) {
660
661                 /* change horizontal first */
662
663                 if (vc.pending) {
664                         visual_changer (vc);
665                 }
666
667                 /* now send a motion event to notify anyone who cares
668                    that we have moved to a new location (because we scrolled)
669                 */
670
671                 GdkEventMotion ev;
672
673                 ev.type = GDK_MOTION_NOTIFY;
674                 ev.state = Gdk::BUTTON1_MASK;
675                 
676                 /* the motion handler expects events in canvas coordinate space */
677
678                 /* we asked for the mouse position above (::get_pointer()) via
679                  * our own top level window (we being the Editor). Convert into 
680                  * coordinates within the canvas window.
681                  */
682
683                 int cx;
684                 int cy;
685
686                 translate_coordinates (*_track_canvas, x, y, cx, cy);
687
688                 /* clamp x and y to remain within the autoscroll boundary,
689                  * which is defined in window coordinates
690                  */
691
692                 x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1);
693                 y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1);
694
695                 /* now convert from Editor window coordinates to canvas
696                  * window coordinates
697                  */
698
699                 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
700                 ev.x = d.x;
701                 ev.y = d.y;
702
703                 motion_handler (0, (GdkEvent*) &ev, true);
704                 
705         } else if (no_stop) {
706
707                 /* not changing visual state but pointer is outside the scrolling boundary
708                  * so we still need to deliver a fake motion event 
709                  */
710
711                 GdkEventMotion ev;
712
713                 ev.type = GDK_MOTION_NOTIFY;
714                 ev.state = Gdk::BUTTON1_MASK;
715                 
716                 /* the motion handler expects events in canvas coordinate space */
717
718                 /* first convert from Editor window coordinates to canvas
719                  * window coordinates
720                  */
721
722                 int cx;
723                 int cy;
724
725                 /* clamp x and y to remain within the visible area. except
726                  * .. if horizontal scrolling is allowed, always allow us to
727                  * move back to zero
728                  */
729
730                 if (autoscroll_horizontal_allowed) {
731                         x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1);
732                 } else {
733                         x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
734                 }
735                 y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
736
737                 translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
738
739                 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
740                 ev.x = d.x;
741                 ev.y = d.y;
742
743                 motion_handler (0, (GdkEvent*) &ev, true);
744                 
745         } else {
746                 stop_canvas_autoscroll ();
747                 return false;
748         }
749
750         autoscroll_cnt++;
751
752         return true; /* call me again */
753 }       
754
755 void
756 Editor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary)
757 {
758         if (!_session) {
759                 return;
760         }
761
762         stop_canvas_autoscroll ();
763
764         autoscroll_cnt = 0;
765         autoscroll_horizontal_allowed = allow_horiz;
766         autoscroll_vertical_allowed = allow_vert;
767         autoscroll_boundary = boundary;
768
769         /* do the first scroll right now
770         */
771
772         autoscroll_canvas ();
773
774         /* scroll again at very very roughly 30FPS */
775
776         autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::autoscroll_canvas), 30);
777 }
778
779 void
780 Editor::stop_canvas_autoscroll ()
781 {
782         autoscroll_connection.disconnect ();
783 }
784
785 bool
786 Editor::left_track_canvas (GdkEventCrossing */*ev*/)
787 {
788         DropDownKeys ();
789         within_track_canvas = false;
790         set_entered_track (0);
791         set_entered_regionview (0);
792         reset_canvas_action_sensitivity (false);
793         return false;
794 }
795
796 bool
797 Editor::entered_track_canvas (GdkEventCrossing */*ev*/)
798 {
799         within_track_canvas = true;
800         reset_canvas_action_sensitivity (true);
801         return FALSE;
802 }
803
804 void
805 Editor::ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top)
806 {
807         if (track.hidden()) {
808                 return;
809         }
810
811         /* compute visible area of trackview group, as offsets from top of
812          * trackview group.
813          */
814
815         double const current_view_min_y = vertical_adjustment.get_value();
816         double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size();
817
818         double const track_min_y = track.y_position ();
819         double const track_max_y = track.y_position () + track.effective_height ();
820
821         if (!at_top && 
822             (track_min_y >= current_view_min_y &&
823              track_max_y < current_view_max_y)) {
824                 /* already visible, and caller did not ask to place it at the
825                  * top of the track canvas
826                  */
827                 return;
828         }
829
830         double new_value;
831
832         if (at_top) {
833                 new_value = track_min_y;
834         } else {
835                 if (track_min_y < current_view_min_y) {
836                         // Track is above the current view
837                         new_value = track_min_y;
838                 } else if (track_max_y > current_view_max_y) {
839                         // Track is below the current view
840                         new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size();
841                 } else {
842                         new_value = track_min_y;
843                 }
844         }
845
846         vertical_adjustment.set_value(new_value);
847 }
848
849 /** Called when the main vertical_adjustment has changed */
850 void
851 Editor::tie_vertical_scrolling ()
852 {
853         if (pending_visual_change.idle_handler_id < 0) {
854                 _summary->set_overlays_dirty ();
855         }
856 }
857
858 void
859 Editor::set_horizontal_position (double p)
860 {
861         horizontal_adjustment.set_value (p);
862
863         leftmost_frame = (framepos_t) floor (p * samples_per_pixel);
864
865         update_fixed_rulers ();
866         redisplay_tempo (true);
867
868         if (pending_visual_change.idle_handler_id < 0) {
869                 _summary->set_overlays_dirty ();
870         }
871
872         update_video_timeline();
873 }
874
875 void
876 Editor::color_handler()
877 {
878         ArdourCanvas::Color base = ARDOUR_UI::config()->get_canvasvar_RulerBase();
879         ArdourCanvas::Color text = ARDOUR_UI::config()->get_canvasvar_RulerText();
880         timecode_ruler->set_fill_color (base);
881         timecode_ruler->set_outline_color (text);
882         minsec_ruler->set_fill_color (base);
883         minsec_ruler->set_outline_color (text);
884         samples_ruler->set_fill_color (base);
885         samples_ruler->set_outline_color (text);
886         bbt_ruler->set_fill_color (base);
887         bbt_ruler->set_outline_color (text);
888         
889         playhead_cursor->set_color (ARDOUR_UI::config()->get_canvasvar_PlayHead());
890
891         meter_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_MeterBar());
892         meter_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
893
894         tempo_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TempoBar());
895         tempo_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
896
897         marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_MarkerBar());
898         marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
899
900         cd_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_CDMarkerBar());
901         cd_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
902
903         range_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeMarkerBar());
904         range_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
905
906         transport_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportMarkerBar());
907         transport_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
908
909         cd_marker_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
910         cd_marker_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
911
912         range_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
913         range_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
914
915         transport_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportDragRect());
916         transport_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportDragRect());
917
918         transport_loop_range_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportLoopRect());
919         transport_loop_range_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportLoopRect());
920
921         transport_punch_range_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportPunchRect());
922         transport_punch_range_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportPunchRect());
923
924         transport_punchin_line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_PunchLine());
925         transport_punchout_line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_PunchLine());
926
927         zoom_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ZoomRect());
928         zoom_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZoomRect());
929
930         rubberband_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RubberBandRect());
931         rubberband_rect->set_fill_color ((guint32) ARDOUR_UI::config()->get_canvasvar_RubberBandRect());
932
933         location_marker_color = ARDOUR_UI::config()->get_canvasvar_LocationMarker();
934         location_range_color = ARDOUR_UI::config()->get_canvasvar_LocationRange();
935         location_cd_marker_color = ARDOUR_UI::config()->get_canvasvar_LocationCDMarker();
936         location_loop_color = ARDOUR_UI::config()->get_canvasvar_LocationLoop();
937         location_punch_color = ARDOUR_UI::config()->get_canvasvar_LocationPunch();
938
939         refresh_location_display ();
940 /*
941         redisplay_tempo (true);
942
943         if (_session)
944               _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
945 */
946 }
947
948 double
949 Editor::horizontal_position () const
950 {
951         return sample_to_pixel (leftmost_frame);
952 }
953
954 bool
955 Editor::track_canvas_key_press (GdkEventKey*)
956 {
957         /* XXX: event does not report the modifier key pressed down, AFAICS, so use the Keyboard object instead */
958         if (mouse_mode == Editing::MouseZoom && Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
959                 set_canvas_cursor (_cursors->zoom_out, true);
960         }
961
962         return false;
963 }
964
965 bool
966 Editor::track_canvas_key_release (GdkEventKey*)
967 {
968         if (mouse_mode == Editing::MouseZoom && !Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
969                 set_canvas_cursor (_cursors->zoom_in, true);
970         }
971
972         return false;
973 }
974
975 double
976 Editor::clamp_verbose_cursor_x (double x)
977 {
978         if (x < 0) {
979                 x = 0;
980         } else {
981                 x = min (_visible_canvas_width - 200.0, x);
982         }
983         return x;
984 }
985
986 double
987 Editor::clamp_verbose_cursor_y (double y)
988 {
989         y = max (0.0, y);
990         y = min (_visible_canvas_height - 50, y);
991         return y;
992 }
993
994 ArdourCanvas::GtkCanvasViewport*
995 Editor::get_track_canvas() const
996 {
997         return _track_canvas_viewport;
998 }
999
1000 void
1001 Editor::set_canvas_cursor (Gdk::Cursor* cursor, bool save)
1002 {
1003         if (save) {
1004                 current_canvas_cursor = cursor;
1005         }
1006
1007         Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
1008
1009         if (win && cursor) {
1010                 win->set_cursor (*cursor);
1011         }
1012 }
1013
1014 void
1015 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
1016 {
1017         if (cursor) {
1018                 _cursor_stack.push (cursor);
1019                 set_canvas_cursor (cursor, false);
1020         }
1021 }
1022
1023 void
1024 Editor::pop_canvas_cursor ()
1025 {
1026         if (!_cursor_stack.empty()) {
1027                 Gdk::Cursor* cursor = _cursor_stack.top ();
1028                 _cursor_stack.pop ();
1029                 set_canvas_cursor (cursor, false);
1030         }
1031 }
1032
1033 Gdk::Cursor*
1034 Editor::which_grabber_cursor () const
1035 {
1036         Gdk::Cursor* c = _cursors->grabber;
1037
1038         if (_internal_editing) {
1039                 switch (mouse_mode) {
1040                 case MouseDraw:
1041                         c = _cursors->midi_pencil;
1042                         break;
1043
1044                 case MouseObject:
1045                         c = _cursors->grabber_note;
1046                         break;
1047
1048                 case MouseTimeFX:
1049                         c = _cursors->midi_resize;
1050                         break;
1051                         
1052                 case MouseRange:
1053                         c = _cursors->grabber_note;
1054                         break;
1055
1056                 default:
1057                         break;
1058                 }
1059
1060         } else {
1061
1062                 switch (_edit_point) {
1063                 case EditAtMouse:
1064                         c = _cursors->grabber_edit_point;
1065                         break;
1066                 default:
1067                         boost::shared_ptr<Movable> m = _movable.lock();
1068                         if (m && m->locked()) {
1069                                 c = _cursors->speaker;
1070                         }
1071                         break;
1072                 }
1073         }
1074
1075         return c;
1076 }
1077
1078 Gdk::Cursor*
1079 Editor::which_trim_cursor (bool left) const
1080 {
1081         if (!entered_regionview) {
1082                 return 0;
1083         }
1084
1085         Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
1086                 
1087         if (left) {
1088                 
1089                 if (ct & Trimmable::FrontTrimEarlier) {
1090                         return _cursors->left_side_trim;
1091                 } else {
1092                         return _cursors->left_side_trim_right_only;
1093                 }
1094         } else {
1095                 if (ct & Trimmable::EndTrimLater) {
1096                         return _cursors->right_side_trim;
1097                 } else {
1098                         return _cursors->right_side_trim_left_only;
1099                 }
1100         }
1101 }
1102
1103 Gdk::Cursor*
1104 Editor::which_mode_cursor () const
1105 {
1106         Gdk::Cursor* mode_cursor = 0;
1107
1108         switch (mouse_mode) {
1109         case MouseRange:
1110                 mode_cursor = _cursors->selector;
1111                 if (_internal_editing) {
1112                         mode_cursor = which_grabber_cursor();
1113                 }
1114                 break;
1115
1116         case MouseCut:
1117                 mode_cursor = _cursors->scissors;
1118                 break;
1119                         
1120         case MouseObject:
1121                 /* don't use mode cursor, pick a grabber cursor based on the item */
1122                 break;
1123
1124         case MouseDraw:
1125                 mode_cursor = _cursors->midi_pencil;
1126                 break;
1127
1128         case MouseGain:
1129                 mode_cursor = _cursors->cross_hair;
1130                 break;
1131
1132         case MouseZoom:
1133                 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
1134                         mode_cursor = _cursors->zoom_out;
1135                 } else {
1136                         mode_cursor = _cursors->zoom_in;
1137                 }
1138                 break;
1139
1140         case MouseTimeFX:
1141                 mode_cursor = _cursors->time_fx; // just use playhead
1142                 break;
1143
1144         case MouseAudition:
1145                 mode_cursor = _cursors->speaker;
1146                 break;
1147         }
1148
1149         /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
1150         if (!_internal_editing && get_smart_mode() ) {
1151
1152                 double x, y;
1153                 get_pointer_position (x, y);
1154
1155                 if (x >= 0 && y >= 0) {
1156                         
1157                         vector<ArdourCanvas::Item const *> items;
1158
1159                         /* Note how we choose a specific scroll group to get
1160                          * items from. This could be problematic.
1161                          */
1162                         
1163                         hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
1164                         
1165                         // first item will be the upper most 
1166                         
1167                         if (!items.empty()) {
1168                                 const ArdourCanvas::Item* i = items.front();
1169                                 
1170                                 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
1171                                         pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
1172                                         if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
1173                                                 mode_cursor = _cursors->up_down;
1174                                         }
1175                                 }
1176                         }
1177                 }
1178         }
1179
1180         return mode_cursor;
1181 }
1182
1183 Gdk::Cursor*
1184 Editor::which_track_cursor () const
1185 {
1186         Gdk::Cursor* cursor = 0;
1187
1188         assert (mouse_mode == MouseObject || get_smart_mode());
1189
1190         if (!_internal_editing) {
1191                 switch (_join_object_range_state) {
1192                 case JOIN_OBJECT_RANGE_NONE:
1193                 case JOIN_OBJECT_RANGE_OBJECT:
1194                         cursor = which_grabber_cursor ();
1195                         break;
1196                 case JOIN_OBJECT_RANGE_RANGE:
1197                         cursor = _cursors->selector;
1198                         break;
1199                 }
1200         }
1201
1202         return cursor;
1203 }
1204
1205 bool
1206 Editor::reset_canvas_cursor ()
1207 {
1208         if (!is_drawable()) {
1209                 return false;
1210         }
1211
1212         Gdk::Cursor* cursor = which_mode_cursor ();
1213
1214         if (!cursor) {
1215                 cursor = which_grabber_cursor ();
1216         }
1217                 
1218         if (cursor) {
1219                 set_canvas_cursor (cursor);
1220                 return true;
1221         }
1222
1223         return false;
1224 }
1225
1226 void
1227 Editor::choose_canvas_cursor_on_entry (GdkEventCrossing* /*event*/, ItemType type)
1228 {
1229         Gdk::Cursor* cursor = 0;
1230
1231         if (_drags->active()) {
1232                 return;
1233         }
1234
1235         cursor = which_mode_cursor ();
1236
1237         if (mouse_mode == MouseObject || get_smart_mode ()) {
1238
1239                 /* find correct cursor to use in object/smart mode */
1240
1241                 switch (type) {
1242                 case RegionItem:
1243                 case RegionViewNameHighlight:
1244                 case RegionViewName:
1245                 case WaveItem:
1246                 case StreamItem:
1247                 case AutomationTrackItem:
1248                         cursor = which_track_cursor ();
1249                         break;
1250                 case PlayheadCursorItem:
1251                         switch (_edit_point) {
1252                         case EditAtMouse:
1253                                 cursor = _cursors->grabber_edit_point;
1254                                 break;
1255                         default:
1256                                 cursor = _cursors->grabber;
1257                                 break;
1258                         }
1259                         break;
1260                 case SelectionItem:
1261                         cursor = _cursors->selector;
1262                         break;
1263                 case ControlPointItem:
1264                         cursor = _cursors->fader;
1265                         break;
1266                 case GainLineItem:
1267                         cursor = which_track_cursor ();
1268                         break;
1269                 case AutomationLineItem:
1270                         cursor = _cursors->cross_hair;
1271                         break;
1272                 case StartSelectionTrimItem:
1273                         cursor = _cursors->left_side_trim;
1274                         break;
1275                 case EndSelectionTrimItem:
1276                         cursor = _cursors->right_side_trim;
1277                         break;
1278                 case FadeInItem:
1279                         cursor = _cursors->fade_in;
1280                         break;
1281                 case FadeInHandleItem:
1282                         cursor = _cursors->fade_in;
1283                         break;
1284                 case FadeInTrimHandleItem:
1285                         cursor = _cursors->fade_in;
1286                         break;
1287                 case FadeOutItem:
1288                         cursor = _cursors->fade_out;
1289                         break;
1290                 case FadeOutHandleItem:
1291                         cursor = _cursors->fade_out;
1292                         break;
1293                 case FadeOutTrimHandleItem:
1294                         cursor = _cursors->fade_out;
1295                         break;
1296                 case NoteItem:
1297                         cursor = which_grabber_cursor();
1298                         break;
1299                 case FeatureLineItem:
1300                         cursor = _cursors->cross_hair;
1301                         break;
1302                 case LeftFrameHandle:
1303                         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
1304                                 cursor = which_trim_cursor (true);  //alternatively, one could argue that we _should_ allow trims here, and disallow range selection
1305                         break;
1306                 case RightFrameHandle:
1307                         if ( effective_mouse_mode() == MouseObject )  //see above
1308                                 cursor = which_trim_cursor (false);
1309                         break;
1310                 case StartCrossFadeItem:
1311                         cursor = _cursors->fade_in;
1312                         break;
1313                 case EndCrossFadeItem:
1314                         cursor = _cursors->fade_out;
1315                         break;
1316                 case CrossfadeViewItem:
1317                         cursor = _cursors->cross_hair;
1318                         break;
1319                 default:
1320                         break;
1321                 }
1322
1323         } else if (mouse_mode == MouseGain) {
1324                 
1325                 /* ControlPointItem is not really specific to region gain mode
1326                    but it is the same cursor so don't worry about this for now.
1327                    The result is that we'll see the fader cursor if we enter
1328                    non-region-gain-line control points while in MouseGain
1329                    mode, even though we can't edit them in this mode.
1330                 */
1331
1332                 switch (type) {
1333                 case GainLineItem:
1334                 case ControlPointItem:
1335                         cursor = _cursors->fader;
1336                         break;
1337                 default:
1338                         break;
1339                 }
1340         }
1341
1342         switch (type) {
1343                 /* These items use the timebar cursor at all times */
1344         case TimecodeRulerItem:
1345         case MinsecRulerItem:
1346         case BBTRulerItem:
1347         case SamplesRulerItem:
1348                 cursor = _cursors->timebar;
1349                 break;
1350
1351                 /* These items use the grabber cursor at all times */
1352         case MeterMarkerItem:
1353         case TempoMarkerItem:
1354         case MeterBarItem:
1355         case TempoBarItem:
1356         case MarkerItem:
1357         case MarkerBarItem:
1358         case RangeMarkerBarItem:
1359         case CdMarkerBarItem:
1360         case VideoBarItem:
1361         case TransportMarkerBarItem:
1362         case DropZoneItem:
1363                 cursor = which_grabber_cursor();
1364                 break;
1365
1366         default:
1367                 break;
1368         }
1369
1370         if (cursor) {
1371                 set_canvas_cursor (cursor, false);
1372         }
1373 }
1374
1375 double
1376 Editor::trackviews_height() const
1377 {
1378         if (!_trackview_group) {
1379                 return 0;
1380         }
1381
1382         return _visible_canvas_height - _trackview_group->canvas_origin().y;
1383 }