fix erroneous start of autoscroll when mouse pointer is left or above the left/upper...
[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 "editor.h"
39 #include "global_signals.h"
40 #include "editing.h"
41 #include "rgb_macros.h"
42 #include "utils.h"
43 #include "audio_time_axis.h"
44 #include "editor_drag.h"
45 #include "region_view.h"
46 #include "editor_group_tabs.h"
47 #include "editor_summary.h"
48 #include "video_timeline.h"
49 #include "keyboard.h"
50 #include "editor_cursors.h"
51 #include "mouse_cursors.h"
52 #include "verbose_cursor.h"
53
54 #include "i18n.h"
55
56 using namespace std;
57 using namespace ARDOUR;
58 using namespace PBD;
59 using namespace Gtk;
60 using namespace Glib;
61 using namespace Gtkmm2ext;
62 using namespace Editing;
63
64 void
65 Editor::initialize_canvas ()
66 {
67         _track_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, vertical_adjustment);
68         _track_canvas = _track_canvas_viewport->canvas ();
69
70         ArdourCanvas::ScrollGroup* hsg; 
71         ArdourCanvas::ScrollGroup* hg;
72         ArdourCanvas::ScrollGroup* vg;
73
74         hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), 
75                                                                ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically|
76                                                                                                              ArdourCanvas::ScrollGroup::ScrollsHorizontally));
77         CANVAS_DEBUG_NAME (hv_scroll_group, "canvas hv scroll");
78         _track_canvas->add_scroller (*hsg);
79
80         v_scroll_group = vg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsVertically);
81         CANVAS_DEBUG_NAME (v_scroll_group, "canvas v scroll");
82         _track_canvas->add_scroller (*vg);
83
84         h_scroll_group = hg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
85         CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll");
86         _track_canvas->add_scroller (*hg);
87
88         _verbose_cursor = new VerboseCursor (this);
89
90         /* on the bottom, an image */
91
92         if (Profile->get_sae()) {
93                 Image img (::get_icon (X_("saelogo")));
94                 // logo_item = new ArdourCanvas::Pixbuf (_track_canvas->root(), 0.0, 0.0, img.get_pixbuf());
95                 // logo_item->property_height_in_pixels() = true;
96                 // logo_item->property_width_in_pixels() = true;
97                 // logo_item->property_height_set() = true;
98                 // logo_item->property_width_set() = true;
99                 // logo_item->show ();
100         }
101
102         /*a group to hold global rects like punch/loop indicators */
103         global_rect_group = new ArdourCanvas::Group (hv_scroll_group);
104         CANVAS_DEBUG_NAME (global_rect_group, "global rect group");
105
106         transport_loop_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
107         CANVAS_DEBUG_NAME (transport_loop_range_rect, "loop rect");
108         transport_loop_range_rect->hide();
109
110         transport_punch_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
111         CANVAS_DEBUG_NAME (transport_punch_range_rect, "punch rect");
112         transport_punch_range_rect->hide();
113
114         /*a group to hold time (measure) lines */
115         time_line_group = new ArdourCanvas::Group (hv_scroll_group);
116         CANVAS_DEBUG_NAME (time_line_group, "time line group");
117
118         _trackview_group = new ArdourCanvas::Group (hv_scroll_group);
119         CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
120         
121         // used to show zoom mode active zooming
122         zoom_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
123         zoom_rect->hide();
124         zoom_rect->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_zoom_rect_event), (ArdourCanvas::Item*) 0));
125
126         // used as rubberband rect
127         rubberband_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
128         rubberband_rect->hide();
129
130         /* a group to hold stuff while it gets dragged around. Must be the
131          * uppermost (last) group with hv_scroll_group as a parent
132          */
133         _drag_motion_group = new ArdourCanvas::Group (hv_scroll_group);                                                                                                                                     
134         CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion");
135
136         /* TIME BAR CANVAS */
137         
138         _time_markers_group = new ArdourCanvas::Group (h_scroll_group);
139         CANVAS_DEBUG_NAME (_time_markers_group, "time bars");
140
141         cd_marker_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, 0.0));
142         CANVAS_DEBUG_NAME (cd_marker_group, "cd marker group");
143         /* the vide is temporarily placed a the same location as the
144            cd_marker_group, but is moved later.
145         */
146         videotl_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0));
147         CANVAS_DEBUG_NAME (videotl_group, "videotl group");
148         marker_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, timebar_height + 1.0));
149         CANVAS_DEBUG_NAME (marker_group, "marker group");
150         transport_marker_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 2.0) + 1.0));
151         CANVAS_DEBUG_NAME (transport_marker_group, "transport marker group");
152         range_marker_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 3.0) + 1.0));
153         CANVAS_DEBUG_NAME (range_marker_group, "range marker group");
154         tempo_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 4.0) + 1.0));
155         CANVAS_DEBUG_NAME (tempo_group, "tempo group");
156         meter_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0));
157         CANVAS_DEBUG_NAME (meter_group, "meter group");
158
159         meter_bar = new ArdourCanvas::Rectangle (meter_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
160         CANVAS_DEBUG_NAME (meter_bar, "meter Bar");
161         meter_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
162
163         tempo_bar = new ArdourCanvas::Rectangle (tempo_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
164         CANVAS_DEBUG_NAME (tempo_bar, "Tempo  Bar");
165         tempo_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
166
167         range_marker_bar = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
168         CANVAS_DEBUG_NAME (range_marker_bar, "Range Marker Bar");
169         range_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
170
171         transport_marker_bar = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
172         CANVAS_DEBUG_NAME (transport_marker_bar, "transport Marker Bar");
173         transport_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
174
175         marker_bar = new ArdourCanvas::Rectangle (marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
176         CANVAS_DEBUG_NAME (marker_bar, "Marker Bar");
177         marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
178
179         cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
180         CANVAS_DEBUG_NAME (cd_marker_bar, "CD Marker Bar");
181         cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
182
183         ARDOUR_UI::instance()->video_timeline = new VideoTimeLine(this, videotl_group, (timebar_height * videotl_bar_height));
184         
185         cd_marker_bar_drag_rect = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
186         CANVAS_DEBUG_NAME (cd_marker_bar_drag_rect, "cd marker drag");
187         cd_marker_bar_drag_rect->set_outline (false);
188         cd_marker_bar_drag_rect->hide ();
189
190         range_bar_drag_rect = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
191         CANVAS_DEBUG_NAME (range_bar_drag_rect, "range drag");
192         range_bar_drag_rect->set_outline (false);
193         range_bar_drag_rect->hide ();
194
195         transport_bar_drag_rect = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
196         CANVAS_DEBUG_NAME (transport_bar_drag_rect, "transport drag");
197         transport_bar_drag_rect->set_outline (false);
198         transport_bar_drag_rect->hide ();
199
200         transport_punchin_line = new ArdourCanvas::Line (hv_scroll_group);
201         transport_punchin_line->set_x0 (0);
202         transport_punchin_line->set_y0 (0);
203         transport_punchin_line->set_x1 (0);
204         transport_punchin_line->set_y1 (ArdourCanvas::COORD_MAX);
205         transport_punchin_line->hide ();
206
207         transport_punchout_line  = new ArdourCanvas::Line (hv_scroll_group);
208         transport_punchout_line->set_x0 (0);
209         transport_punchout_line->set_y0 (0);
210         transport_punchout_line->set_x1 (0);
211         transport_punchout_line->set_y1 (ArdourCanvas::COORD_MAX);
212         transport_punchout_line->hide();
213
214         tempo_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_tempo_bar_event), tempo_bar));
215         meter_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_meter_bar_event), meter_bar));
216         marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_marker_bar_event), marker_bar));
217         cd_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_cd_marker_bar_event), cd_marker_bar));
218         videotl_group->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_videotl_bar_event), videotl_group));
219         range_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_range_marker_bar_event), range_marker_bar));
220         transport_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_transport_marker_bar_event), transport_marker_bar));
221
222         playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event);
223
224         if (logo_item) {
225                 logo_item->lower_to_bottom ();
226         }
227
228
229         _canvas_drop_zone = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, 0.0));
230         /* this thing is transparent */
231         _canvas_drop_zone->set_fill (false);
232         _canvas_drop_zone->set_outline (false);
233         _canvas_drop_zone->Event.connect (sigc::mem_fun (*this, &Editor::canvas_drop_zone_event));
234
235         /* these signals will initially be delivered to the canvas itself, but if they end up remaining unhandled, they are passed to Editor-level
236            handlers.
237         */
238
239         _track_canvas->signal_scroll_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_scroll_event), true));
240         _track_canvas->signal_motion_notify_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_motion_notify_event));
241         _track_canvas->signal_button_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_press_event));
242         _track_canvas->signal_button_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_release_event));
243         _track_canvas->signal_drag_motion().connect (sigc::mem_fun (*this, &Editor::track_canvas_drag_motion));
244         _track_canvas->signal_key_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_press));
245         _track_canvas->signal_key_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_release));
246
247         _track_canvas->set_name ("EditorMainCanvas");
248         _track_canvas->add_events (Gdk::POINTER_MOTION_HINT_MASK | Gdk::SCROLL_MASK | Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
249         _track_canvas->signal_leave_notify_event().connect (sigc::mem_fun(*this, &Editor::left_track_canvas), false);
250         _track_canvas->signal_enter_notify_event().connect (sigc::mem_fun(*this, &Editor::entered_track_canvas), false);
251         _track_canvas->set_flags (CAN_FOCUS);
252
253         /* set up drag-n-drop */
254
255         vector<TargetEntry> target_table;
256
257         // Drag-N-Drop from the region list can generate this target
258         target_table.push_back (TargetEntry ("regions"));
259
260         target_table.push_back (TargetEntry ("text/plain"));
261         target_table.push_back (TargetEntry ("text/uri-list"));
262         target_table.push_back (TargetEntry ("application/x-rootwin-drop"));
263
264         _track_canvas->drag_dest_set (target_table);
265         _track_canvas->signal_drag_data_received().connect (sigc::mem_fun(*this, &Editor::track_canvas_drag_data_received));
266
267         _track_canvas_viewport->signal_size_allocate().connect (sigc::mem_fun(*this, &Editor::track_canvas_viewport_allocate));
268
269         initialize_rulers ();
270
271         ColorsChanged.connect (sigc::mem_fun (*this, &Editor::color_handler));
272         color_handler();
273
274 }
275
276 void
277 Editor::track_canvas_viewport_allocate (Gtk::Allocation alloc)
278 {
279         _canvas_viewport_allocation = alloc;
280         track_canvas_viewport_size_allocated ();
281 }
282
283 void
284 Editor::track_canvas_viewport_size_allocated ()
285 {
286         bool height_changed = _visible_canvas_height != _canvas_viewport_allocation.get_height();
287
288         _visible_canvas_width  = _canvas_viewport_allocation.get_width ();
289         _visible_canvas_height = _canvas_viewport_allocation.get_height ();
290
291         _canvas_drop_zone->set_y1 (_canvas_drop_zone->y0() + (_visible_canvas_height - 20.0));
292
293         // SHOWTRACKS
294
295         if (height_changed) {
296
297                 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
298                         i->second->canvas_height_set (_visible_canvas_height);
299                 }
300
301                 vertical_adjustment.set_page_size (_visible_canvas_height);
302                 if ((vertical_adjustment.get_value() + _visible_canvas_height) >= vertical_adjustment.get_upper()) {
303                         /*
304                            We're increasing the size of the canvas while the bottom is visible.
305                            We scroll down to keep in step with the controls layout.
306                         */
307                         vertical_adjustment.set_value (_full_canvas_height - _visible_canvas_height);
308                 }
309
310                 set_visible_track_count (_visible_track_count);
311         }
312
313         update_fixed_rulers();
314         redisplay_tempo (false);
315         _summary->set_overlays_dirty ();
316 }
317
318 void
319 Editor::reset_controls_layout_width ()
320 {
321         GtkRequisition req;
322         gint w;
323
324         edit_controls_vbox.size_request (req);
325         w = req.width;
326
327         if (_group_tabs->is_mapped()) {
328                 _group_tabs->size_request (req);
329                 w += req.width;
330         }
331
332         /* the controls layout has no horizontal scrolling, its visible
333            width is always equal to the total width of its contents.
334         */
335
336         controls_layout.property_width() = w;
337         controls_layout.property_width_request() = w;
338 }
339
340 void
341 Editor::reset_controls_layout_height (int32_t h)
342 {
343         /* ensure that the rect that represents the "bottom" of the canvas
344          * (the drag-n-drop zone) is, in fact, at the bottom.
345          */
346
347         _canvas_drop_zone->set_position (ArdourCanvas::Duple (0, h));
348
349         /* track controls layout must span the full height of "h" (all tracks)
350          * plus the bottom rect.
351          */
352
353         h += _canvas_drop_zone->height ();
354
355         /* set the height of the scrollable area (i.e. the sum of all contained widgets)
356          * for the controls layout. The size request is set elsewhere.
357          */
358
359         controls_layout.property_height() = h;
360
361 }
362
363 bool
364 Editor::track_canvas_map_handler (GdkEventAny* /*ev*/)
365 {
366         if (current_canvas_cursor) {
367                 set_canvas_cursor (current_canvas_cursor);
368         }
369         return false;
370 }
371
372 /** This is called when something is dropped onto the track canvas */
373 void
374 Editor::track_canvas_drag_data_received (const RefPtr<Gdk::DragContext>& context,
375                                          int x, int y,
376                                          const SelectionData& data,
377                                          guint info, guint time)
378 {
379         if (data.get_target() == "regions") {
380                 drop_regions (context, x, y, data, info, time);
381         } else {
382                 drop_paths (context, x, y, data, info, time);
383         }
384 }
385
386 bool
387 Editor::idle_drop_paths (vector<string> paths, framepos_t frame, double ypos, bool copy)
388 {
389         drop_paths_part_two (paths, frame, ypos, copy);
390         return false;
391 }
392
393 void
394 Editor::drop_paths_part_two (const vector<string>& paths, framepos_t frame, double ypos, bool copy)
395 {
396         RouteTimeAxisView* tv;
397         
398         /* MIDI files must always be imported, because we consider them
399          * writable. So split paths into two vectors, and follow the import
400          * path on the MIDI part.
401          */
402
403         vector<string> midi_paths;
404         vector<string> audio_paths;
405
406         for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
407                 if (SMFSource::safe_midi_file_extension (*i)) {
408                         midi_paths.push_back (*i);
409                 } else {
410                         audio_paths.push_back (*i);
411                 }
412         }
413
414
415         std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (ypos);
416         if (tvp.first == 0) {
417
418                 /* drop onto canvas background: create new tracks */
419
420                 frame = 0;
421
422                 do_import (midi_paths, Editing::ImportDistinctFiles, ImportAsTrack, SrcBest, frame);
423                 
424                 if (Profile->get_sae() || Config->get_only_copy_imported_files() || copy) {
425                         do_import (audio_paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack, SrcBest, frame);
426                 } else {
427                         do_embed (audio_paths, Editing::ImportDistinctFiles, ImportAsTrack, frame);
428                 }
429
430         } else if ((tv = dynamic_cast<RouteTimeAxisView*> (tvp.first)) != 0) {
431
432                 /* check that its a track, not a bus */
433
434                 if (tv->track()) {
435                         /* select the track, then embed/import */
436                         selection->set (tv);
437
438                         do_import (midi_paths, Editing::ImportSerializeFiles, ImportToTrack, SrcBest, frame);
439
440                         if (Profile->get_sae() || Config->get_only_copy_imported_files() || copy) {
441                                 do_import (audio_paths, Editing::ImportSerializeFiles, Editing::ImportToTrack, SrcBest, frame);
442                         } else {
443                                 do_embed (audio_paths, Editing::ImportSerializeFiles, ImportToTrack, frame);
444                         }
445                 }
446         }
447 }
448
449 void
450 Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
451                     int x, int y,
452                     const SelectionData& data,
453                     guint info, guint time)
454 {
455         vector<string> paths;
456         GdkEvent ev;
457         framepos_t frame;
458         double cy;
459
460         if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
461
462                 /* D-n-D coordinates are window-relative, so convert to canvas coordinates
463                  */
464
465                 ev.type = GDK_BUTTON_RELEASE;
466                 ev.button.x = x;
467                 ev.button.y = y;
468
469                 frame = window_event_sample (&ev, 0, &cy);
470
471                 snap_to (frame);
472
473                 bool copy = ((context->get_actions() & (Gdk::ACTION_COPY | Gdk::ACTION_LINK | Gdk::ACTION_MOVE)) == Gdk::ACTION_COPY);
474 #ifdef GTKOSX
475                 /* We are not allowed to call recursive main event loops from within
476                    the main event loop with GTK/Quartz. Since import/embed wants
477                    to push up a progress dialog, defer all this till we go idle.
478                 */
479                 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun (*this, &Editor::idle_drop_paths), paths, frame, cy, copy));
480 #else
481                 drop_paths_part_two (paths, frame, cy, copy);
482 #endif
483         }
484
485         context->drag_finish (true, false, time);
486 }
487
488 /** @param allow_horiz true to allow horizontal autoscroll, otherwise false.
489  *
490  *  @param allow_vert true to allow vertical autoscroll, otherwise false.
491  *
492  */
493 void
494 Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
495 {
496         if (!Config->get_autoscroll_editor ()) {
497                 return;
498         }
499
500         ArdourCanvas::Rect scrolling_boundary;
501         Gtk::Allocation alloc;
502         
503         if (from_headers) {
504                 alloc = controls_layout.get_allocation ();
505         } else {
506                 alloc = _track_canvas_viewport->get_allocation ();
507                 
508                 /* the effective width of the autoscroll boundary so
509                    that we start scrolling before we hit the edge.
510                    
511                    this helps when the window is slammed up against the
512                    right edge of the screen, making it hard to scroll
513                    effectively.
514                 */
515                 
516                 if (alloc.get_width() > 20) { 
517                         alloc.set_width (alloc.get_width() - 20);
518                         alloc.set_x (alloc.get_x() + 10);
519                 } 
520         }
521         
522         scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), 
523                                                  alloc.get_x() + alloc.get_width(), 
524                                                  alloc.get_y() + alloc.get_height());
525         
526         int x, y;
527         Gdk::ModifierType mask;
528
529         get_window()->get_pointer (x, y, mask);
530
531         if ((allow_horiz && ((x < scrolling_boundary.x0 && leftmost_frame > 0) || x >= scrolling_boundary.x1)) ||
532             (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) {
533                 start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary);
534         }
535 }
536
537 bool
538 Editor::autoscroll_active () const
539 {
540         return autoscroll_connection.connected ();
541 }
542
543 bool
544 Editor::autoscroll_canvas ()
545 {
546         int x, y;
547         Gdk::ModifierType mask;
548         frameoffset_t dx = 0;
549         bool no_stop = false;
550         bool y_motion = false;
551
552         get_window()->get_pointer (x, y, mask);
553
554         VisualChange vc;
555
556         if (autoscroll_horizontal_allowed) {
557
558                 framepos_t new_frame = leftmost_frame;
559
560                 /* horizontal */
561
562                 if (x > autoscroll_boundary.x1) {
563
564                         /* bring it back into view */
565                         dx = x - autoscroll_boundary.x1;
566                         dx += 10 + (2 * (autoscroll_cnt/2));
567
568                         dx = pixel_to_sample (dx);
569
570                         if (leftmost_frame < max_framepos - dx) {
571                                 new_frame = leftmost_frame + dx;
572                         } else {
573                                 new_frame = max_framepos;
574                         }
575
576                         no_stop = true;
577
578                 } else if (x < autoscroll_boundary.x0) {
579                         
580                         dx = autoscroll_boundary.x0 - x;
581                         dx += 10 + (2 * (autoscroll_cnt/2));
582
583                         dx = pixel_to_sample (dx);
584
585                         if (leftmost_frame >= dx) {
586                                 new_frame = leftmost_frame - dx;
587                         } else {
588                                 new_frame = 0;
589                         }
590
591                         no_stop = true;
592                 }
593                 
594                 if (new_frame != leftmost_frame) {
595                         vc.time_origin = new_frame;
596                         vc.add (VisualChange::TimeOrigin);
597                 }
598         }
599
600         if (autoscroll_vertical_allowed) {
601                 
602                 // const double vertical_pos = vertical_adjustment.get_value();
603                 const int speed_factor = 20;
604
605                 /* vertical */ 
606                 
607                 if (y < autoscroll_boundary.y0) {
608
609                         /* scroll to make higher tracks visible */
610
611                         if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
612                                 y_motion = scroll_up_one_track ();
613                         }
614
615                 } else if (y > autoscroll_boundary.y1) {
616
617                         if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
618                                 y_motion = scroll_down_one_track ();
619                                 
620                         }
621                 }
622
623                 no_stop = true;
624         }
625
626         if (vc.pending) {
627
628                 /* change horizontal first */
629
630                 if (vc.pending) {
631                         visual_changer (vc);
632                 }
633
634                 /* now send a motion event to notify anyone who cares
635                    that we have moved to a new location (because we scrolled)
636                 */
637
638                 GdkEventMotion ev;
639
640                 ev.type = GDK_MOTION_NOTIFY;
641                 ev.state = Gdk::BUTTON1_MASK;
642                 
643                 /* the motion handler expects events in canvas coordinate space */
644
645                 /* first convert from Editor window coordinates to canvas
646                  * window coordinates
647                  */
648
649                 int cx;
650                 int cy;
651
652                 /* clamp x and y to remain within the visible area */
653
654                 x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
655                 y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
656
657                 translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
658
659                 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
660                 ev.x = d.x;
661                 ev.y = d.y;
662
663                 motion_handler (0, (GdkEvent*) &ev, true);
664
665         } else if (no_stop) {
666
667                 /* not changing visual state but pointer is outside the scrolling boundary
668                  * so we still need to deliver a fake motion event 
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                 /* first convert from Editor window coordinates to canvas
679                  * window coordinates
680                  */
681
682                 int cx;
683                 int cy;
684
685                 /* clamp x and y to remain within the visible area. except
686                  * .. if horizontal scrolling is allowed, always allow us to
687                  * move back to zero
688                  */
689
690                 if (autoscroll_horizontal_allowed) {
691                         x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1);
692                 } else {
693                         x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
694                 }
695                 y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
696
697                 translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
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 {
706                 stop_canvas_autoscroll ();
707                 return false;
708         }
709
710         autoscroll_cnt++;
711
712         return true; /* call me again */
713 }       
714
715 void
716 Editor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary)
717 {
718         if (!_session) {
719                 return;
720         }
721
722         stop_canvas_autoscroll ();
723
724         autoscroll_cnt = 0;
725         autoscroll_horizontal_allowed = allow_horiz;
726         autoscroll_vertical_allowed = allow_vert;
727         autoscroll_boundary = boundary;
728
729         /* do the first scroll right now
730         */
731
732         autoscroll_canvas ();
733
734         /* scroll again at very very roughly 30FPS */
735
736         autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::autoscroll_canvas), 30);
737 }
738
739 void
740 Editor::stop_canvas_autoscroll ()
741 {
742         autoscroll_connection.disconnect ();
743 }
744
745 bool
746 Editor::left_track_canvas (GdkEventCrossing */*ev*/)
747 {
748         DropDownKeys ();
749         within_track_canvas = false;
750         set_entered_track (0);
751         set_entered_regionview (0);
752         reset_canvas_action_sensitivity (false);
753         return false;
754 }
755
756 bool
757 Editor::entered_track_canvas (GdkEventCrossing */*ev*/)
758 {
759         within_track_canvas = true;
760         reset_canvas_action_sensitivity (true);
761         return FALSE;
762 }
763
764 void
765 Editor::_ensure_time_axis_view_is_visible (const TimeAxisView& tav, bool at_top)
766 {
767         double begin = tav.y_position();
768         double v = vertical_adjustment.get_value ();
769
770         if (!at_top && (begin < v || begin + tav.current_height() > v + _visible_canvas_height)) {
771                 /* try to put the TimeAxisView roughly central */
772                 if (begin >= _visible_canvas_height/2.0) {
773                         begin -= _visible_canvas_height/2.0;
774                 }
775         }
776
777         /* Clamp the y pos so that we do not extend beyond the canvas full
778          * height. 
779          */
780         if (_full_canvas_height - begin < _visible_canvas_height){
781                 begin = _full_canvas_height - _visible_canvas_height;
782         }
783
784         vertical_adjustment.set_value (begin);
785 }
786
787 /** Called when the main vertical_adjustment has changed */
788 void
789 Editor::tie_vertical_scrolling ()
790 {
791         if (pending_visual_change.idle_handler_id < 0) {
792                 _summary->set_overlays_dirty ();
793         }
794 }
795
796 void
797 Editor::set_horizontal_position (double p)
798 {
799         horizontal_adjustment.set_value (p);
800
801         leftmost_frame = (framepos_t) floor (p * samples_per_pixel);
802
803         update_fixed_rulers ();
804         redisplay_tempo (true);
805
806         if (pending_visual_change.idle_handler_id < 0) {
807                 _summary->set_overlays_dirty ();
808         }
809
810         update_video_timeline();
811 }
812
813 void
814 Editor::color_handler()
815 {
816         ArdourCanvas::Color base = ARDOUR_UI::config()->get_canvasvar_RulerBase();
817         ArdourCanvas::Color text = ARDOUR_UI::config()->get_canvasvar_RulerText();
818         timecode_ruler->set_fill_color (base);
819         timecode_ruler->set_outline_color (text);
820         minsec_ruler->set_fill_color (base);
821         minsec_ruler->set_outline_color (text);
822         samples_ruler->set_fill_color (base);
823         samples_ruler->set_outline_color (text);
824         bbt_ruler->set_fill_color (base);
825         bbt_ruler->set_outline_color (text);
826         
827         playhead_cursor->set_color (ARDOUR_UI::config()->get_canvasvar_PlayHead());
828         _verbose_cursor->set_color (ARDOUR_UI::config()->get_canvasvar_VerboseCanvasCursor());
829
830         meter_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_MeterBar());
831         meter_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
832
833         tempo_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TempoBar());
834         tempo_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
835
836         marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_MarkerBar());
837         marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
838
839         cd_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_CDMarkerBar());
840         cd_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
841
842         range_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeMarkerBar());
843         range_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
844
845         transport_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportMarkerBar());
846         transport_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
847
848         cd_marker_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
849         cd_marker_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
850
851         range_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
852         range_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
853
854         transport_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportDragRect());
855         transport_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportDragRect());
856
857         transport_loop_range_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportLoopRect());
858         transport_loop_range_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportLoopRect());
859
860         transport_punch_range_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportPunchRect());
861         transport_punch_range_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportPunchRect());
862
863         transport_punchin_line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_PunchLine());
864         transport_punchout_line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_PunchLine());
865
866         zoom_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ZoomRect());
867         zoom_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZoomRect());
868
869         rubberband_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RubberBandRect());
870         rubberband_rect->set_fill_color ((guint32) ARDOUR_UI::config()->get_canvasvar_RubberBandRect());
871
872         location_marker_color = ARDOUR_UI::config()->get_canvasvar_LocationMarker();
873         location_range_color = ARDOUR_UI::config()->get_canvasvar_LocationRange();
874         location_cd_marker_color = ARDOUR_UI::config()->get_canvasvar_LocationCDMarker();
875         location_loop_color = ARDOUR_UI::config()->get_canvasvar_LocationLoop();
876         location_punch_color = ARDOUR_UI::config()->get_canvasvar_LocationPunch();
877
878         refresh_location_display ();
879 /*
880         redisplay_tempo (true);
881
882         if (_session)
883               _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
884 */
885 }
886
887 double
888 Editor::horizontal_position () const
889 {
890         return sample_to_pixel (leftmost_frame);
891 }
892
893 void
894 Editor::set_canvas_cursor (Gdk::Cursor* cursor, bool save)
895 {
896         if (save) {
897                 current_canvas_cursor = cursor;
898         }
899
900         Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
901
902         if (win) {
903                 _track_canvas->get_window()->set_cursor (*cursor);
904         }
905 }
906
907 void
908 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
909 {
910         if (cursor) {
911                 _cursor_stack.push (cursor);
912                 set_canvas_cursor (cursor, false);
913         }
914 }
915
916 void
917 Editor::pop_canvas_cursor ()
918 {
919         if (!_cursor_stack.empty()) {
920                 Gdk::Cursor* cursor = _cursor_stack.top ();
921                 _cursor_stack.pop ();
922                 set_canvas_cursor (cursor, false);
923         }
924 }
925
926 bool
927 Editor::track_canvas_key_press (GdkEventKey*)
928 {
929         /* XXX: event does not report the modifier key pressed down, AFAICS, so use the Keyboard object instead */
930         if (mouse_mode == Editing::MouseZoom && Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
931                 set_canvas_cursor (_cursors->zoom_out, true);
932         }
933
934         return false;
935 }
936
937 bool
938 Editor::track_canvas_key_release (GdkEventKey*)
939 {
940         if (mouse_mode == Editing::MouseZoom && !Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
941                 set_canvas_cursor (_cursors->zoom_in, true);
942         }
943
944         return false;
945 }
946
947 double
948 Editor::clamp_verbose_cursor_x (double x)
949 {
950         if (x < 0) {
951                 x = 0;
952         } else {
953                 x = min (_visible_canvas_width - 200.0, x);
954         }
955         return x;
956 }
957
958 double
959 Editor::clamp_verbose_cursor_y (double y)
960 {
961         y = max (0.0, y);
962         y = min (_visible_canvas_height - 50, y);
963         return y;
964 }
965
966 ArdourCanvas::GtkCanvasViewport*
967 Editor::get_track_canvas() const
968 {
969         return _track_canvas_viewport;
970 }