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