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