2 Copyright (C) 2005 Paul Davis
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.
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.
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.
21 #include "gtk2ardour-config.h"
24 #include "gtkmm2ext/utils.h"
26 #include "ardour/profile.h"
27 #include "ardour/rc_configuration.h"
28 #include "ardour/smf_source.h"
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"
37 #include "ardour_ui.h"
39 #include "global_signals.h"
41 #include "rgb_macros.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"
50 #include "editor_cursors.h"
51 #include "mouse_cursors.h"
52 #include "verbose_cursor.h"
57 using namespace ARDOUR;
61 using namespace Gtkmm2ext;
62 using namespace Editing;
65 Editor::initialize_canvas ()
67 _track_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, vertical_adjustment);
68 _track_canvas = _track_canvas_viewport->canvas ();
70 ArdourCanvas::ScrollGroup* hsg;
71 ArdourCanvas::ScrollGroup* hg;
72 ArdourCanvas::ScrollGroup* vg;
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);
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);
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);
88 _verbose_cursor = new VerboseCursor (this);
90 /* on the bottom, an image */
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 ();
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");
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();
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();
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");
118 _trackview_group = new ArdourCanvas::Group (hv_scroll_group);
119 CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
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));
124 zoom_rect->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_zoom_rect_event), (ArdourCanvas::Item*) 0));
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();
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
133 _drag_motion_group = new ArdourCanvas::Group (hv_scroll_group);
134 CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion");
136 /* TIME BAR CANVAS */
138 _time_markers_group = new ArdourCanvas::Group (h_scroll_group);
139 CANVAS_DEBUG_NAME (_time_markers_group, "time bars");
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.
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");
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);
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);
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);
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);
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);
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);
183 ARDOUR_UI::instance()->video_timeline = new VideoTimeLine(this, videotl_group, (timebar_height * videotl_bar_height));
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 ();
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 ();
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 ();
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 ();
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();
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));
222 playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event);
225 logo_item->lower_to_bottom ();
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));
235 /* these signals will initially be delivered to the canvas itself, but if they end up remaining unhandled, they are passed to Editor-level
239 _track_canvas->signal_scroll_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_scroll_event), true));
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));
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);
253 /* set up drag-n-drop */
255 vector<TargetEntry> target_table;
257 // Drag-N-Drop from the region list can generate this target
258 target_table.push_back (TargetEntry ("regions"));
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"));
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));
267 _track_canvas_viewport->signal_size_allocate().connect (sigc::mem_fun(*this, &Editor::track_canvas_viewport_allocate));
269 initialize_rulers ();
271 ColorsChanged.connect (sigc::mem_fun (*this, &Editor::color_handler));
277 Editor::track_canvas_viewport_allocate (Gtk::Allocation alloc)
279 _canvas_viewport_allocation = alloc;
280 track_canvas_viewport_size_allocated ();
284 Editor::track_canvas_viewport_size_allocated ()
286 bool height_changed = _visible_canvas_height != _canvas_viewport_allocation.get_height();
288 _visible_canvas_width = _canvas_viewport_allocation.get_width ();
289 _visible_canvas_height = _canvas_viewport_allocation.get_height ();
291 _canvas_drop_zone->set_y1 (_canvas_drop_zone->y0() + (_visible_canvas_height - 20.0));
295 if (height_changed) {
297 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
298 i->second->canvas_height_set (_visible_canvas_height);
301 vertical_adjustment.set_page_size (_visible_canvas_height);
302 if ((vertical_adjustment.get_value() + _visible_canvas_height) >= vertical_adjustment.get_upper()) {
304 We're increasing the size of the canvas while the bottom is visible.
305 We scroll down to keep in step with the controls layout.
307 vertical_adjustment.set_value (_full_canvas_height - _visible_canvas_height);
310 set_visible_track_count (_visible_track_count);
313 update_fixed_rulers();
314 redisplay_tempo (false);
315 _summary->set_overlays_dirty ();
319 Editor::reset_controls_layout_width ()
324 edit_controls_vbox.size_request (req);
327 if (_group_tabs->is_mapped()) {
328 _group_tabs->size_request (req);
332 /* the controls layout has no horizontal scrolling, its visible
333 width is always equal to the total width of its contents.
336 controls_layout.property_width() = w;
337 controls_layout.property_width_request() = w;
341 Editor::reset_controls_layout_height (int32_t h)
343 /* ensure that the rect that represents the "bottom" of the canvas
344 * (the drag-n-drop zone) is, in fact, at the bottom.
347 _canvas_drop_zone->set_position (ArdourCanvas::Duple (0, h));
349 /* track controls layout must span the full height of "h" (all tracks)
350 * plus the bottom rect.
353 h += _canvas_drop_zone->height ();
355 /* set the height of the scrollable area (i.e. the sum of all contained widgets)
356 * for the controls layout. The size request is set elsewhere.
359 controls_layout.property_height() = h;
364 Editor::track_canvas_map_handler (GdkEventAny* /*ev*/)
366 if (current_canvas_cursor) {
367 set_canvas_cursor (current_canvas_cursor);
372 /** This is called when something is dropped onto the track canvas */
374 Editor::track_canvas_drag_data_received (const RefPtr<Gdk::DragContext>& context,
376 const SelectionData& data,
377 guint info, guint time)
379 if (data.get_target() == "regions") {
380 drop_regions (context, x, y, data, info, time);
382 drop_paths (context, x, y, data, info, time);
387 Editor::idle_drop_paths (vector<string> paths, framepos_t frame, double ypos, bool copy)
389 drop_paths_part_two (paths, frame, ypos, copy);
394 Editor::drop_paths_part_two (const vector<string>& paths, framepos_t frame, double ypos, bool copy)
396 RouteTimeAxisView* tv;
398 /* MIDI files must always be imported, because we consider them
399 * writable. So split paths into two vectors, and follow the import
400 * path on the MIDI part.
403 vector<string> midi_paths;
404 vector<string> audio_paths;
406 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
407 if (SMFSource::safe_midi_file_extension (*i)) {
408 midi_paths.push_back (*i);
410 audio_paths.push_back (*i);
415 std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (ypos);
416 if (tvp.first == 0) {
418 /* drop onto canvas background: create new tracks */
422 do_import (midi_paths, Editing::ImportDistinctFiles, ImportAsTrack, SrcBest, frame);
424 if (Profile->get_sae() || Config->get_only_copy_imported_files() || copy) {
425 do_import (audio_paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack, SrcBest, frame);
427 do_embed (audio_paths, Editing::ImportDistinctFiles, ImportAsTrack, frame);
430 } else if ((tv = dynamic_cast<RouteTimeAxisView*> (tvp.first)) != 0) {
432 /* check that its a track, not a bus */
435 /* select the track, then embed/import */
438 do_import (midi_paths, Editing::ImportSerializeFiles, ImportToTrack, SrcBest, frame);
440 if (Profile->get_sae() || Config->get_only_copy_imported_files() || copy) {
441 do_import (audio_paths, Editing::ImportSerializeFiles, Editing::ImportToTrack, SrcBest, frame);
443 do_embed (audio_paths, Editing::ImportSerializeFiles, ImportToTrack, frame);
450 Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
452 const SelectionData& data,
453 guint info, guint time)
455 vector<string> paths;
460 if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
462 /* D-n-D coordinates are window-relative, so convert to canvas coordinates
465 ev.type = GDK_BUTTON_RELEASE;
469 frame = window_event_sample (&ev, 0, &cy);
473 bool copy = ((context->get_actions() & (Gdk::ACTION_COPY | Gdk::ACTION_LINK | Gdk::ACTION_MOVE)) == Gdk::ACTION_COPY);
475 /* We are not allowed to call recursive main event loops from within
476 the main event loop with GTK/Quartz. Since import/embed wants
477 to push up a progress dialog, defer all this till we go idle.
479 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun (*this, &Editor::idle_drop_paths), paths, frame, cy, copy));
481 drop_paths_part_two (paths, frame, cy, copy);
485 context->drag_finish (true, false, time);
488 /** @param allow_horiz true to allow horizontal autoscroll, otherwise false.
490 * @param allow_vert true to allow vertical autoscroll, otherwise false.
494 Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
496 if (!Config->get_autoscroll_editor () || autoscroll_active ()) {
500 /* define a rectangular boundary for scrolling. If the mouse moves
501 * outside of this area and/or continue to be outside of this area,
502 * then we will continuously auto-scroll the canvas in the appropriate
505 * the boundary is defined in coordinates relative to the toplevel
506 * window since that is what we're going to call ::get_pointer() on
507 * during autoscrolling to determine if we're still outside the
511 ArdourCanvas::Rect scrolling_boundary;
512 Gtk::Allocation alloc;
516 alloc = controls_layout.get_allocation ();
518 alloc = _track_canvas_viewport->get_allocation ();
522 /* reduce height by the height of the timebars, which happens
523 to correspond to the position of the hv_scroll_group.
526 alloc.set_height (alloc.get_height() - hv_scroll_group->position().y);
527 alloc.set_y (alloc.get_y() + hv_scroll_group->position().y);
529 /* now reduce it again so that we start autoscrolling before we
530 * move off the top or bottom of the canvas
533 alloc.set_height (alloc.get_height() - 20);
534 alloc.set_y (alloc.get_y() + 10);
536 /* the effective width of the autoscroll boundary so
537 that we start scrolling before we hit the edge.
539 this helps when the window is slammed up against the
540 right edge of the screen, making it hard to scroll
544 if (alloc.get_width() > 20) {
545 alloc.set_width (alloc.get_width() - 20);
546 alloc.set_x (alloc.get_x() + 10);
551 scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), alloc.get_x() + alloc.get_width(), alloc.get_y() + alloc.get_height());
554 Gdk::ModifierType mask;
556 get_window()->get_pointer (x, y, mask);
558 if ((allow_horiz && ((x < scrolling_boundary.x0 && leftmost_frame > 0) || x >= scrolling_boundary.x1)) ||
559 (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) {
560 start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary);
565 Editor::autoscroll_active () const
567 return autoscroll_connection.connected ();
571 Editor::autoscroll_canvas ()
574 Gdk::ModifierType mask;
575 frameoffset_t dx = 0;
576 bool no_stop = false;
577 bool y_motion = false;
579 get_window()->get_pointer (x, y, mask);
582 bool vertical_motion = false;
584 if (autoscroll_horizontal_allowed) {
586 framepos_t new_frame = leftmost_frame;
590 if (x > autoscroll_boundary.x1) {
592 /* bring it back into view */
593 dx = x - autoscroll_boundary.x1;
594 dx += 10 + (2 * (autoscroll_cnt/2));
596 dx = pixel_to_sample (dx);
598 if (leftmost_frame < max_framepos - dx) {
599 new_frame = leftmost_frame + dx;
601 new_frame = max_framepos;
606 } else if (x < autoscroll_boundary.x0) {
608 dx = autoscroll_boundary.x0 - x;
609 dx += 10 + (2 * (autoscroll_cnt/2));
611 dx = pixel_to_sample (dx);
613 if (leftmost_frame >= dx) {
614 new_frame = leftmost_frame - dx;
622 if (new_frame != leftmost_frame) {
623 vc.time_origin = new_frame;
624 vc.add (VisualChange::TimeOrigin);
628 if (autoscroll_vertical_allowed) {
630 // const double vertical_pos = vertical_adjustment.get_value();
631 const int speed_factor = 10;
635 if (y < autoscroll_boundary.y0) {
637 /* scroll to make higher tracks visible */
639 if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
640 y_motion = scroll_up_one_track ();
641 vertical_motion = true;
644 } else if (y > autoscroll_boundary.y1) {
646 if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
647 y_motion = scroll_down_one_track ();
648 vertical_motion = true;
655 if (vc.pending || vertical_motion) {
657 /* change horizontal first */
663 /* now send a motion event to notify anyone who cares
664 that we have moved to a new location (because we scrolled)
669 ev.type = GDK_MOTION_NOTIFY;
670 ev.state = Gdk::BUTTON1_MASK;
672 /* the motion handler expects events in canvas coordinate space */
674 /* we asked for the mouse position above (::get_pointer()) via
675 * our own top level window (we being the Editor). Convert into
676 * coordinates within the canvas window.
682 translate_coordinates (*_track_canvas, x, y, cx, cy);
684 /* clamp x and y to remain within the autoscroll boundary,
685 * which is defined in window coordinates
688 x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1);
689 y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1);
691 /* now convert from Editor window coordinates to canvas
695 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
699 motion_handler (0, (GdkEvent*) &ev, true);
701 } else if (no_stop) {
703 /* not changing visual state but pointer is outside the scrolling boundary
704 * so we still need to deliver a fake motion event
709 ev.type = GDK_MOTION_NOTIFY;
710 ev.state = Gdk::BUTTON1_MASK;
712 /* the motion handler expects events in canvas coordinate space */
714 /* first convert from Editor window coordinates to canvas
721 /* clamp x and y to remain within the visible area. except
722 * .. if horizontal scrolling is allowed, always allow us to
726 if (autoscroll_horizontal_allowed) {
727 x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1);
729 x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
731 y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
733 translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
735 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
739 motion_handler (0, (GdkEvent*) &ev, true);
742 stop_canvas_autoscroll ();
748 return true; /* call me again */
752 Editor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary)
758 stop_canvas_autoscroll ();
761 autoscroll_horizontal_allowed = allow_horiz;
762 autoscroll_vertical_allowed = allow_vert;
763 autoscroll_boundary = boundary;
765 /* do the first scroll right now
768 autoscroll_canvas ();
770 /* scroll again at very very roughly 30FPS */
772 autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::autoscroll_canvas), 30);
776 Editor::stop_canvas_autoscroll ()
778 autoscroll_connection.disconnect ();
782 Editor::left_track_canvas (GdkEventCrossing */*ev*/)
785 within_track_canvas = false;
786 set_entered_track (0);
787 set_entered_regionview (0);
788 reset_canvas_action_sensitivity (false);
793 Editor::entered_track_canvas (GdkEventCrossing */*ev*/)
795 within_track_canvas = true;
796 reset_canvas_action_sensitivity (true);
801 Editor::_ensure_time_axis_view_is_visible (const TimeAxisView& tav, bool at_top)
803 double begin = tav.y_position();
804 double v = vertical_adjustment.get_value ();
806 if (!at_top && (begin < v || begin + tav.current_height() > v + _visible_canvas_height)) {
807 /* try to put the TimeAxisView roughly central */
808 if (begin >= _visible_canvas_height/2.0) {
809 begin -= _visible_canvas_height/2.0;
813 /* Clamp the y pos so that we do not extend beyond the canvas full
816 if (_full_canvas_height - begin < _visible_canvas_height){
817 begin = _full_canvas_height - _visible_canvas_height;
820 vertical_adjustment.set_value (begin);
823 /** Called when the main vertical_adjustment has changed */
825 Editor::tie_vertical_scrolling ()
827 if (pending_visual_change.idle_handler_id < 0) {
828 _summary->set_overlays_dirty ();
833 Editor::set_horizontal_position (double p)
835 horizontal_adjustment.set_value (p);
837 leftmost_frame = (framepos_t) floor (p * samples_per_pixel);
839 update_fixed_rulers ();
840 redisplay_tempo (true);
842 if (pending_visual_change.idle_handler_id < 0) {
843 _summary->set_overlays_dirty ();
846 update_video_timeline();
850 Editor::color_handler()
852 ArdourCanvas::Color base = ARDOUR_UI::config()->get_canvasvar_RulerBase();
853 ArdourCanvas::Color text = ARDOUR_UI::config()->get_canvasvar_RulerText();
854 timecode_ruler->set_fill_color (base);
855 timecode_ruler->set_outline_color (text);
856 minsec_ruler->set_fill_color (base);
857 minsec_ruler->set_outline_color (text);
858 samples_ruler->set_fill_color (base);
859 samples_ruler->set_outline_color (text);
860 bbt_ruler->set_fill_color (base);
861 bbt_ruler->set_outline_color (text);
863 playhead_cursor->set_color (ARDOUR_UI::config()->get_canvasvar_PlayHead());
864 _verbose_cursor->set_color (ARDOUR_UI::config()->get_canvasvar_VerboseCanvasCursor());
866 meter_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_MeterBar());
867 meter_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
869 tempo_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TempoBar());
870 tempo_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
872 marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_MarkerBar());
873 marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
875 cd_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_CDMarkerBar());
876 cd_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
878 range_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeMarkerBar());
879 range_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
881 transport_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportMarkerBar());
882 transport_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
884 cd_marker_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
885 cd_marker_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
887 range_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
888 range_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
890 transport_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportDragRect());
891 transport_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportDragRect());
893 transport_loop_range_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportLoopRect());
894 transport_loop_range_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportLoopRect());
896 transport_punch_range_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportPunchRect());
897 transport_punch_range_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportPunchRect());
899 transport_punchin_line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_PunchLine());
900 transport_punchout_line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_PunchLine());
902 zoom_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ZoomRect());
903 zoom_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZoomRect());
905 rubberband_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RubberBandRect());
906 rubberband_rect->set_fill_color ((guint32) ARDOUR_UI::config()->get_canvasvar_RubberBandRect());
908 location_marker_color = ARDOUR_UI::config()->get_canvasvar_LocationMarker();
909 location_range_color = ARDOUR_UI::config()->get_canvasvar_LocationRange();
910 location_cd_marker_color = ARDOUR_UI::config()->get_canvasvar_LocationCDMarker();
911 location_loop_color = ARDOUR_UI::config()->get_canvasvar_LocationLoop();
912 location_punch_color = ARDOUR_UI::config()->get_canvasvar_LocationPunch();
914 refresh_location_display ();
916 redisplay_tempo (true);
919 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
924 Editor::horizontal_position () const
926 return sample_to_pixel (leftmost_frame);
930 Editor::set_canvas_cursor (Gdk::Cursor* cursor, bool save)
933 current_canvas_cursor = cursor;
936 Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
939 _track_canvas->get_window()->set_cursor (*cursor);
944 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
947 _cursor_stack.push (cursor);
948 set_canvas_cursor (cursor, false);
953 Editor::pop_canvas_cursor ()
955 if (!_cursor_stack.empty()) {
956 Gdk::Cursor* cursor = _cursor_stack.top ();
957 _cursor_stack.pop ();
958 set_canvas_cursor (cursor, false);
963 Editor::track_canvas_key_press (GdkEventKey*)
965 /* XXX: event does not report the modifier key pressed down, AFAICS, so use the Keyboard object instead */
966 if (mouse_mode == Editing::MouseZoom && Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
967 set_canvas_cursor (_cursors->zoom_out, true);
974 Editor::track_canvas_key_release (GdkEventKey*)
976 if (mouse_mode == Editing::MouseZoom && !Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
977 set_canvas_cursor (_cursors->zoom_in, true);
984 Editor::clamp_verbose_cursor_x (double x)
989 x = min (_visible_canvas_width - 200.0, x);
995 Editor::clamp_verbose_cursor_y (double y)
998 y = min (_visible_canvas_height - 50, y);
1002 ArdourCanvas::GtkCanvasViewport*
1003 Editor::get_track_canvas() const
1005 return _track_canvas_viewport;