13fd6fe8b94b3ec92dc19bfba21c4207016dd157
[ardour.git] / gtk2_ardour / editor_canvas.cc
1 /*
2     Copyright (C) 2005 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #ifdef WAF_BUILD
21 #include "gtk2ardour-config.h"
22 #endif
23
24 #include "gtkmm2ext/utils.h"
25
26 #include "ardour/profile.h"
27 #include "ardour/rc_configuration.h"
28 #include "ardour/smf_source.h"
29
30 #include "pbd/error.h"
31
32 #include "canvas/canvas.h"
33 #include "canvas/rectangle.h"
34 #include "canvas/pixbuf.h"
35 #include "canvas/scroll_group.h"
36 #include "canvas/text.h"
37 #include "canvas/debug.h"
38
39 #include "ardour_ui.h"
40 #include "automation_time_axis.h"
41 #include "editor.h"
42 #include "editing.h"
43 #include "rgb_macros.h"
44 #include "utils.h"
45 #include "audio_time_axis.h"
46 #include "editor_drag.h"
47 #include "region_view.h"
48 #include "editor_group_tabs.h"
49 #include "editor_summary.h"
50 #include "video_timeline.h"
51 #include "keyboard.h"
52 #include "editor_cursors.h"
53 #include "mouse_cursors.h"
54 #include "ui_config.h"
55 #include "verbose_cursor.h"
56
57 #include "i18n.h"
58
59 using namespace std;
60 using namespace ARDOUR;
61 using namespace ARDOUR_UI_UTILS;
62 using namespace PBD;
63 using namespace Gtk;
64 using namespace Glib;
65 using namespace Gtkmm2ext;
66 using namespace Editing;
67
68 void
69 Editor::initialize_canvas ()
70 {
71         _track_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, vertical_adjustment);
72         _track_canvas = _track_canvas_viewport->canvas ();
73
74         _track_canvas->set_background_color (UIConfiguration::instance().color ("arrange base"));
75
76         /* scroll group for items that should not automatically scroll
77          *  (e.g verbose cursor). It shares the canvas coordinate space.
78         */
79         no_scroll_group = new ArdourCanvas::Container (_track_canvas->root());
80
81         ArdourCanvas::ScrollGroup* hsg;
82         ArdourCanvas::ScrollGroup* hg;
83         ArdourCanvas::ScrollGroup* cg;
84
85         h_scroll_group = hg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
86         CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll");
87         _track_canvas->add_scroller (*hg);
88
89         hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_track_canvas->root(),
90                                                                ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically|
91                                                                                                              ArdourCanvas::ScrollGroup::ScrollsHorizontally));
92         CANVAS_DEBUG_NAME (hv_scroll_group, "canvas hv scroll");
93         _track_canvas->add_scroller (*hsg);
94
95         cursor_scroll_group = cg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
96         CANVAS_DEBUG_NAME (cursor_scroll_group, "canvas cursor scroll");
97         _track_canvas->add_scroller (*cg);
98
99         _verbose_cursor = new VerboseCursor (this);
100
101         /* on the bottom, an image */
102
103         if (Profile->get_sae()) {
104                 Image img (::get_icon (X_("saelogo")));
105                 // logo_item = new ArdourCanvas::Pixbuf (_track_canvas->root(), 0.0, 0.0, img.get_pixbuf());
106                 // logo_item->property_height_in_pixels() = true;
107                 // logo_item->property_width_in_pixels() = true;
108                 // logo_item->property_height_set() = true;
109                 // logo_item->property_width_set() = true;
110                 // logo_item->show ();
111         }
112
113         /*a group to hold global rects like punch/loop indicators */
114         global_rect_group = new ArdourCanvas::Container (hv_scroll_group);
115         CANVAS_DEBUG_NAME (global_rect_group, "global rect group");
116
117         transport_loop_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
118         CANVAS_DEBUG_NAME (transport_loop_range_rect, "loop rect");
119         transport_loop_range_rect->hide();
120
121         transport_punch_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
122         CANVAS_DEBUG_NAME (transport_punch_range_rect, "punch rect");
123         transport_punch_range_rect->hide();
124
125         /*a group to hold time (measure) lines */
126         time_line_group = new ArdourCanvas::Container (h_scroll_group);
127         CANVAS_DEBUG_NAME (time_line_group, "time line group");
128
129         _trackview_group = new ArdourCanvas::Container (hv_scroll_group);
130         CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
131         
132         // used as rubberband rect
133         rubberband_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
134         rubberband_rect->hide();
135
136         /* a group to hold stuff while it gets dragged around. Must be the
137          * uppermost (last) group with hv_scroll_group as a parent
138          */
139         _drag_motion_group = new ArdourCanvas::Container (hv_scroll_group);
140         CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion");
141
142         /* TIME BAR CANVAS */
143         
144         _time_markers_group = new ArdourCanvas::Container (h_scroll_group);
145         CANVAS_DEBUG_NAME (_time_markers_group, "time bars");
146
147         cd_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, 0.0));
148         CANVAS_DEBUG_NAME (cd_marker_group, "cd marker group");
149         /* the vide is temporarily placed a the same location as the
150            cd_marker_group, but is moved later.
151         */
152         videotl_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0));
153         CANVAS_DEBUG_NAME (videotl_group, "videotl group");
154         marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, timebar_height + 1.0));
155         CANVAS_DEBUG_NAME (marker_group, "marker group");
156         transport_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 2.0) + 1.0));
157         CANVAS_DEBUG_NAME (transport_marker_group, "transport marker group");
158         range_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 3.0) + 1.0));
159         CANVAS_DEBUG_NAME (range_marker_group, "range marker group");
160         tempo_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 4.0) + 1.0));
161         CANVAS_DEBUG_NAME (tempo_group, "tempo group");
162         meter_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0));
163         CANVAS_DEBUG_NAME (meter_group, "meter group");
164
165         meter_bar = new ArdourCanvas::Rectangle (meter_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
166         CANVAS_DEBUG_NAME (meter_bar, "meter Bar");
167         meter_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
168
169         tempo_bar = new ArdourCanvas::Rectangle (tempo_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
170         CANVAS_DEBUG_NAME (tempo_bar, "Tempo  Bar");
171         tempo_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
172
173         range_marker_bar = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
174         CANVAS_DEBUG_NAME (range_marker_bar, "Range Marker Bar");
175         range_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
176
177         transport_marker_bar = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
178         CANVAS_DEBUG_NAME (transport_marker_bar, "transport Marker Bar");
179         transport_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
180
181         marker_bar = new ArdourCanvas::Rectangle (marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
182         CANVAS_DEBUG_NAME (marker_bar, "Marker Bar");
183         marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
184
185         cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
186         CANVAS_DEBUG_NAME (cd_marker_bar, "CD Marker Bar");
187         cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
188
189         ARDOUR_UI::instance()->video_timeline = new VideoTimeLine(this, videotl_group, (timebar_height * videotl_bar_height));
190         
191         cd_marker_bar_drag_rect = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
192         CANVAS_DEBUG_NAME (cd_marker_bar_drag_rect, "cd marker drag");
193         cd_marker_bar_drag_rect->set_outline (false);
194         cd_marker_bar_drag_rect->hide ();
195
196         range_bar_drag_rect = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
197         CANVAS_DEBUG_NAME (range_bar_drag_rect, "range drag");
198         range_bar_drag_rect->set_outline (false);
199         range_bar_drag_rect->hide ();
200
201         transport_bar_drag_rect = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
202         CANVAS_DEBUG_NAME (transport_bar_drag_rect, "transport drag");
203         transport_bar_drag_rect->set_outline (false);
204         transport_bar_drag_rect->hide ();
205
206         transport_punchin_line = new ArdourCanvas::Line (hv_scroll_group);
207         transport_punchin_line->set_x0 (0);
208         transport_punchin_line->set_y0 (0);
209         transport_punchin_line->set_x1 (0);
210         transport_punchin_line->set_y1 (ArdourCanvas::COORD_MAX);
211         transport_punchin_line->hide ();
212
213         transport_punchout_line  = new ArdourCanvas::Line (hv_scroll_group);
214         transport_punchout_line->set_x0 (0);
215         transport_punchout_line->set_y0 (0);
216         transport_punchout_line->set_x1 (0);
217         transport_punchout_line->set_y1 (ArdourCanvas::COORD_MAX);
218         transport_punchout_line->hide();
219
220         tempo_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_tempo_bar_event), tempo_bar));
221         meter_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_meter_bar_event), meter_bar));
222         marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_marker_bar_event), marker_bar));
223         cd_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_cd_marker_bar_event), cd_marker_bar));
224         videotl_group->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_videotl_bar_event), videotl_group));
225         range_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_range_marker_bar_event), range_marker_bar));
226         transport_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_transport_marker_bar_event), transport_marker_bar));
227
228         playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event);
229
230         if (logo_item) {
231                 logo_item->lower_to_bottom ();
232         }
233
234         _canvas_drop_zone = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, 0.0));
235         /* this thing is transparent */
236         _canvas_drop_zone->set_fill (false);
237         _canvas_drop_zone->set_outline (false);
238         _canvas_drop_zone->Event.connect (sigc::mem_fun (*this, &Editor::canvas_drop_zone_event));
239
240         /* these signals will initially be delivered to the canvas itself, but if they end up remaining unhandled, they are passed to Editor-level
241            handlers.
242         */
243
244         _track_canvas->signal_scroll_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_scroll_event), true));
245         _track_canvas->signal_motion_notify_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_motion_notify_event));
246         _track_canvas->signal_button_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_press_event));
247         _track_canvas->signal_button_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_release_event));
248         _track_canvas->signal_drag_motion().connect (sigc::mem_fun (*this, &Editor::track_canvas_drag_motion));
249         _track_canvas->signal_key_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_press));
250         _track_canvas->signal_key_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_release));
251
252         _track_canvas->set_name ("EditorMainCanvas");
253         _track_canvas->add_events (Gdk::POINTER_MOTION_HINT_MASK | Gdk::SCROLL_MASK | Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
254         _track_canvas->signal_leave_notify_event().connect (sigc::mem_fun(*this, &Editor::left_track_canvas), false);
255         _track_canvas->signal_enter_notify_event().connect (sigc::mem_fun(*this, &Editor::entered_track_canvas), false);
256         _track_canvas->set_flags (CAN_FOCUS);
257
258         /* set up drag-n-drop */
259
260         vector<TargetEntry> target_table;
261
262         // Drag-N-Drop from the region list can generate this target
263         target_table.push_back (TargetEntry ("regions"));
264
265         target_table.push_back (TargetEntry ("text/plain"));
266         target_table.push_back (TargetEntry ("text/uri-list"));
267         target_table.push_back (TargetEntry ("application/x-rootwin-drop"));
268
269         _track_canvas->drag_dest_set (target_table);
270         _track_canvas->signal_drag_data_received().connect (sigc::mem_fun(*this, &Editor::track_canvas_drag_data_received));
271
272         _track_canvas_viewport->signal_size_allocate().connect (sigc::mem_fun(*this, &Editor::track_canvas_viewport_allocate));
273
274         initialize_rulers ();
275
276         UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &Editor::color_handler));
277         color_handler();
278
279 }
280
281 void
282 Editor::track_canvas_viewport_allocate (Gtk::Allocation alloc)
283 {
284         _canvas_viewport_allocation = alloc;
285         track_canvas_viewport_size_allocated ();
286 }
287
288 void
289 Editor::track_canvas_viewport_size_allocated ()
290 {
291         bool height_changed = _visible_canvas_height != _canvas_viewport_allocation.get_height();
292
293         _visible_canvas_width  = _canvas_viewport_allocation.get_width ();
294         _visible_canvas_height = _canvas_viewport_allocation.get_height ();
295
296         _canvas_drop_zone->set_y1 (_canvas_drop_zone->y0() + (_visible_canvas_height - 20.0));
297
298         // SHOWTRACKS
299
300         if (height_changed) {
301
302                 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
303                         i->second->canvas_height_set (_visible_canvas_height);
304                 }
305
306                 vertical_adjustment.set_page_size (_visible_canvas_height);
307                 if ((vertical_adjustment.get_value() + _visible_canvas_height) >= vertical_adjustment.get_upper()) {
308                         /*
309                            We're increasing the size of the canvas while the bottom is visible.
310                            We scroll down to keep in step with the controls layout.
311                         */
312                         vertical_adjustment.set_value (_full_canvas_height - _visible_canvas_height);
313                 }
314
315                 set_visible_track_count (_visible_track_count);
316         }
317
318         update_fixed_rulers();
319         redisplay_tempo (false);
320         _summary->set_overlays_dirty ();
321 }
322
323 void
324 Editor::reset_controls_layout_width ()
325 {
326         GtkRequisition req = { 0, 0 };
327         gint w;
328
329         edit_controls_vbox.size_request (req);
330         w = req.width;
331
332         if (_group_tabs->is_mapped()) {
333                 _group_tabs->size_request (req);
334                 w += req.width;
335         }
336
337         /* the controls layout has no horizontal scrolling, its visible
338            width is always equal to the total width of its contents.
339         */
340
341         controls_layout.property_width() = w;
342         controls_layout.property_width_request() = w;
343 }
344
345 void
346 Editor::reset_controls_layout_height (int32_t h)
347 {
348         /* ensure that the rect that represents the "bottom" of the canvas
349          * (the drag-n-drop zone) is, in fact, at the bottom.
350          */
351
352         _canvas_drop_zone->set_position (ArdourCanvas::Duple (0, h));
353
354         /* track controls layout must span the full height of "h" (all tracks)
355          * plus the bottom rect.
356          */
357
358         h += _canvas_drop_zone->height ();
359
360         /* set the height of the scrollable area (i.e. the sum of all contained widgets)
361          * for the controls layout. The size request is set elsewhere.
362          */
363
364         controls_layout.property_height() = h;
365
366 }
367
368 bool
369 Editor::track_canvas_map_handler (GdkEventAny* /*ev*/)
370 {
371         if (!_cursor_stack.empty()) {
372                 set_canvas_cursor (get_canvas_cursor());
373         } else {
374                 PBD::error << "cursor stack is empty" << endmsg;
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                 InstrumentSelector is; // instantiation builds instrument-list and sets default.
429                 do_import (midi_paths, Editing::ImportDistinctFiles, ImportAsTrack, SrcBest, frame, is.selected_instrument());
430                 
431                 if (Profile->get_sae() || UIConfiguration::instance().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() || UIConfiguration::instance().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 (!UIConfiguration::instance().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
587         if (autoscroll_horizontal_allowed) {
588
589                 framepos_t new_frame = leftmost_frame;
590
591                 /* horizontal */
592
593                 if (x > autoscroll_boundary.x1) {
594
595                         /* bring it back into view */
596                         dx = x - autoscroll_boundary.x1;
597                         dx += 10 + (2 * (autoscroll_cnt/2));
598
599                         dx = pixel_to_sample (dx);
600
601                         if (leftmost_frame < max_framepos - dx) {
602                                 new_frame = leftmost_frame + dx;
603                         } else {
604                                 new_frame = max_framepos;
605                         }
606
607                         no_stop = true;
608
609                 } else if (x < autoscroll_boundary.x0) {
610                         
611                         dx = autoscroll_boundary.x0 - x;
612                         dx += 10 + (2 * (autoscroll_cnt/2));
613
614                         dx = pixel_to_sample (dx);
615
616                         if (leftmost_frame >= dx) {
617                                 new_frame = leftmost_frame - dx;
618                         } else {
619                                 new_frame = 0;
620                         }
621
622                         no_stop = true;
623                 }
624                 
625                 if (new_frame != leftmost_frame) {
626                         vc.time_origin = new_frame;
627                         vc.add (VisualChange::TimeOrigin);
628                 }
629         }
630
631         if (autoscroll_vertical_allowed) {
632                 
633                 // const double vertical_pos = vertical_adjustment.get_value();
634                 const int speed_factor = 10;
635
636                 /* vertical */
637                 
638                 if (y < autoscroll_boundary.y0) {
639
640                         /* scroll to make higher tracks visible */
641
642                         if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
643                                 scroll_up_one_track ();
644                                 vertical_motion = true;
645                         }
646
647                 } else if (y > autoscroll_boundary.y1) {
648
649                         if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
650                                 scroll_down_one_track ();
651                                 vertical_motion = true;
652                         }
653                 }
654
655                 no_stop = true;
656         }
657
658         if (vc.pending || vertical_motion) {
659
660                 /* change horizontal first */
661
662                 if (vc.pending) {
663                         visual_changer (vc);
664                 }
665
666                 /* now send a motion event to notify anyone who cares
667                    that we have moved to a new location (because we scrolled)
668                 */
669
670                 GdkEventMotion ev;
671
672                 ev.type = GDK_MOTION_NOTIFY;
673                 ev.state = Gdk::BUTTON1_MASK;
674                 
675                 /* the motion handler expects events in canvas coordinate space */
676
677                 /* we asked for the mouse position above (::get_pointer()) via
678                  * our own top level window (we being the Editor). Convert into
679                  * coordinates within the canvas window.
680                  */
681
682                 int cx;
683                 int cy;
684
685                 translate_coordinates (*_track_canvas, x, y, cx, cy);
686
687                 /* clamp x and y to remain within the autoscroll boundary,
688                  * which is defined in window coordinates
689                  */
690
691                 x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1);
692                 y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1);
693
694                 /* now convert from Editor window coordinates to canvas
695                  * window coordinates
696                  */
697
698                 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
699                 ev.x = d.x;
700                 ev.y = d.y;
701
702                 motion_handler (0, (GdkEvent*) &ev, true);
703                 
704         } else if (no_stop) {
705
706                 /* not changing visual state but pointer is outside the scrolling boundary
707                  * so we still need to deliver a fake motion event
708                  */
709
710                 GdkEventMotion ev;
711
712                 ev.type = GDK_MOTION_NOTIFY;
713                 ev.state = Gdk::BUTTON1_MASK;
714                 
715                 /* the motion handler expects events in canvas coordinate space */
716
717                 /* first convert from Editor window coordinates to canvas
718                  * window coordinates
719                  */
720
721                 int cx;
722                 int cy;
723
724                 /* clamp x and y to remain within the visible area. except
725                  * .. if horizontal scrolling is allowed, always allow us to
726                  * move back to zero
727                  */
728
729                 if (autoscroll_horizontal_allowed) {
730                         x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1);
731                 } else {
732                         x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
733                 }
734                 y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
735
736                 translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
737
738                 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
739                 ev.x = d.x;
740                 ev.y = d.y;
741
742                 motion_handler (0, (GdkEvent*) &ev, true);
743                 
744         } else {
745                 stop_canvas_autoscroll ();
746                 return false;
747         }
748
749         autoscroll_cnt++;
750
751         return true; /* call me again */
752 }       
753
754 void
755 Editor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary)
756 {
757         if (!_session) {
758                 return;
759         }
760
761         stop_canvas_autoscroll ();
762
763         autoscroll_cnt = 0;
764         autoscroll_horizontal_allowed = allow_horiz;
765         autoscroll_vertical_allowed = allow_vert;
766         autoscroll_boundary = boundary;
767
768         /* do the first scroll right now
769         */
770
771         autoscroll_canvas ();
772
773         /* scroll again at very very roughly 30FPS */
774
775         autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::autoscroll_canvas), 30);
776 }
777
778 void
779 Editor::stop_canvas_autoscroll ()
780 {
781         autoscroll_connection.disconnect ();
782 }
783
784 Editor::EnterContext*
785 Editor::get_enter_context(ItemType type)
786 {
787         for (ssize_t i = _enter_stack.size() - 1; i >= 0; --i) {
788                 if (_enter_stack[i].item_type == type) {
789                         return &_enter_stack[i];
790                 }
791         }
792         return NULL;
793 }
794
795 bool
796 Editor::left_track_canvas (GdkEventCrossing */*ev*/)
797 {
798         DropDownKeys ();
799         within_track_canvas = false;
800         set_entered_track (0);
801         set_entered_regionview (0);
802         reset_canvas_action_sensitivity (false);
803         return false;
804 }
805
806 bool
807 Editor::entered_track_canvas (GdkEventCrossing */*ev*/)
808 {
809         within_track_canvas = true;
810         reset_canvas_action_sensitivity (true);
811         return FALSE;
812 }
813
814 void
815 Editor::ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top)
816 {
817         if (track.hidden()) {
818                 return;
819         }
820
821         /* compute visible area of trackview group, as offsets from top of
822          * trackview group.
823          */
824
825         double const current_view_min_y = vertical_adjustment.get_value();
826         double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size();
827
828         double const track_min_y = track.y_position ();
829         double const track_max_y = track.y_position () + track.effective_height ();
830
831         if (!at_top &&
832             (track_min_y >= current_view_min_y &&
833              track_max_y < current_view_max_y)) {
834                 /* already visible, and caller did not ask to place it at the
835                  * top of the track canvas
836                  */
837                 return;
838         }
839
840         double new_value;
841
842         if (at_top) {
843                 new_value = track_min_y;
844         } else {
845                 if (track_min_y < current_view_min_y) {
846                         // Track is above the current view
847                         new_value = track_min_y;
848                 } else if (track_max_y > current_view_max_y) {
849                         // Track is below the current view
850                         new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size();
851                 } else {
852                         new_value = track_min_y;
853                 }
854         }
855
856         vertical_adjustment.set_value(new_value);
857 }
858
859 /** Called when the main vertical_adjustment has changed */
860 void
861 Editor::tie_vertical_scrolling ()
862 {
863         if (pending_visual_change.idle_handler_id < 0) {
864                 _summary->set_overlays_dirty ();
865         }
866 }
867
868 void
869 Editor::set_horizontal_position (double p)
870 {
871         horizontal_adjustment.set_value (p);
872
873         leftmost_frame = (framepos_t) floor (p * samples_per_pixel);
874
875         update_fixed_rulers ();
876         redisplay_tempo (true);
877
878         if (pending_visual_change.idle_handler_id < 0) {
879                 _summary->set_overlays_dirty ();
880         }
881
882         update_video_timeline();
883 }
884
885 void
886 Editor::color_handler()
887 {
888         ArdourCanvas::Color base = UIConfiguration::instance().color ("ruler base");
889         ArdourCanvas::Color text = UIConfiguration::instance().color ("ruler text");
890         timecode_ruler->set_fill_color (base);
891         timecode_ruler->set_outline_color (text);
892         minsec_ruler->set_fill_color (base);
893         minsec_ruler->set_outline_color (text);
894         samples_ruler->set_fill_color (base);
895         samples_ruler->set_outline_color (text);
896         bbt_ruler->set_fill_color (base);
897         bbt_ruler->set_outline_color (text);
898         
899         playhead_cursor->set_color (UIConfiguration::instance().color ("play head"));
900
901         meter_bar->set_fill_color (UIConfiguration::instance().color_mod ("meter bar", "marker bar"));
902         meter_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
903
904         tempo_bar->set_fill_color (UIConfiguration::instance().color_mod ("tempo bar", "marker bar"));
905         tempo_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
906
907         marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("marker bar", "marker bar"));
908         marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
909
910         cd_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("cd marker bar", "marker bar"));
911         cd_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
912
913         range_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("range marker bar", "marker bar"));
914         range_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
915
916         transport_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("transport marker bar", "marker bar"));
917         transport_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
918
919         cd_marker_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect"));
920         cd_marker_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect"));
921
922         range_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect"));
923         range_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect"));
924
925         transport_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("transport drag rect"));
926         transport_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("transport drag rect"));
927
928         transport_loop_range_rect->set_fill_color (UIConfiguration::instance().color_mod ("transport loop rect", "loop rectangle"));
929         transport_loop_range_rect->set_outline_color (UIConfiguration::instance().color ("transport loop rect"));
930
931         transport_punch_range_rect->set_fill_color (UIConfiguration::instance().color ("transport punch rect"));
932         transport_punch_range_rect->set_outline_color (UIConfiguration::instance().color ("transport punch rect"));
933
934         transport_punchin_line->set_outline_color (UIConfiguration::instance().color ("punch line"));
935         transport_punchout_line->set_outline_color (UIConfiguration::instance().color ("punch line"));
936
937         rubberband_rect->set_outline_color (UIConfiguration::instance().color ("rubber band rect"));
938         rubberband_rect->set_fill_color (UIConfiguration::instance().color_mod ("rubber band rect", "selection rect"));
939
940         location_marker_color = UIConfiguration::instance().color ("location marker");
941         location_range_color = UIConfiguration::instance().color ("location range");
942         location_cd_marker_color = UIConfiguration::instance().color ("location cd marker");
943         location_loop_color = UIConfiguration::instance().color ("location loop");
944         location_punch_color = UIConfiguration::instance().color ("location punch");
945
946         refresh_location_display ();
947
948         /* redraw the whole thing */
949         _track_canvas->set_background_color (UIConfiguration::instance().color ("arrange base"));
950         _track_canvas->queue_draw ();
951
952 /*
953         redisplay_tempo (true);
954
955         if (_session)
956               _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
957 */
958 }
959
960 double
961 Editor::horizontal_position () const
962 {
963         return sample_to_pixel (leftmost_frame);
964 }
965
966 bool
967 Editor::track_canvas_key_press (GdkEventKey*)
968 {
969         return false;
970 }
971
972 bool
973 Editor::track_canvas_key_release (GdkEventKey*)
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 Gdk::Cursor*
1004 Editor::get_canvas_cursor () const
1005 {
1006         /* The top of the cursor stack is always the currently visible cursor. */
1007         return _cursor_stack.back();
1008 }
1009
1010 void
1011 Editor::set_canvas_cursor (Gdk::Cursor* cursor)
1012 {
1013         Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
1014
1015         if (win && !_cursors->is_invalid (cursor)) {
1016                 /* glibmm 2.4 doesn't allow null cursor pointer because it uses
1017                    a Gdk::Cursor& as the argument to Gdk::Window::set_cursor().
1018                    But a null pointer just means "use parent window cursor",
1019                    and so should be allowed. Gtkmm 3.x has fixed this API.
1020
1021                    For now, drop down and use C API
1022                 */
1023                 gdk_window_set_cursor (win->gobj(), cursor ? cursor->gobj() : 0);
1024         }
1025 }
1026
1027 size_t
1028 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
1029 {
1030         if (!_cursors->is_invalid (cursor)) {
1031                 _cursor_stack.push_back (cursor);
1032                 set_canvas_cursor (cursor);
1033         }
1034         return _cursor_stack.size() - 1;
1035 }
1036
1037 void
1038 Editor::pop_canvas_cursor ()
1039 {
1040         while (true) {
1041                 if (_cursor_stack.size() <= 1) {
1042                         PBD::error << "attempt to pop default cursor" << endmsg;
1043                         return;
1044                 }
1045
1046                 _cursor_stack.pop_back();
1047                 if (_cursor_stack.back()) {
1048                         /* Popped to an existing cursor, we're done.  Otherwise, the
1049                            context that created this cursor has been destroyed, so we need
1050                            to skip to the next down the stack. */
1051                         set_canvas_cursor (_cursor_stack.back());
1052                         return;
1053                 }
1054         }
1055 }
1056
1057 Gdk::Cursor*
1058 Editor::which_grabber_cursor () const
1059 {
1060         Gdk::Cursor* c = _cursors->grabber;
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         return c;
1075 }
1076
1077 Gdk::Cursor*
1078 Editor::which_trim_cursor (bool left) const
1079 {
1080         if (!entered_regionview) {
1081                 return 0;
1082         }
1083
1084         Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
1085                 
1086         if (left) {
1087                 
1088                 if (ct & Trimmable::FrontTrimEarlier) {
1089                         return _cursors->left_side_trim;
1090                 } else {
1091                         return _cursors->left_side_trim_right_only;
1092                 }
1093         } else {
1094                 if (ct & Trimmable::EndTrimLater) {
1095                         return _cursors->right_side_trim;
1096                 } else {
1097                         return _cursors->right_side_trim_left_only;
1098                 }
1099         }
1100 }
1101
1102 Gdk::Cursor*
1103 Editor::which_mode_cursor () const
1104 {
1105         Gdk::Cursor* mode_cursor = MouseCursors::invalid_cursor ();
1106
1107         switch (mouse_mode) {
1108         case MouseRange:
1109                 mode_cursor = _cursors->selector;
1110                 break;
1111
1112         case MouseCut:
1113                 mode_cursor = _cursors->scissors;
1114                 break;
1115                         
1116         case MouseObject:
1117         case MouseContent:
1118                 /* don't use mode cursor, pick a grabber cursor based on the item */
1119                 break;
1120
1121         case MouseDraw:
1122                 mode_cursor = _cursors->midi_pencil;
1123                 break;
1124
1125         case MouseTimeFX:
1126                 mode_cursor = _cursors->time_fx; // just use playhead
1127                 break;
1128
1129         case MouseAudition:
1130                 mode_cursor = _cursors->speaker;
1131                 break;
1132         }
1133
1134         /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
1135         if (get_smart_mode()) {
1136
1137                 double x, y;
1138                 get_pointer_position (x, y);
1139
1140                 if (x >= 0 && y >= 0) {
1141                         
1142                         vector<ArdourCanvas::Item const *> items;
1143
1144                         /* Note how we choose a specific scroll group to get
1145                          * items from. This could be problematic.
1146                          */
1147                         
1148                         hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
1149                         
1150                         // first item will be the upper most
1151                         
1152                         if (!items.empty()) {
1153                                 const ArdourCanvas::Item* i = items.front();
1154                                 
1155                                 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
1156                                         pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
1157                                         if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
1158                                                 mode_cursor = _cursors->up_down;
1159                                         }
1160                                 }
1161                         }
1162                 }
1163         }
1164
1165         return mode_cursor;
1166 }
1167
1168 Gdk::Cursor*
1169 Editor::which_track_cursor () const
1170 {
1171         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
1172
1173         switch (_join_object_range_state) {
1174         case JOIN_OBJECT_RANGE_NONE:
1175         case JOIN_OBJECT_RANGE_OBJECT:
1176                 cursor = which_grabber_cursor ();
1177                 break;
1178         case JOIN_OBJECT_RANGE_RANGE:
1179                 cursor = _cursors->selector;
1180                 break;
1181         }
1182
1183         return cursor;
1184 }
1185
1186 Gdk::Cursor*
1187 Editor::which_canvas_cursor(ItemType type) const
1188 {
1189         Gdk::Cursor* cursor = which_mode_cursor ();
1190
1191         if (mouse_mode == MouseRange) {
1192                 switch (type) {
1193                 case StartSelectionTrimItem:
1194                         cursor = _cursors->left_side_trim;
1195                         break;
1196                 case EndSelectionTrimItem:
1197                         cursor = _cursors->right_side_trim;
1198                         break;
1199                 default:
1200                         break;
1201                 }
1202         }
1203         
1204         if ((mouse_mode == MouseObject || get_smart_mode ()) ||
1205             mouse_mode == MouseContent) {
1206
1207                 /* find correct cursor to use in object/smart mode */
1208
1209                 switch (type) {
1210                 case RegionItem:
1211                 /* We don't choose a cursor for these items on top of a region view,
1212                    because this would push a new context on the enter stack which
1213                    means switching the region context for things like smart mode
1214                    won't actualy change the cursor. */
1215                 // case RegionViewNameHighlight:
1216                 // case RegionViewName:
1217                 // case WaveItem:
1218                 case StreamItem:
1219                 case AutomationTrackItem:
1220                         cursor = which_track_cursor ();
1221                         break;
1222                 case PlayheadCursorItem:
1223                         switch (_edit_point) {
1224                         case EditAtMouse:
1225                                 cursor = _cursors->grabber_edit_point;
1226                                 break;
1227                         default:
1228                                 cursor = _cursors->grabber;
1229                                 break;
1230                         }
1231                         break;
1232                 case SelectionItem:
1233                         cursor = _cursors->selector;
1234                         break;
1235                 case ControlPointItem:
1236                         cursor = _cursors->fader;
1237                         break;
1238                 case GainLineItem:
1239                         cursor = _cursors->cross_hair;
1240                         break;
1241                 case AutomationLineItem:
1242                         cursor = _cursors->cross_hair;
1243                         break;
1244                 case StartSelectionTrimItem:
1245                         cursor = _cursors->left_side_trim;
1246                         break;
1247                 case EndSelectionTrimItem:
1248                         cursor = _cursors->right_side_trim;
1249                         break;
1250                 case FadeInItem:
1251                         cursor = _cursors->fade_in;
1252                         break;
1253                 case FadeInHandleItem:
1254                         cursor = _cursors->fade_in;
1255                         break;
1256                 case FadeInTrimHandleItem:
1257                         cursor = _cursors->fade_in;
1258                         break;
1259                 case FadeOutItem:
1260                         cursor = _cursors->fade_out;
1261                         break;
1262                 case FadeOutHandleItem:
1263                         cursor = _cursors->fade_out;
1264                         break;
1265                 case FadeOutTrimHandleItem:
1266                         cursor = _cursors->fade_out;
1267                         break;
1268                 case FeatureLineItem:
1269                         cursor = _cursors->cross_hair;
1270                         break;
1271                 case LeftFrameHandle:
1272                         if ( effective_mouse_mode() == MouseObject )  // (smart mode): if the user is in the btm half, show the trim cursor
1273                                 cursor = which_trim_cursor (true);
1274                         else
1275                                 cursor = _cursors->selector;  // (smart mode): in the top half, just show the selection (range) cursor
1276                         break;
1277                 case RightFrameHandle:
1278                         if ( effective_mouse_mode() == MouseObject )  //see above
1279                                 cursor = which_trim_cursor (false);
1280                         else
1281                                 cursor = _cursors->selector;
1282                         break;
1283                 case StartCrossFadeItem:
1284                         cursor = _cursors->fade_in;
1285                         break;
1286                 case EndCrossFadeItem:
1287                         cursor = _cursors->fade_out;
1288                         break;
1289                 case CrossfadeViewItem:
1290                         cursor = _cursors->cross_hair;
1291                         break;
1292                 case NoteItem:
1293                         cursor = _cursors->grabber_note;
1294                 default:
1295                         break;
1296                 }
1297
1298         } else if (mouse_mode == MouseDraw) {
1299                 
1300                 /* ControlPointItem is not really specific to region gain mode
1301                    but it is the same cursor so don't worry about this for now.
1302                    The result is that we'll see the fader cursor if we enter
1303                    non-region-gain-line control points while in MouseDraw
1304                    mode, even though we can't edit them in this mode.
1305                 */
1306
1307                 switch (type) {
1308                 case GainLineItem:
1309                 case ControlPointItem:
1310                         cursor = _cursors->fader;
1311                         break;
1312                 case NoteItem:
1313                         cursor = _cursors->grabber_note;
1314                 default:
1315                         break;
1316                 }
1317         }
1318
1319         switch (type) {
1320                 /* These items use the timebar cursor at all times */
1321         case TimecodeRulerItem:
1322         case MinsecRulerItem:
1323         case BBTRulerItem:
1324         case SamplesRulerItem:
1325                 cursor = _cursors->timebar;
1326                 break;
1327
1328                 /* These items use the grabber cursor at all times */
1329         case MeterMarkerItem:
1330         case TempoMarkerItem:
1331         case MeterBarItem:
1332         case TempoBarItem:
1333         case MarkerItem:
1334         case MarkerBarItem:
1335         case RangeMarkerBarItem:
1336         case CdMarkerBarItem:
1337         case VideoBarItem:
1338         case TransportMarkerBarItem:
1339         case DropZoneItem:
1340                 cursor = which_grabber_cursor();
1341                 break;
1342
1343         default:
1344                 break;
1345         }
1346
1347         return cursor;
1348 }
1349
1350 void
1351 Editor::choose_canvas_cursor_on_entry (ItemType type)
1352 {
1353         if (_drags->active()) {
1354                 return;
1355         }
1356
1357         Gdk::Cursor* cursor = which_canvas_cursor(type);
1358
1359         if (!_cursors->is_invalid (cursor)) {
1360                 // Push a new enter context
1361                 const EnterContext ctx = { type, CursorContext::create(*this, cursor) };
1362                 _enter_stack.push_back(ctx);
1363         }
1364 }
1365
1366 void
1367 Editor::update_all_enter_cursors ()
1368 {
1369         for (std::vector<EnterContext>::iterator i = _enter_stack.begin(); i != _enter_stack.end(); ++i) {
1370                 i->cursor_ctx->change(which_canvas_cursor(i->item_type));
1371         }
1372 }
1373
1374 double
1375 Editor::trackviews_height() const
1376 {
1377         if (!_trackview_group) {
1378                 return 0;
1379         }
1380
1381         return _visible_canvas_height - _trackview_group->canvas_origin().y;
1382 }