a set of inter-related subtle changes to get vertical autoscrolling to work, or at...
[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 () || autoscroll_active ()) {
497                 return;
498         }
499
500         /* define a rectangular boundary for scrolling. If the mouse moves
501          * outside of this area and/or continue to be outside of this area,
502          * then we will continuously auto-scroll the canvas in the appropriate
503          * direction(s)
504          *
505          * the boundary is defined in coordinates relative to the toplevel
506          * window since that is what we're going to call ::get_pointer() on
507          * during autoscrolling to determine if we're still outside the
508          * boundary or not.
509          */
510
511         ArdourCanvas::Rect scrolling_boundary;
512         Gtk::Allocation alloc;
513         int cx, cy;
514
515         if (from_headers) {
516                 alloc = controls_layout.get_allocation ();
517         } else {        
518                 alloc = _track_canvas_viewport->get_allocation ();
519                 cx = alloc.get_x();
520                 cy = alloc.get_y();
521
522                 /* reduce height by the height of the timebars, which happens
523                    to correspond to the position of the hv_scroll_group.
524                 */
525                 
526                 alloc.set_height (alloc.get_height() - hv_scroll_group->position().y);
527                 alloc.set_y (alloc.get_y() + hv_scroll_group->position().y);
528
529                 /* now reduce it again so that we start autoscrolling before we
530                  * move off the top or bottom of the canvas
531                  */
532
533                 alloc.set_height (alloc.get_height() - 20);
534                 alloc.set_y (alloc.get_y() + 10);
535
536                 /* the effective width of the autoscroll boundary so
537                    that we start scrolling before we hit the edge.
538                    
539                    this helps when the window is slammed up against the
540                    right edge of the screen, making it hard to scroll
541                    effectively.
542                 */
543                 
544                 if (alloc.get_width() > 20) { 
545                         alloc.set_width (alloc.get_width() - 20);
546                         alloc.set_x (alloc.get_x() + 10);
547                 } 
548
549         }
550         
551         scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), alloc.get_x() + alloc.get_width(), alloc.get_y() + alloc.get_height());
552         
553         int x, y;
554         Gdk::ModifierType mask;
555
556         get_window()->get_pointer (x, y, mask);
557
558         if ((allow_horiz && ((x < scrolling_boundary.x0 && leftmost_frame > 0) || x >= scrolling_boundary.x1)) ||
559             (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) {
560                 start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary);
561         }
562 }
563
564 bool
565 Editor::autoscroll_active () const
566 {
567         return autoscroll_connection.connected ();
568 }
569
570 bool
571 Editor::autoscroll_canvas ()
572 {
573         int x, y;
574         Gdk::ModifierType mask;
575         frameoffset_t dx = 0;
576         bool no_stop = false;
577         bool y_motion = false;
578
579         get_window()->get_pointer (x, y, mask);
580
581         VisualChange vc;
582         bool vertical_motion = false;
583
584         if (autoscroll_horizontal_allowed) {
585
586                 framepos_t new_frame = leftmost_frame;
587
588                 /* horizontal */
589
590                 if (x > autoscroll_boundary.x1) {
591
592                         /* bring it back into view */
593                         dx = x - autoscroll_boundary.x1;
594                         dx += 10 + (2 * (autoscroll_cnt/2));
595
596                         dx = pixel_to_sample (dx);
597
598                         if (leftmost_frame < max_framepos - dx) {
599                                 new_frame = leftmost_frame + dx;
600                         } else {
601                                 new_frame = max_framepos;
602                         }
603
604                         no_stop = true;
605
606                 } else if (x < autoscroll_boundary.x0) {
607                         
608                         dx = autoscroll_boundary.x0 - x;
609                         dx += 10 + (2 * (autoscroll_cnt/2));
610
611                         dx = pixel_to_sample (dx);
612
613                         if (leftmost_frame >= dx) {
614                                 new_frame = leftmost_frame - dx;
615                         } else {
616                                 new_frame = 0;
617                         }
618
619                         no_stop = true;
620                 }
621                 
622                 if (new_frame != leftmost_frame) {
623                         vc.time_origin = new_frame;
624                         vc.add (VisualChange::TimeOrigin);
625                 }
626         }
627
628         if (autoscroll_vertical_allowed) {
629                 
630                 // const double vertical_pos = vertical_adjustment.get_value();
631                 const int speed_factor = 10;
632
633                 /* vertical */ 
634                 
635                 if (y < autoscroll_boundary.y0) {
636
637                         /* scroll to make higher tracks visible */
638
639                         if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
640                                 y_motion = scroll_up_one_track ();
641                                 vertical_motion = true;
642                         }
643
644                 } else if (y > autoscroll_boundary.y1) {
645
646                         if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
647                                 y_motion = scroll_down_one_track ();
648                                 vertical_motion = true;
649                         }
650                 }
651
652                 no_stop = true;
653         }
654
655         if (vc.pending || vertical_motion) {
656
657                 /* change horizontal first */
658
659                 if (vc.pending) {
660                         visual_changer (vc);
661                 }
662
663                 /* now send a motion event to notify anyone who cares
664                    that we have moved to a new location (because we scrolled)
665                 */
666
667                 GdkEventMotion ev;
668
669                 ev.type = GDK_MOTION_NOTIFY;
670                 ev.state = Gdk::BUTTON1_MASK;
671                 
672                 /* the motion handler expects events in canvas coordinate space */
673
674                 /* we asked for the mouse position above (::get_pointer()) via
675                  * our own top level window (we being the Editor). Convert into 
676                  * coordinates within the canvas window.
677                  */
678
679                 int cx;
680                 int cy;
681
682                 translate_coordinates (*_track_canvas, x, y, cx, cy);
683
684                 /* clamp x and y to remain within the autoscroll boundary,
685                  * which is defined in window coordinates
686                  */
687
688                 x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1);
689                 y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1);
690
691                 /* now convert from Editor window coordinates to canvas
692                  * window coordinates
693                  */
694
695                 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
696                 ev.x = d.x;
697                 ev.y = d.y;
698
699                 motion_handler (0, (GdkEvent*) &ev, true);
700                 
701         } else if (no_stop) {
702
703                 /* not changing visual state but pointer is outside the scrolling boundary
704                  * so we still need to deliver a fake motion event 
705                  */
706
707                 GdkEventMotion ev;
708
709                 ev.type = GDK_MOTION_NOTIFY;
710                 ev.state = Gdk::BUTTON1_MASK;
711                 
712                 /* the motion handler expects events in canvas coordinate space */
713
714                 /* first convert from Editor window coordinates to canvas
715                  * window coordinates
716                  */
717
718                 int cx;
719                 int cy;
720
721                 /* clamp x and y to remain within the visible area. except
722                  * .. if horizontal scrolling is allowed, always allow us to
723                  * move back to zero
724                  */
725
726                 if (autoscroll_horizontal_allowed) {
727                         x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1);
728                 } else {
729                         x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
730                 }
731                 y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
732
733                 translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
734
735                 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
736                 ev.x = d.x;
737                 ev.y = d.y;
738
739                 motion_handler (0, (GdkEvent*) &ev, true);
740                 
741         } else {
742                 stop_canvas_autoscroll ();
743                 return false;
744         }
745
746         autoscroll_cnt++;
747
748         return true; /* call me again */
749 }       
750
751 void
752 Editor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary)
753 {
754         if (!_session) {
755                 return;
756         }
757
758         stop_canvas_autoscroll ();
759
760         autoscroll_cnt = 0;
761         autoscroll_horizontal_allowed = allow_horiz;
762         autoscroll_vertical_allowed = allow_vert;
763         autoscroll_boundary = boundary;
764
765         /* do the first scroll right now
766         */
767
768         autoscroll_canvas ();
769
770         /* scroll again at very very roughly 30FPS */
771
772         autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::autoscroll_canvas), 30);
773 }
774
775 void
776 Editor::stop_canvas_autoscroll ()
777 {
778         autoscroll_connection.disconnect ();
779 }
780
781 bool
782 Editor::left_track_canvas (GdkEventCrossing */*ev*/)
783 {
784         DropDownKeys ();
785         within_track_canvas = false;
786         set_entered_track (0);
787         set_entered_regionview (0);
788         reset_canvas_action_sensitivity (false);
789         return false;
790 }
791
792 bool
793 Editor::entered_track_canvas (GdkEventCrossing */*ev*/)
794 {
795         within_track_canvas = true;
796         reset_canvas_action_sensitivity (true);
797         return FALSE;
798 }
799
800 void
801 Editor::_ensure_time_axis_view_is_visible (const TimeAxisView& tav, bool at_top)
802 {
803         double begin = tav.y_position();
804         double v = vertical_adjustment.get_value ();
805
806         if (!at_top && (begin < v || begin + tav.current_height() > v + _visible_canvas_height)) {
807                 /* try to put the TimeAxisView roughly central */
808                 if (begin >= _visible_canvas_height/2.0) {
809                         begin -= _visible_canvas_height/2.0;
810                 }
811         }
812
813         /* Clamp the y pos so that we do not extend beyond the canvas full
814          * height. 
815          */
816         if (_full_canvas_height - begin < _visible_canvas_height){
817                 begin = _full_canvas_height - _visible_canvas_height;
818         }
819
820         vertical_adjustment.set_value (begin);
821 }
822
823 /** Called when the main vertical_adjustment has changed */
824 void
825 Editor::tie_vertical_scrolling ()
826 {
827         if (pending_visual_change.idle_handler_id < 0) {
828                 _summary->set_overlays_dirty ();
829         }
830 }
831
832 void
833 Editor::set_horizontal_position (double p)
834 {
835         horizontal_adjustment.set_value (p);
836
837         leftmost_frame = (framepos_t) floor (p * samples_per_pixel);
838
839         update_fixed_rulers ();
840         redisplay_tempo (true);
841
842         if (pending_visual_change.idle_handler_id < 0) {
843                 _summary->set_overlays_dirty ();
844         }
845
846         update_video_timeline();
847 }
848
849 void
850 Editor::color_handler()
851 {
852         ArdourCanvas::Color base = ARDOUR_UI::config()->get_canvasvar_RulerBase();
853         ArdourCanvas::Color text = ARDOUR_UI::config()->get_canvasvar_RulerText();
854         timecode_ruler->set_fill_color (base);
855         timecode_ruler->set_outline_color (text);
856         minsec_ruler->set_fill_color (base);
857         minsec_ruler->set_outline_color (text);
858         samples_ruler->set_fill_color (base);
859         samples_ruler->set_outline_color (text);
860         bbt_ruler->set_fill_color (base);
861         bbt_ruler->set_outline_color (text);
862         
863         playhead_cursor->set_color (ARDOUR_UI::config()->get_canvasvar_PlayHead());
864         _verbose_cursor->set_color (ARDOUR_UI::config()->get_canvasvar_VerboseCanvasCursor());
865
866         meter_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_MeterBar());
867         meter_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
868
869         tempo_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TempoBar());
870         tempo_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
871
872         marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_MarkerBar());
873         marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
874
875         cd_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_CDMarkerBar());
876         cd_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
877
878         range_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeMarkerBar());
879         range_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
880
881         transport_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportMarkerBar());
882         transport_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
883
884         cd_marker_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
885         cd_marker_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
886
887         range_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
888         range_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
889
890         transport_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportDragRect());
891         transport_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportDragRect());
892
893         transport_loop_range_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportLoopRect());
894         transport_loop_range_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportLoopRect());
895
896         transport_punch_range_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportPunchRect());
897         transport_punch_range_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportPunchRect());
898
899         transport_punchin_line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_PunchLine());
900         transport_punchout_line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_PunchLine());
901
902         zoom_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ZoomRect());
903         zoom_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZoomRect());
904
905         rubberband_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RubberBandRect());
906         rubberband_rect->set_fill_color ((guint32) ARDOUR_UI::config()->get_canvasvar_RubberBandRect());
907
908         location_marker_color = ARDOUR_UI::config()->get_canvasvar_LocationMarker();
909         location_range_color = ARDOUR_UI::config()->get_canvasvar_LocationRange();
910         location_cd_marker_color = ARDOUR_UI::config()->get_canvasvar_LocationCDMarker();
911         location_loop_color = ARDOUR_UI::config()->get_canvasvar_LocationLoop();
912         location_punch_color = ARDOUR_UI::config()->get_canvasvar_LocationPunch();
913
914         refresh_location_display ();
915 /*
916         redisplay_tempo (true);
917
918         if (_session)
919               _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
920 */
921 }
922
923 double
924 Editor::horizontal_position () const
925 {
926         return sample_to_pixel (leftmost_frame);
927 }
928
929 void
930 Editor::set_canvas_cursor (Gdk::Cursor* cursor, bool save)
931 {
932         if (save) {
933                 current_canvas_cursor = cursor;
934         }
935
936         Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
937
938         if (win) {
939                 _track_canvas->get_window()->set_cursor (*cursor);
940         }
941 }
942
943 void
944 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
945 {
946         if (cursor) {
947                 _cursor_stack.push (cursor);
948                 set_canvas_cursor (cursor, false);
949         }
950 }
951
952 void
953 Editor::pop_canvas_cursor ()
954 {
955         if (!_cursor_stack.empty()) {
956                 Gdk::Cursor* cursor = _cursor_stack.top ();
957                 _cursor_stack.pop ();
958                 set_canvas_cursor (cursor, false);
959         }
960 }
961
962 bool
963 Editor::track_canvas_key_press (GdkEventKey*)
964 {
965         /* XXX: event does not report the modifier key pressed down, AFAICS, so use the Keyboard object instead */
966         if (mouse_mode == Editing::MouseZoom && Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
967                 set_canvas_cursor (_cursors->zoom_out, true);
968         }
969
970         return false;
971 }
972
973 bool
974 Editor::track_canvas_key_release (GdkEventKey*)
975 {
976         if (mouse_mode == Editing::MouseZoom && !Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
977                 set_canvas_cursor (_cursors->zoom_in, true);
978         }
979
980         return false;
981 }
982
983 double
984 Editor::clamp_verbose_cursor_x (double x)
985 {
986         if (x < 0) {
987                 x = 0;
988         } else {
989                 x = min (_visible_canvas_width - 200.0, x);
990         }
991         return x;
992 }
993
994 double
995 Editor::clamp_verbose_cursor_y (double y)
996 {
997         y = max (0.0, y);
998         y = min (_visible_canvas_height - 50, y);
999         return y;
1000 }
1001
1002 ArdourCanvas::GtkCanvasViewport*
1003 Editor::get_track_canvas() const
1004 {
1005         return _track_canvas_viewport;
1006 }