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