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"
38 #include "automation_time_axis.h"
40 #include "global_signals.h"
42 #include "rgb_macros.h"
44 #include "audio_time_axis.h"
45 #include "editor_drag.h"
46 #include "region_view.h"
47 #include "editor_group_tabs.h"
48 #include "editor_summary.h"
49 #include "video_timeline.h"
51 #include "editor_cursors.h"
52 #include "mouse_cursors.h"
53 #include "verbose_cursor.h"
58 using namespace ARDOUR;
62 using namespace Gtkmm2ext;
63 using namespace Editing;
66 Editor::initialize_canvas ()
68 _track_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, vertical_adjustment);
69 _track_canvas = _track_canvas_viewport->canvas ();
71 ArdourCanvas::ScrollGroup* hsg;
72 ArdourCanvas::ScrollGroup* hg;
73 ArdourCanvas::ScrollGroup* vg;
75 hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_track_canvas->root(),
76 ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically|
77 ArdourCanvas::ScrollGroup::ScrollsHorizontally));
78 CANVAS_DEBUG_NAME (hv_scroll_group, "canvas hv scroll");
79 _track_canvas->add_scroller (*hsg);
81 v_scroll_group = vg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsVertically);
82 CANVAS_DEBUG_NAME (v_scroll_group, "canvas v scroll");
83 _track_canvas->add_scroller (*vg);
85 h_scroll_group = hg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
86 CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll");
87 _track_canvas->add_scroller (*hg);
89 _verbose_cursor = new VerboseCursor (this);
91 /* on the bottom, an image */
93 if (Profile->get_sae()) {
94 Image img (::get_icon (X_("saelogo")));
95 // logo_item = new ArdourCanvas::Pixbuf (_track_canvas->root(), 0.0, 0.0, img.get_pixbuf());
96 // logo_item->property_height_in_pixels() = true;
97 // logo_item->property_width_in_pixels() = true;
98 // logo_item->property_height_set() = true;
99 // logo_item->property_width_set() = true;
100 // logo_item->show ();
103 /*a group to hold global rects like punch/loop indicators */
104 global_rect_group = new ArdourCanvas::Group (hv_scroll_group);
105 CANVAS_DEBUG_NAME (global_rect_group, "global rect group");
107 transport_loop_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
108 CANVAS_DEBUG_NAME (transport_loop_range_rect, "loop rect");
109 transport_loop_range_rect->hide();
111 transport_punch_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
112 CANVAS_DEBUG_NAME (transport_punch_range_rect, "punch rect");
113 transport_punch_range_rect->hide();
115 /*a group to hold time (measure) lines */
116 time_line_group = new ArdourCanvas::Group (hv_scroll_group);
117 CANVAS_DEBUG_NAME (time_line_group, "time line group");
119 _trackview_group = new ArdourCanvas::Group (hv_scroll_group);
120 CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
122 // used to show zoom mode active zooming
123 zoom_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
125 zoom_rect->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_zoom_rect_event), (ArdourCanvas::Item*) 0));
127 // used as rubberband rect
128 rubberband_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
129 rubberband_rect->hide();
131 /* a group to hold stuff while it gets dragged around. Must be the
132 * uppermost (last) group with hv_scroll_group as a parent
134 _drag_motion_group = new ArdourCanvas::Group (hv_scroll_group);
135 CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion");
137 /* TIME BAR CANVAS */
139 _time_markers_group = new ArdourCanvas::Group (h_scroll_group);
140 CANVAS_DEBUG_NAME (_time_markers_group, "time bars");
142 cd_marker_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, 0.0));
143 CANVAS_DEBUG_NAME (cd_marker_group, "cd marker group");
144 /* the vide is temporarily placed a the same location as the
145 cd_marker_group, but is moved later.
147 videotl_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0));
148 CANVAS_DEBUG_NAME (videotl_group, "videotl group");
149 marker_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, timebar_height + 1.0));
150 CANVAS_DEBUG_NAME (marker_group, "marker group");
151 transport_marker_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 2.0) + 1.0));
152 CANVAS_DEBUG_NAME (transport_marker_group, "transport marker group");
153 range_marker_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 3.0) + 1.0));
154 CANVAS_DEBUG_NAME (range_marker_group, "range marker group");
155 tempo_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 4.0) + 1.0));
156 CANVAS_DEBUG_NAME (tempo_group, "tempo group");
157 meter_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0));
158 CANVAS_DEBUG_NAME (meter_group, "meter group");
160 meter_bar = new ArdourCanvas::Rectangle (meter_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
161 CANVAS_DEBUG_NAME (meter_bar, "meter Bar");
162 meter_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
164 tempo_bar = new ArdourCanvas::Rectangle (tempo_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
165 CANVAS_DEBUG_NAME (tempo_bar, "Tempo Bar");
166 tempo_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
168 range_marker_bar = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
169 CANVAS_DEBUG_NAME (range_marker_bar, "Range Marker Bar");
170 range_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
172 transport_marker_bar = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
173 CANVAS_DEBUG_NAME (transport_marker_bar, "transport Marker Bar");
174 transport_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
176 marker_bar = new ArdourCanvas::Rectangle (marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
177 CANVAS_DEBUG_NAME (marker_bar, "Marker Bar");
178 marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
180 cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
181 CANVAS_DEBUG_NAME (cd_marker_bar, "CD Marker Bar");
182 cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
184 ARDOUR_UI::instance()->video_timeline = new VideoTimeLine(this, videotl_group, (timebar_height * videotl_bar_height));
186 cd_marker_bar_drag_rect = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
187 CANVAS_DEBUG_NAME (cd_marker_bar_drag_rect, "cd marker drag");
188 cd_marker_bar_drag_rect->set_outline (false);
189 cd_marker_bar_drag_rect->hide ();
191 range_bar_drag_rect = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
192 CANVAS_DEBUG_NAME (range_bar_drag_rect, "range drag");
193 range_bar_drag_rect->set_outline (false);
194 range_bar_drag_rect->hide ();
196 transport_bar_drag_rect = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
197 CANVAS_DEBUG_NAME (transport_bar_drag_rect, "transport drag");
198 transport_bar_drag_rect->set_outline (false);
199 transport_bar_drag_rect->hide ();
201 transport_punchin_line = new ArdourCanvas::Line (hv_scroll_group);
202 transport_punchin_line->set_x0 (0);
203 transport_punchin_line->set_y0 (0);
204 transport_punchin_line->set_x1 (0);
205 transport_punchin_line->set_y1 (ArdourCanvas::COORD_MAX);
206 transport_punchin_line->hide ();
208 transport_punchout_line = new ArdourCanvas::Line (hv_scroll_group);
209 transport_punchout_line->set_x0 (0);
210 transport_punchout_line->set_y0 (0);
211 transport_punchout_line->set_x1 (0);
212 transport_punchout_line->set_y1 (ArdourCanvas::COORD_MAX);
213 transport_punchout_line->hide();
215 tempo_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_tempo_bar_event), tempo_bar));
216 meter_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_meter_bar_event), meter_bar));
217 marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_marker_bar_event), marker_bar));
218 cd_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_cd_marker_bar_event), cd_marker_bar));
219 videotl_group->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_videotl_bar_event), videotl_group));
220 range_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_range_marker_bar_event), range_marker_bar));
221 transport_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_transport_marker_bar_event), transport_marker_bar));
223 playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event);
226 logo_item->lower_to_bottom ();
230 _canvas_drop_zone = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, 0.0));
231 /* this thing is transparent */
232 _canvas_drop_zone->set_fill (false);
233 _canvas_drop_zone->set_outline (false);
234 _canvas_drop_zone->Event.connect (sigc::mem_fun (*this, &Editor::canvas_drop_zone_event));
236 /* these signals will initially be delivered to the canvas itself, but if they end up remaining unhandled, they are passed to Editor-level
240 _track_canvas->signal_scroll_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_scroll_event), true));
241 _track_canvas->signal_motion_notify_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_motion_notify_event));
242 _track_canvas->signal_button_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_press_event));
243 _track_canvas->signal_button_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_release_event));
244 _track_canvas->signal_drag_motion().connect (sigc::mem_fun (*this, &Editor::track_canvas_drag_motion));
245 _track_canvas->signal_key_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_press));
246 _track_canvas->signal_key_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_release));
248 _track_canvas->set_name ("EditorMainCanvas");
249 _track_canvas->add_events (Gdk::POINTER_MOTION_HINT_MASK | Gdk::SCROLL_MASK | Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
250 _track_canvas->signal_leave_notify_event().connect (sigc::mem_fun(*this, &Editor::left_track_canvas), false);
251 _track_canvas->signal_enter_notify_event().connect (sigc::mem_fun(*this, &Editor::entered_track_canvas), false);
252 _track_canvas->set_flags (CAN_FOCUS);
254 /* set up drag-n-drop */
256 vector<TargetEntry> target_table;
258 // Drag-N-Drop from the region list can generate this target
259 target_table.push_back (TargetEntry ("regions"));
261 target_table.push_back (TargetEntry ("text/plain"));
262 target_table.push_back (TargetEntry ("text/uri-list"));
263 target_table.push_back (TargetEntry ("application/x-rootwin-drop"));
265 _track_canvas->drag_dest_set (target_table);
266 _track_canvas->signal_drag_data_received().connect (sigc::mem_fun(*this, &Editor::track_canvas_drag_data_received));
268 _track_canvas_viewport->signal_size_allocate().connect (sigc::mem_fun(*this, &Editor::track_canvas_viewport_allocate));
270 initialize_rulers ();
272 ColorsChanged.connect (sigc::mem_fun (*this, &Editor::color_handler));
278 Editor::track_canvas_viewport_allocate (Gtk::Allocation alloc)
280 _canvas_viewport_allocation = alloc;
281 track_canvas_viewport_size_allocated ();
285 Editor::track_canvas_viewport_size_allocated ()
287 bool height_changed = _visible_canvas_height != _canvas_viewport_allocation.get_height();
289 _visible_canvas_width = _canvas_viewport_allocation.get_width ();
290 _visible_canvas_height = _canvas_viewport_allocation.get_height ();
292 _canvas_drop_zone->set_y1 (_canvas_drop_zone->y0() + (_visible_canvas_height - 20.0));
296 if (height_changed) {
298 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
299 i->second->canvas_height_set (_visible_canvas_height);
302 vertical_adjustment.set_page_size (_visible_canvas_height);
303 if ((vertical_adjustment.get_value() + _visible_canvas_height) >= vertical_adjustment.get_upper()) {
305 We're increasing the size of the canvas while the bottom is visible.
306 We scroll down to keep in step with the controls layout.
308 vertical_adjustment.set_value (_full_canvas_height - _visible_canvas_height);
311 set_visible_track_count (_visible_track_count);
314 update_fixed_rulers();
315 redisplay_tempo (false);
316 _summary->set_overlays_dirty ();
320 Editor::reset_controls_layout_width ()
325 edit_controls_vbox.size_request (req);
328 if (_group_tabs->is_mapped()) {
329 _group_tabs->size_request (req);
333 /* the controls layout has no horizontal scrolling, its visible
334 width is always equal to the total width of its contents.
337 controls_layout.property_width() = w;
338 controls_layout.property_width_request() = w;
342 Editor::reset_controls_layout_height (int32_t h)
344 /* ensure that the rect that represents the "bottom" of the canvas
345 * (the drag-n-drop zone) is, in fact, at the bottom.
348 _canvas_drop_zone->set_position (ArdourCanvas::Duple (0, h));
350 /* track controls layout must span the full height of "h" (all tracks)
351 * plus the bottom rect.
354 h += _canvas_drop_zone->height ();
356 /* set the height of the scrollable area (i.e. the sum of all contained widgets)
357 * for the controls layout. The size request is set elsewhere.
360 controls_layout.property_height() = h;
365 Editor::track_canvas_map_handler (GdkEventAny* /*ev*/)
367 if (current_canvas_cursor) {
368 set_canvas_cursor (current_canvas_cursor);
373 /** This is called when something is dropped onto the track canvas */
375 Editor::track_canvas_drag_data_received (const RefPtr<Gdk::DragContext>& context,
377 const SelectionData& data,
378 guint info, guint time)
380 if (data.get_target() == "regions") {
381 drop_regions (context, x, y, data, info, time);
383 drop_paths (context, x, y, data, info, time);
388 Editor::idle_drop_paths (vector<string> paths, framepos_t frame, double ypos, bool copy)
390 drop_paths_part_two (paths, frame, ypos, copy);
395 Editor::drop_paths_part_two (const vector<string>& paths, framepos_t frame, double ypos, bool copy)
397 RouteTimeAxisView* tv;
399 /* MIDI files must always be imported, because we consider them
400 * writable. So split paths into two vectors, and follow the import
401 * path on the MIDI part.
404 vector<string> midi_paths;
405 vector<string> audio_paths;
407 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
408 if (SMFSource::safe_midi_file_extension (*i)) {
409 midi_paths.push_back (*i);
411 audio_paths.push_back (*i);
416 std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (ypos);
417 if (tvp.first == 0) {
419 /* drop onto canvas background: create new tracks */
423 do_import (midi_paths, Editing::ImportDistinctFiles, ImportAsTrack, SrcBest, frame);
425 if (Profile->get_sae() || Config->get_only_copy_imported_files() || copy) {
426 do_import (audio_paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack, SrcBest, frame);
428 do_embed (audio_paths, Editing::ImportDistinctFiles, ImportAsTrack, frame);
431 } else if ((tv = dynamic_cast<RouteTimeAxisView*> (tvp.first)) != 0) {
433 /* check that its a track, not a bus */
436 /* select the track, then embed/import */
439 do_import (midi_paths, Editing::ImportSerializeFiles, ImportToTrack, SrcBest, frame);
441 if (Profile->get_sae() || Config->get_only_copy_imported_files() || copy) {
442 do_import (audio_paths, Editing::ImportSerializeFiles, Editing::ImportToTrack, SrcBest, frame);
444 do_embed (audio_paths, Editing::ImportSerializeFiles, ImportToTrack, frame);
451 Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
453 const SelectionData& data,
454 guint info, guint time)
456 vector<string> paths;
461 if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
463 /* D-n-D coordinates are window-relative, so convert to canvas coordinates
466 ev.type = GDK_BUTTON_RELEASE;
470 frame = window_event_sample (&ev, 0, &cy);
474 bool copy = ((context->get_actions() & (Gdk::ACTION_COPY | Gdk::ACTION_LINK | Gdk::ACTION_MOVE)) == Gdk::ACTION_COPY);
476 /* We are not allowed to call recursive main event loops from within
477 the main event loop with GTK/Quartz. Since import/embed wants
478 to push up a progress dialog, defer all this till we go idle.
480 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun (*this, &Editor::idle_drop_paths), paths, frame, cy, copy));
482 drop_paths_part_two (paths, frame, cy, copy);
486 context->drag_finish (true, false, time);
489 /** @param allow_horiz true to allow horizontal autoscroll, otherwise false.
491 * @param allow_vert true to allow vertical autoscroll, otherwise false.
495 Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
497 if (!Config->get_autoscroll_editor () || autoscroll_active ()) {
501 /* define a rectangular boundary for scrolling. If the mouse moves
502 * outside of this area and/or continue to be outside of this area,
503 * then we will continuously auto-scroll the canvas in the appropriate
506 * the boundary is defined in coordinates relative to the toplevel
507 * window since that is what we're going to call ::get_pointer() on
508 * during autoscrolling to determine if we're still outside the
512 ArdourCanvas::Rect scrolling_boundary;
513 Gtk::Allocation alloc;
517 alloc = controls_layout.get_allocation ();
519 alloc = _track_canvas_viewport->get_allocation ();
523 /* reduce height by the height of the timebars, which happens
524 to correspond to the position of the hv_scroll_group.
527 alloc.set_height (alloc.get_height() - hv_scroll_group->position().y);
528 alloc.set_y (alloc.get_y() + hv_scroll_group->position().y);
530 /* now reduce it again so that we start autoscrolling before we
531 * move off the top or bottom of the canvas
534 alloc.set_height (alloc.get_height() - 20);
535 alloc.set_y (alloc.get_y() + 10);
537 /* the effective width of the autoscroll boundary so
538 that we start scrolling before we hit the edge.
540 this helps when the window is slammed up against the
541 right edge of the screen, making it hard to scroll
545 if (alloc.get_width() > 20) {
546 alloc.set_width (alloc.get_width() - 20);
547 alloc.set_x (alloc.get_x() + 10);
552 scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), alloc.get_x() + alloc.get_width(), alloc.get_y() + alloc.get_height());
555 Gdk::ModifierType mask;
557 get_window()->get_pointer (x, y, mask);
559 if ((allow_horiz && ((x < scrolling_boundary.x0 && leftmost_frame > 0) || x >= scrolling_boundary.x1)) ||
560 (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) {
561 start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary);
566 Editor::autoscroll_active () const
568 return autoscroll_connection.connected ();
572 Editor::autoscroll_canvas ()
575 Gdk::ModifierType mask;
576 frameoffset_t dx = 0;
577 bool no_stop = false;
578 bool y_motion = false;
580 get_window()->get_pointer (x, y, mask);
583 bool vertical_motion = false;
585 if (autoscroll_horizontal_allowed) {
587 framepos_t new_frame = leftmost_frame;
591 if (x > autoscroll_boundary.x1) {
593 /* bring it back into view */
594 dx = x - autoscroll_boundary.x1;
595 dx += 10 + (2 * (autoscroll_cnt/2));
597 dx = pixel_to_sample (dx);
599 if (leftmost_frame < max_framepos - dx) {
600 new_frame = leftmost_frame + dx;
602 new_frame = max_framepos;
607 } else if (x < autoscroll_boundary.x0) {
609 dx = autoscroll_boundary.x0 - x;
610 dx += 10 + (2 * (autoscroll_cnt/2));
612 dx = pixel_to_sample (dx);
614 if (leftmost_frame >= dx) {
615 new_frame = leftmost_frame - dx;
623 if (new_frame != leftmost_frame) {
624 vc.time_origin = new_frame;
625 vc.add (VisualChange::TimeOrigin);
629 if (autoscroll_vertical_allowed) {
631 // const double vertical_pos = vertical_adjustment.get_value();
632 const int speed_factor = 10;
636 if (y < autoscroll_boundary.y0) {
638 /* scroll to make higher tracks visible */
640 if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
641 y_motion = scroll_up_one_track ();
642 vertical_motion = true;
645 } else if (y > autoscroll_boundary.y1) {
647 if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
648 y_motion = scroll_down_one_track ();
649 vertical_motion = true;
656 if (vc.pending || vertical_motion) {
658 /* change horizontal first */
664 /* now send a motion event to notify anyone who cares
665 that we have moved to a new location (because we scrolled)
670 ev.type = GDK_MOTION_NOTIFY;
671 ev.state = Gdk::BUTTON1_MASK;
673 /* the motion handler expects events in canvas coordinate space */
675 /* we asked for the mouse position above (::get_pointer()) via
676 * our own top level window (we being the Editor). Convert into
677 * coordinates within the canvas window.
683 translate_coordinates (*_track_canvas, x, y, cx, cy);
685 /* clamp x and y to remain within the autoscroll boundary,
686 * which is defined in window coordinates
689 x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1);
690 y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1);
692 /* now convert from Editor window coordinates to canvas
696 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
700 motion_handler (0, (GdkEvent*) &ev, true);
702 } else if (no_stop) {
704 /* not changing visual state but pointer is outside the scrolling boundary
705 * so we still need to deliver a fake motion event
710 ev.type = GDK_MOTION_NOTIFY;
711 ev.state = Gdk::BUTTON1_MASK;
713 /* the motion handler expects events in canvas coordinate space */
715 /* first convert from Editor window coordinates to canvas
722 /* clamp x and y to remain within the visible area. except
723 * .. if horizontal scrolling is allowed, always allow us to
727 if (autoscroll_horizontal_allowed) {
728 x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1);
730 x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
732 y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
734 translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
736 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
740 motion_handler (0, (GdkEvent*) &ev, true);
743 stop_canvas_autoscroll ();
749 return true; /* call me again */
753 Editor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary)
759 stop_canvas_autoscroll ();
762 autoscroll_horizontal_allowed = allow_horiz;
763 autoscroll_vertical_allowed = allow_vert;
764 autoscroll_boundary = boundary;
766 /* do the first scroll right now
769 autoscroll_canvas ();
771 /* scroll again at very very roughly 30FPS */
773 autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::autoscroll_canvas), 30);
777 Editor::stop_canvas_autoscroll ()
779 autoscroll_connection.disconnect ();
783 Editor::left_track_canvas (GdkEventCrossing */*ev*/)
786 within_track_canvas = false;
787 set_entered_track (0);
788 set_entered_regionview (0);
789 reset_canvas_action_sensitivity (false);
794 Editor::entered_track_canvas (GdkEventCrossing */*ev*/)
796 within_track_canvas = true;
797 reset_canvas_action_sensitivity (true);
802 Editor::_ensure_time_axis_view_is_visible (const TimeAxisView& tav, bool at_top)
804 double begin = tav.y_position();
805 double v = vertical_adjustment.get_value ();
807 if (!at_top && (begin < v || begin + tav.current_height() > v + _visible_canvas_height)) {
808 /* try to put the TimeAxisView roughly central */
809 if (begin >= _visible_canvas_height/2.0) {
810 begin -= _visible_canvas_height/2.0;
814 /* Clamp the y pos so that we do not extend beyond the canvas full
817 if (_full_canvas_height - begin < _visible_canvas_height){
818 begin = _full_canvas_height - _visible_canvas_height;
821 vertical_adjustment.set_value (begin);
824 /** Called when the main vertical_adjustment has changed */
826 Editor::tie_vertical_scrolling ()
828 if (pending_visual_change.idle_handler_id < 0) {
829 _summary->set_overlays_dirty ();
834 Editor::set_horizontal_position (double p)
836 horizontal_adjustment.set_value (p);
838 leftmost_frame = (framepos_t) floor (p * samples_per_pixel);
840 update_fixed_rulers ();
841 redisplay_tempo (true);
843 if (pending_visual_change.idle_handler_id < 0) {
844 _summary->set_overlays_dirty ();
847 update_video_timeline();
851 Editor::color_handler()
853 ArdourCanvas::Color base = ARDOUR_UI::config()->get_canvasvar_RulerBase();
854 ArdourCanvas::Color text = ARDOUR_UI::config()->get_canvasvar_RulerText();
855 timecode_ruler->set_fill_color (base);
856 timecode_ruler->set_outline_color (text);
857 minsec_ruler->set_fill_color (base);
858 minsec_ruler->set_outline_color (text);
859 samples_ruler->set_fill_color (base);
860 samples_ruler->set_outline_color (text);
861 bbt_ruler->set_fill_color (base);
862 bbt_ruler->set_outline_color (text);
864 playhead_cursor->set_color (ARDOUR_UI::config()->get_canvasvar_PlayHead());
865 _verbose_cursor->set_color (ARDOUR_UI::config()->get_canvasvar_VerboseCanvasCursor());
867 meter_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_MeterBar());
868 meter_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
870 tempo_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TempoBar());
871 tempo_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
873 marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_MarkerBar());
874 marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
876 cd_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_CDMarkerBar());
877 cd_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
879 range_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeMarkerBar());
880 range_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
882 transport_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportMarkerBar());
883 transport_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
885 cd_marker_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
886 cd_marker_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
888 range_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
889 range_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
891 transport_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportDragRect());
892 transport_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportDragRect());
894 transport_loop_range_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportLoopRect());
895 transport_loop_range_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportLoopRect());
897 transport_punch_range_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportPunchRect());
898 transport_punch_range_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportPunchRect());
900 transport_punchin_line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_PunchLine());
901 transport_punchout_line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_PunchLine());
903 zoom_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ZoomRect());
904 zoom_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZoomRect());
906 rubberband_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RubberBandRect());
907 rubberband_rect->set_fill_color ((guint32) ARDOUR_UI::config()->get_canvasvar_RubberBandRect());
909 location_marker_color = ARDOUR_UI::config()->get_canvasvar_LocationMarker();
910 location_range_color = ARDOUR_UI::config()->get_canvasvar_LocationRange();
911 location_cd_marker_color = ARDOUR_UI::config()->get_canvasvar_LocationCDMarker();
912 location_loop_color = ARDOUR_UI::config()->get_canvasvar_LocationLoop();
913 location_punch_color = ARDOUR_UI::config()->get_canvasvar_LocationPunch();
915 refresh_location_display ();
917 redisplay_tempo (true);
920 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
925 Editor::horizontal_position () const
927 return sample_to_pixel (leftmost_frame);
931 Editor::track_canvas_key_press (GdkEventKey*)
933 /* XXX: event does not report the modifier key pressed down, AFAICS, so use the Keyboard object instead */
934 if (mouse_mode == Editing::MouseZoom && Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
935 set_canvas_cursor (_cursors->zoom_out, true);
942 Editor::track_canvas_key_release (GdkEventKey*)
944 if (mouse_mode == Editing::MouseZoom && !Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
945 set_canvas_cursor (_cursors->zoom_in, true);
952 Editor::clamp_verbose_cursor_x (double x)
957 x = min (_visible_canvas_width - 200.0, x);
963 Editor::clamp_verbose_cursor_y (double y)
966 y = min (_visible_canvas_height - 50, y);
970 ArdourCanvas::GtkCanvasViewport*
971 Editor::get_track_canvas() const
973 return _track_canvas_viewport;
977 Editor::set_canvas_cursor (Gdk::Cursor* cursor, bool save)
980 current_canvas_cursor = cursor;
983 Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
986 _track_canvas->get_window()->set_cursor (*cursor);
991 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
994 _cursor_stack.push (cursor);
995 set_canvas_cursor (cursor, false);
1000 Editor::pop_canvas_cursor ()
1002 if (!_cursor_stack.empty()) {
1003 Gdk::Cursor* cursor = _cursor_stack.top ();
1004 _cursor_stack.pop ();
1005 set_canvas_cursor (cursor, false);
1010 Editor::which_grabber_cursor () const
1012 Gdk::Cursor* c = _cursors->grabber;
1014 if (_internal_editing) {
1015 switch (mouse_mode) {
1017 c = _cursors->midi_pencil;
1021 c = _cursors->grabber_note;
1025 c = _cursors->midi_resize;
1029 c = _cursors->grabber_note;
1038 switch (_edit_point) {
1040 c = _cursors->grabber_edit_point;
1043 boost::shared_ptr<Movable> m = _movable.lock();
1044 if (m && m->locked()) {
1045 c = _cursors->speaker;
1055 Editor::which_trim_cursor (bool left) const
1057 if (!entered_regionview) {
1061 Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
1065 if (ct & Trimmable::FrontTrimEarlier) {
1066 return _cursors->left_side_trim;
1068 return _cursors->left_side_trim_right_only;
1071 if (ct & Trimmable::EndTrimLater) {
1072 return _cursors->right_side_trim;
1074 return _cursors->right_side_trim_left_only;
1080 Editor::which_mode_cursor () const
1082 Gdk::Cursor* mode_cursor = 0;
1084 switch (mouse_mode) {
1086 mode_cursor = _cursors->selector;
1087 if (_internal_editing) {
1088 mode_cursor = which_grabber_cursor();
1093 /* don't use mode cursor, pick a grabber cursor based on the item */
1097 mode_cursor = _cursors->midi_pencil;
1101 mode_cursor = _cursors->cross_hair;
1105 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
1106 mode_cursor = _cursors->zoom_out;
1108 mode_cursor = _cursors->zoom_in;
1113 mode_cursor = _cursors->time_fx; // just use playhead
1117 mode_cursor = _cursors->speaker;
1121 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
1122 if (!_internal_editing && get_smart_mode() ) {
1125 get_pointer_position (x, y);
1127 if (x >= 0 && y >= 0) {
1129 vector<ArdourCanvas::Item const *> items;
1131 /* Note how we choose a specific scroll group to get
1132 * items from. This could be problematic.
1135 hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
1137 // first item will be the upper most
1139 if (!items.empty()) {
1140 const ArdourCanvas::Item* i = items.front();
1142 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
1143 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
1144 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
1145 mode_cursor = _cursors->up_down;
1156 Editor::which_region_cursor () const
1158 Gdk::Cursor* cursor = 0;
1160 assert (mouse_mode == MouseObject || get_smart_mode());
1162 if (!_internal_editing) {
1163 switch (_join_object_range_state) {
1164 case JOIN_OBJECT_RANGE_NONE:
1165 case JOIN_OBJECT_RANGE_OBJECT:
1166 cursor = which_grabber_cursor ();
1167 cerr << "region use grabber\n";
1169 case JOIN_OBJECT_RANGE_RANGE:
1170 cursor = _cursors->selector;
1171 cerr << "region use selector\n";
1180 Editor::reset_canvas_cursor ()
1182 if (!is_drawable()) {
1186 Gdk::Cursor* cursor = which_mode_cursor ();
1189 set_canvas_cursor (cursor);
1197 Editor::choose_canvas_cursor_on_entry (GdkEventCrossing* /*event*/, ItemType type)
1199 Gdk::Cursor* cursor = 0;
1201 cerr << "entered new item type " << enum_2_string (type) << endl;
1203 if (_drags->active()) {
1207 cursor = which_mode_cursor ();
1209 if (mouse_mode == MouseObject || get_smart_mode ()) {
1211 /* find correct cursor to use in object/smart mode */
1215 case RegionViewNameHighlight:
1216 case RegionViewName:
1218 cursor = which_region_cursor ();
1220 case PlayheadCursorItem:
1221 switch (_edit_point) {
1223 cursor = _cursors->grabber_edit_point;
1226 cursor = _cursors->grabber;
1231 cursor = _cursors->selector;
1233 case ControlPointItem:
1234 cursor = _cursors->fader;
1237 cursor = _cursors->fader;
1239 case AutomationLineItem:
1240 cursor = _cursors->cross_hair;
1242 case StartSelectionTrimItem:
1244 case EndSelectionTrimItem:
1246 case AutomationTrackItem:
1247 cursor = _cursors->cross_hair;
1250 cursor = _cursors->fade_in;
1252 case FadeInHandleItem:
1253 cursor = _cursors->fade_in;
1255 case FadeInTrimHandleItem:
1256 cursor = _cursors->fade_in;
1259 cursor = _cursors->fade_out;
1261 case FadeOutHandleItem:
1262 cursor = _cursors->fade_out;
1264 case FadeOutTrimHandleItem:
1265 cursor = _cursors->fade_out;
1268 cursor = which_grabber_cursor();
1270 case FeatureLineItem:
1271 cursor = _cursors->cross_hair;
1273 case LeftFrameHandle:
1274 cursor = which_trim_cursor (true);
1276 case RightFrameHandle:
1277 cursor = which_trim_cursor (false);
1279 case StartCrossFadeItem:
1280 cursor = _cursors->fade_in;
1282 case EndCrossFadeItem:
1283 cursor = _cursors->fade_out;
1285 case CrossfadeViewItem:
1286 cursor = _cursors->cross_hair;
1294 /* These items use the timebar cursor at all times */
1295 case TimecodeRulerItem:
1296 case MinsecRulerItem:
1298 case SamplesRulerItem:
1299 cursor = _cursors->timebar;
1302 /* These items use the grabber cursor at all times */
1303 case MeterMarkerItem:
1304 case TempoMarkerItem:
1309 case RangeMarkerBarItem:
1310 case CdMarkerBarItem:
1312 case TransportMarkerBarItem:
1313 cursor = which_grabber_cursor();
1321 set_canvas_cursor (cursor, false);