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