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 "pbd/error.h"
32 #include "canvas/canvas.h"
33 #include "canvas/rectangle.h"
34 #include "canvas/pixbuf.h"
35 #include "canvas/scroll_group.h"
36 #include "canvas/text.h"
37 #include "canvas/debug.h"
39 #include "ardour_ui.h"
40 #include "automation_time_axis.h"
42 #include "global_signals.h"
44 #include "rgb_macros.h"
46 #include "audio_time_axis.h"
47 #include "editor_drag.h"
48 #include "region_view.h"
49 #include "editor_group_tabs.h"
50 #include "editor_summary.h"
51 #include "video_timeline.h"
53 #include "editor_cursors.h"
54 #include "mouse_cursors.h"
55 #include "verbose_cursor.h"
60 using namespace ARDOUR;
61 using namespace ARDOUR_UI_UTILS;
65 using namespace Gtkmm2ext;
66 using namespace Editing;
69 Editor::initialize_canvas ()
71 _track_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, vertical_adjustment);
72 _track_canvas = _track_canvas_viewport->canvas ();
74 _track_canvas->set_background_color (ARDOUR_UI::config()->color ("arrange base"));
76 /* scroll group for items that should not automatically scroll
77 * (e.g verbose cursor). It shares the canvas coordinate space.
79 no_scroll_group = new ArdourCanvas::Container (_track_canvas->root());
81 ArdourCanvas::ScrollGroup* hsg;
82 ArdourCanvas::ScrollGroup* hg;
84 hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_track_canvas->root(),
85 ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically|
86 ArdourCanvas::ScrollGroup::ScrollsHorizontally));
87 CANVAS_DEBUG_NAME (hv_scroll_group, "canvas hv scroll");
88 _track_canvas->add_scroller (*hsg);
90 h_scroll_group = hg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
91 CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll");
92 _track_canvas->add_scroller (*hg);
94 _verbose_cursor = new VerboseCursor (this);
96 /* on the bottom, an image */
98 if (Profile->get_sae()) {
99 Image img (::get_icon (X_("saelogo")));
100 // logo_item = new ArdourCanvas::Pixbuf (_track_canvas->root(), 0.0, 0.0, img.get_pixbuf());
101 // logo_item->property_height_in_pixels() = true;
102 // logo_item->property_width_in_pixels() = true;
103 // logo_item->property_height_set() = true;
104 // logo_item->property_width_set() = true;
105 // logo_item->show ();
108 /*a group to hold global rects like punch/loop indicators */
109 global_rect_group = new ArdourCanvas::Container (hv_scroll_group);
110 CANVAS_DEBUG_NAME (global_rect_group, "global rect group");
112 transport_loop_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
113 CANVAS_DEBUG_NAME (transport_loop_range_rect, "loop rect");
114 transport_loop_range_rect->hide();
116 transport_punch_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
117 CANVAS_DEBUG_NAME (transport_punch_range_rect, "punch rect");
118 transport_punch_range_rect->hide();
120 /*a group to hold time (measure) lines */
121 time_line_group = new ArdourCanvas::Container (h_scroll_group);
122 CANVAS_DEBUG_NAME (time_line_group, "time line group");
124 _trackview_group = new ArdourCanvas::Container (hv_scroll_group);
125 CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
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::Container (hv_scroll_group);
135 CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion");
137 /* TIME BAR CANVAS */
139 _time_markers_group = new ArdourCanvas::Container (h_scroll_group);
140 CANVAS_DEBUG_NAME (_time_markers_group, "time bars");
142 cd_marker_group = new ArdourCanvas::Container (_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::Container (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0));
148 CANVAS_DEBUG_NAME (videotl_group, "videotl group");
149 marker_group = new ArdourCanvas::Container (_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::Container (_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::Container (_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::Container (_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::Container (_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 ();
229 hv_scroll_group->raise_to_top ();
231 _canvas_drop_zone = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, 0.0));
232 /* this thing is transparent */
233 _canvas_drop_zone->set_fill (false);
234 _canvas_drop_zone->set_outline (false);
235 _canvas_drop_zone->Event.connect (sigc::mem_fun (*this, &Editor::canvas_drop_zone_event));
237 /* these signals will initially be delivered to the canvas itself, but if they end up remaining unhandled, they are passed to Editor-level
241 _track_canvas->signal_scroll_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_scroll_event), true));
242 _track_canvas->signal_motion_notify_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_motion_notify_event));
243 _track_canvas->signal_button_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_press_event));
244 _track_canvas->signal_button_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_release_event));
245 _track_canvas->signal_drag_motion().connect (sigc::mem_fun (*this, &Editor::track_canvas_drag_motion));
246 _track_canvas->signal_key_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_press));
247 _track_canvas->signal_key_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_release));
249 _track_canvas->set_name ("EditorMainCanvas");
250 _track_canvas->add_events (Gdk::POINTER_MOTION_HINT_MASK | Gdk::SCROLL_MASK | Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
251 _track_canvas->signal_leave_notify_event().connect (sigc::mem_fun(*this, &Editor::left_track_canvas), false);
252 _track_canvas->signal_enter_notify_event().connect (sigc::mem_fun(*this, &Editor::entered_track_canvas), false);
253 _track_canvas->set_flags (CAN_FOCUS);
255 /* set up drag-n-drop */
257 vector<TargetEntry> target_table;
259 // Drag-N-Drop from the region list can generate this target
260 target_table.push_back (TargetEntry ("regions"));
262 target_table.push_back (TargetEntry ("text/plain"));
263 target_table.push_back (TargetEntry ("text/uri-list"));
264 target_table.push_back (TargetEntry ("application/x-rootwin-drop"));
266 _track_canvas->drag_dest_set (target_table);
267 _track_canvas->signal_drag_data_received().connect (sigc::mem_fun(*this, &Editor::track_canvas_drag_data_received));
269 _track_canvas_viewport->signal_size_allocate().connect (sigc::mem_fun(*this, &Editor::track_canvas_viewport_allocate));
271 initialize_rulers ();
273 ColorsChanged.connect (sigc::mem_fun (*this, &Editor::color_handler));
279 Editor::track_canvas_viewport_allocate (Gtk::Allocation alloc)
281 _canvas_viewport_allocation = alloc;
282 track_canvas_viewport_size_allocated ();
286 Editor::track_canvas_viewport_size_allocated ()
288 bool height_changed = _visible_canvas_height != _canvas_viewport_allocation.get_height();
290 _visible_canvas_width = _canvas_viewport_allocation.get_width ();
291 _visible_canvas_height = _canvas_viewport_allocation.get_height ();
293 _canvas_drop_zone->set_y1 (_canvas_drop_zone->y0() + (_visible_canvas_height - 20.0));
297 if (height_changed) {
299 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
300 i->second->canvas_height_set (_visible_canvas_height);
303 vertical_adjustment.set_page_size (_visible_canvas_height);
304 if ((vertical_adjustment.get_value() + _visible_canvas_height) >= vertical_adjustment.get_upper()) {
306 We're increasing the size of the canvas while the bottom is visible.
307 We scroll down to keep in step with the controls layout.
309 vertical_adjustment.set_value (_full_canvas_height - _visible_canvas_height);
312 set_visible_track_count (_visible_track_count);
315 update_fixed_rulers();
316 redisplay_tempo (false);
317 _summary->set_overlays_dirty ();
321 Editor::reset_controls_layout_width ()
323 GtkRequisition req = { 0, 0 };
326 edit_controls_vbox.size_request (req);
329 if (_group_tabs->is_mapped()) {
330 _group_tabs->size_request (req);
334 /* the controls layout has no horizontal scrolling, its visible
335 width is always equal to the total width of its contents.
338 controls_layout.property_width() = w;
339 controls_layout.property_width_request() = w;
343 Editor::reset_controls_layout_height (int32_t h)
345 /* ensure that the rect that represents the "bottom" of the canvas
346 * (the drag-n-drop zone) is, in fact, at the bottom.
349 _canvas_drop_zone->set_position (ArdourCanvas::Duple (0, h));
351 /* track controls layout must span the full height of "h" (all tracks)
352 * plus the bottom rect.
355 h += _canvas_drop_zone->height ();
357 /* set the height of the scrollable area (i.e. the sum of all contained widgets)
358 * for the controls layout. The size request is set elsewhere.
361 controls_layout.property_height() = h;
366 Editor::track_canvas_map_handler (GdkEventAny* /*ev*/)
368 if (!_cursor_stack.empty()) {
369 set_canvas_cursor (get_canvas_cursor());
371 PBD::error << "cursor stack is empty" << endmsg;
376 /** This is called when something is dropped onto the track canvas */
378 Editor::track_canvas_drag_data_received (const RefPtr<Gdk::DragContext>& context,
380 const SelectionData& data,
381 guint info, guint time)
383 if (data.get_target() == "regions") {
384 drop_regions (context, x, y, data, info, time);
386 drop_paths (context, x, y, data, info, time);
391 Editor::idle_drop_paths (vector<string> paths, framepos_t frame, double ypos, bool copy)
393 drop_paths_part_two (paths, frame, ypos, copy);
398 Editor::drop_paths_part_two (const vector<string>& paths, framepos_t frame, double ypos, bool copy)
400 RouteTimeAxisView* tv;
402 /* MIDI files must always be imported, because we consider them
403 * writable. So split paths into two vectors, and follow the import
404 * path on the MIDI part.
407 vector<string> midi_paths;
408 vector<string> audio_paths;
410 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
411 if (SMFSource::safe_midi_file_extension (*i)) {
412 midi_paths.push_back (*i);
414 audio_paths.push_back (*i);
419 std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (ypos, false);
420 if (tvp.first == 0) {
422 /* drop onto canvas background: create new tracks */
426 do_import (midi_paths, Editing::ImportDistinctFiles, ImportAsTrack, SrcBest, frame);
428 if (Profile->get_sae() || ARDOUR_UI::config()->get_only_copy_imported_files() || copy) {
429 do_import (audio_paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack, SrcBest, frame);
431 do_embed (audio_paths, Editing::ImportDistinctFiles, ImportAsTrack, frame);
434 } else if ((tv = dynamic_cast<RouteTimeAxisView*> (tvp.first)) != 0) {
436 /* check that its a track, not a bus */
439 /* select the track, then embed/import */
442 do_import (midi_paths, Editing::ImportSerializeFiles, ImportToTrack, SrcBest, frame);
444 if (Profile->get_sae() || ARDOUR_UI::config()->get_only_copy_imported_files() || copy) {
445 do_import (audio_paths, Editing::ImportSerializeFiles, Editing::ImportToTrack, SrcBest, frame);
447 do_embed (audio_paths, Editing::ImportSerializeFiles, ImportToTrack, frame);
454 Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
456 const SelectionData& data,
457 guint info, guint time)
459 vector<string> paths;
464 if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
466 /* D-n-D coordinates are window-relative, so convert to canvas coordinates
469 ev.type = GDK_BUTTON_RELEASE;
473 frame = window_event_sample (&ev, 0, &cy);
477 bool copy = ((context->get_actions() & (Gdk::ACTION_COPY | Gdk::ACTION_LINK | Gdk::ACTION_MOVE)) == Gdk::ACTION_COPY);
479 /* We are not allowed to call recursive main event loops from within
480 the main event loop with GTK/Quartz. Since import/embed wants
481 to push up a progress dialog, defer all this till we go idle.
483 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun (*this, &Editor::idle_drop_paths), paths, frame, cy, copy));
485 drop_paths_part_two (paths, frame, cy, copy);
489 context->drag_finish (true, false, time);
492 /** @param allow_horiz true to allow horizontal autoscroll, otherwise false.
494 * @param allow_vert true to allow vertical autoscroll, otherwise false.
498 Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
500 if (!ARDOUR_UI::config()->get_autoscroll_editor () || autoscroll_active ()) {
504 /* define a rectangular boundary for scrolling. If the mouse moves
505 * outside of this area and/or continue to be outside of this area,
506 * then we will continuously auto-scroll the canvas in the appropriate
509 * the boundary is defined in coordinates relative to the toplevel
510 * window since that is what we're going to call ::get_pointer() on
511 * during autoscrolling to determine if we're still outside the
515 ArdourCanvas::Rect scrolling_boundary;
516 Gtk::Allocation alloc;
519 alloc = controls_layout.get_allocation ();
521 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;
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 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 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 ();
781 Editor::EnterContext*
782 Editor::get_enter_context(ItemType type)
784 for (ssize_t i = _enter_stack.size() - 1; i >= 0; --i) {
785 if (_enter_stack[i].item_type == type) {
786 return &_enter_stack[i];
793 Editor::left_track_canvas (GdkEventCrossing */*ev*/)
796 within_track_canvas = false;
797 set_entered_track (0);
798 set_entered_regionview (0);
799 reset_canvas_action_sensitivity (false);
804 Editor::entered_track_canvas (GdkEventCrossing */*ev*/)
806 within_track_canvas = true;
807 reset_canvas_action_sensitivity (true);
812 Editor::ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top)
814 if (track.hidden()) {
818 /* compute visible area of trackview group, as offsets from top of
822 double const current_view_min_y = vertical_adjustment.get_value();
823 double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size();
825 double const track_min_y = track.y_position ();
826 double const track_max_y = track.y_position () + track.effective_height ();
829 (track_min_y >= current_view_min_y &&
830 track_max_y < current_view_max_y)) {
831 /* already visible, and caller did not ask to place it at the
832 * top of the track canvas
840 new_value = track_min_y;
842 if (track_min_y < current_view_min_y) {
843 // Track is above the current view
844 new_value = track_min_y;
845 } else if (track_max_y > current_view_max_y) {
846 // Track is below the current view
847 new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size();
849 new_value = track_min_y;
853 vertical_adjustment.set_value(new_value);
856 /** Called when the main vertical_adjustment has changed */
858 Editor::tie_vertical_scrolling ()
860 if (pending_visual_change.idle_handler_id < 0) {
861 _summary->set_overlays_dirty ();
866 Editor::set_horizontal_position (double p)
868 horizontal_adjustment.set_value (p);
870 leftmost_frame = (framepos_t) floor (p * samples_per_pixel);
872 update_fixed_rulers ();
873 redisplay_tempo (true);
875 if (pending_visual_change.idle_handler_id < 0) {
876 _summary->set_overlays_dirty ();
879 update_video_timeline();
883 Editor::color_handler()
885 ArdourCanvas::Color base = ARDOUR_UI::config()->color ("ruler base");
886 ArdourCanvas::Color text = ARDOUR_UI::config()->color ("ruler text");
887 timecode_ruler->set_fill_color (base);
888 timecode_ruler->set_outline_color (text);
889 minsec_ruler->set_fill_color (base);
890 minsec_ruler->set_outline_color (text);
891 samples_ruler->set_fill_color (base);
892 samples_ruler->set_outline_color (text);
893 bbt_ruler->set_fill_color (base);
894 bbt_ruler->set_outline_color (text);
896 playhead_cursor->set_color (ARDOUR_UI::config()->color ("play head"));
898 meter_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("meter bar", "marker bar"));
899 meter_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
901 tempo_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("tempo bar", "marker bar"));
902 tempo_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
904 marker_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("marker bar", "marker bar"));
905 marker_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
907 cd_marker_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("cd marker bar", "marker bar"));
908 cd_marker_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
910 range_marker_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("range marker bar", "marker bar"));
911 range_marker_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
913 transport_marker_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("transport marker bar", "marker bar"));
914 transport_marker_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
916 cd_marker_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag bar rect"));
917 cd_marker_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag bar rect"));
919 range_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag bar rect"));
920 range_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag bar rect"));
922 transport_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->color ("transport drag rect"));
923 transport_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->color ("transport drag rect"));
925 transport_loop_range_rect->set_fill_color (ARDOUR_UI::config()->color_mod ("transport loop rect", "loop rectangle"));
926 transport_loop_range_rect->set_outline_color (ARDOUR_UI::config()->color ("transport loop rect"));
928 transport_punch_range_rect->set_fill_color (ARDOUR_UI::config()->color ("transport punch rect"));
929 transport_punch_range_rect->set_outline_color (ARDOUR_UI::config()->color ("transport punch rect"));
931 transport_punchin_line->set_outline_color (ARDOUR_UI::config()->color ("punch line"));
932 transport_punchout_line->set_outline_color (ARDOUR_UI::config()->color ("punch line"));
934 rubberband_rect->set_outline_color (ARDOUR_UI::config()->color ("rubber band rect"));
935 rubberband_rect->set_fill_color (ARDOUR_UI::config()->color_mod ("rubber band rect", "selection rect"));
937 location_marker_color = ARDOUR_UI::config()->color ("location marker");
938 location_range_color = ARDOUR_UI::config()->color ("location range");
939 location_cd_marker_color = ARDOUR_UI::config()->color ("location cd marker");
940 location_loop_color = ARDOUR_UI::config()->color ("location loop");
941 location_punch_color = ARDOUR_UI::config()->color ("location punch");
943 refresh_location_display ();
945 /* redraw the whole thing */
946 _track_canvas->set_background_color (ARDOUR_UI::config()->color ("arrange base"));
947 _track_canvas->queue_draw ();
950 redisplay_tempo (true);
953 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
958 Editor::horizontal_position () const
960 return sample_to_pixel (leftmost_frame);
964 Editor::track_canvas_key_press (GdkEventKey*)
970 Editor::track_canvas_key_release (GdkEventKey*)
976 Editor::clamp_verbose_cursor_x (double x)
981 x = min (_visible_canvas_width - 200.0, x);
987 Editor::clamp_verbose_cursor_y (double y)
990 y = min (_visible_canvas_height - 50, y);
994 ArdourCanvas::GtkCanvasViewport*
995 Editor::get_track_canvas() const
997 return _track_canvas_viewport;
1001 Editor::get_canvas_cursor () const
1003 /* The top of the cursor stack is always the currently visible cursor. */
1004 return _cursor_stack.back();
1008 Editor::set_canvas_cursor (Gdk::Cursor* cursor)
1010 Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
1012 if (win && cursor) {
1013 win->set_cursor (*cursor);
1018 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
1021 _cursor_stack.push_back (cursor);
1022 set_canvas_cursor (cursor);
1024 return _cursor_stack.size() - 1;
1028 Editor::pop_canvas_cursor ()
1031 if (_cursor_stack.size() <= 1) {
1032 PBD::error << "attempt to pop default cursor" << endmsg;
1036 _cursor_stack.pop_back();
1037 if (_cursor_stack.back()) {
1038 /* Popped to an existing cursor, we're done. Otherwise, the
1039 context that created this cursor has been destroyed, so we need
1040 to skip to the next down the stack. */
1041 set_canvas_cursor (_cursor_stack.back());
1048 Editor::which_grabber_cursor () const
1050 Gdk::Cursor* c = _cursors->grabber;
1052 switch (_edit_point) {
1054 c = _cursors->grabber_edit_point;
1057 boost::shared_ptr<Movable> m = _movable.lock();
1058 if (m && m->locked()) {
1059 c = _cursors->speaker;
1068 Editor::which_trim_cursor (bool left) const
1070 if (!entered_regionview) {
1074 Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
1078 if (ct & Trimmable::FrontTrimEarlier) {
1079 return _cursors->left_side_trim;
1081 return _cursors->left_side_trim_right_only;
1084 if (ct & Trimmable::EndTrimLater) {
1085 return _cursors->right_side_trim;
1087 return _cursors->right_side_trim_left_only;
1093 Editor::which_mode_cursor () const
1095 Gdk::Cursor* mode_cursor = 0;
1097 switch (mouse_mode) {
1099 mode_cursor = _cursors->selector;
1103 mode_cursor = _cursors->scissors;
1108 /* don't use mode cursor, pick a grabber cursor based on the item */
1112 mode_cursor = _cursors->midi_pencil;
1116 mode_cursor = _cursors->time_fx; // just use playhead
1120 mode_cursor = _cursors->speaker;
1124 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
1125 if (get_smart_mode()) {
1128 get_pointer_position (x, y);
1130 if (x >= 0 && y >= 0) {
1132 vector<ArdourCanvas::Item const *> items;
1134 /* Note how we choose a specific scroll group to get
1135 * items from. This could be problematic.
1138 hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
1140 // first item will be the upper most
1142 if (!items.empty()) {
1143 const ArdourCanvas::Item* i = items.front();
1145 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
1146 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
1147 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
1148 mode_cursor = _cursors->up_down;
1159 Editor::which_track_cursor () const
1161 Gdk::Cursor* cursor = 0;
1163 switch (_join_object_range_state) {
1164 case JOIN_OBJECT_RANGE_NONE:
1165 case JOIN_OBJECT_RANGE_OBJECT:
1166 cursor = which_grabber_cursor ();
1168 case JOIN_OBJECT_RANGE_RANGE:
1169 cursor = _cursors->selector;
1177 Editor::which_canvas_cursor(ItemType type) const
1179 Gdk::Cursor* cursor = which_mode_cursor ();
1181 if ((mouse_mode == MouseObject || get_smart_mode ()) ||
1182 mouse_mode == MouseContent) {
1184 /* find correct cursor to use in object/smart mode */
1188 /* We don't choose a cursor for these items on top of a region view,
1189 because this would push a new context on the enter stack which
1190 means switching the region context for things like smart mode
1191 won't actualy change the cursor. */
1192 // case RegionViewNameHighlight:
1193 // case RegionViewName:
1196 case AutomationTrackItem:
1197 cursor = which_track_cursor ();
1199 case PlayheadCursorItem:
1200 switch (_edit_point) {
1202 cursor = _cursors->grabber_edit_point;
1205 cursor = _cursors->grabber;
1210 cursor = _cursors->selector;
1212 case ControlPointItem:
1213 cursor = _cursors->fader;
1216 cursor = which_track_cursor ();
1218 case AutomationLineItem:
1219 cursor = _cursors->cross_hair;
1221 case StartSelectionTrimItem:
1222 cursor = _cursors->left_side_trim;
1224 case EndSelectionTrimItem:
1225 cursor = _cursors->right_side_trim;
1228 cursor = _cursors->fade_in;
1230 case FadeInHandleItem:
1231 cursor = _cursors->fade_in;
1233 case FadeInTrimHandleItem:
1234 cursor = _cursors->fade_in;
1237 cursor = _cursors->fade_out;
1239 case FadeOutHandleItem:
1240 cursor = _cursors->fade_out;
1242 case FadeOutTrimHandleItem:
1243 cursor = _cursors->fade_out;
1245 case FeatureLineItem:
1246 cursor = _cursors->cross_hair;
1248 case LeftFrameHandle:
1249 if ( effective_mouse_mode() == MouseObject ) // (smart mode): if the user is in the top half, override the trim cursor, since they are in the range zone
1250 cursor = which_trim_cursor (true); //alternatively, one could argue that we _should_ allow trims here, and disallow range selection
1252 case RightFrameHandle:
1253 if ( effective_mouse_mode() == MouseObject ) //see above
1254 cursor = which_trim_cursor (false);
1256 case StartCrossFadeItem:
1257 cursor = _cursors->fade_in;
1259 case EndCrossFadeItem:
1260 cursor = _cursors->fade_out;
1262 case CrossfadeViewItem:
1263 cursor = _cursors->cross_hair;
1266 cursor = _cursors->grabber_note;
1271 } else if (mouse_mode == MouseDraw) {
1273 /* ControlPointItem is not really specific to region gain mode
1274 but it is the same cursor so don't worry about this for now.
1275 The result is that we'll see the fader cursor if we enter
1276 non-region-gain-line control points while in MouseDraw
1277 mode, even though we can't edit them in this mode.
1282 case ControlPointItem:
1283 cursor = _cursors->fader;
1286 cursor = _cursors->grabber_note;
1293 /* These items use the timebar cursor at all times */
1294 case TimecodeRulerItem:
1295 case MinsecRulerItem:
1297 case SamplesRulerItem:
1298 cursor = _cursors->timebar;
1301 /* These items use the grabber cursor at all times */
1302 case MeterMarkerItem:
1303 case TempoMarkerItem:
1308 case RangeMarkerBarItem:
1309 case CdMarkerBarItem:
1311 case TransportMarkerBarItem:
1313 cursor = which_grabber_cursor();
1324 Editor::choose_canvas_cursor_on_entry (ItemType type)
1326 if (_drags->active()) {
1330 Gdk::Cursor* cursor = which_canvas_cursor(type);
1333 // Push a new enter context
1334 const EnterContext ctx = { type, CursorContext::create(*this, cursor) };
1335 _enter_stack.push_back(ctx);
1340 Editor::update_all_enter_cursors ()
1342 for (std::vector<EnterContext>::iterator i = _enter_stack.begin(); i != _enter_stack.end(); ++i) {
1343 i->cursor_ctx->change(which_canvas_cursor(i->item_type));
1348 Editor::trackviews_height() const
1350 if (!_trackview_group) {
1354 return _visible_canvas_height - _trackview_group->canvas_origin().y;