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