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