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