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