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