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;
59 using namespace ARDOUR_UI_UTILS;
63 using namespace Gtkmm2ext;
64 using namespace Editing;
67 Editor::initialize_canvas ()
69 _track_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, vertical_adjustment);
70 _track_canvas = _track_canvas_viewport->canvas ();
72 /* scroll group for items that should not automatically scroll
73 * (e.g verbose cursor). It shares the canvas coordinate space.
75 no_scroll_group = new ArdourCanvas::Container (_track_canvas->root());
77 ArdourCanvas::ScrollGroup* hsg;
78 ArdourCanvas::ScrollGroup* hg;
79 ArdourCanvas::ScrollGroup* vg;
81 hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_track_canvas->root(),
82 ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically|
83 ArdourCanvas::ScrollGroup::ScrollsHorizontally));
84 CANVAS_DEBUG_NAME (hv_scroll_group, "canvas hv scroll");
85 _track_canvas->add_scroller (*hsg);
87 v_scroll_group = vg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsVertically);
88 CANVAS_DEBUG_NAME (v_scroll_group, "canvas v scroll");
89 _track_canvas->add_scroller (*vg);
91 h_scroll_group = hg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
92 CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll");
93 _track_canvas->add_scroller (*hg);
95 _verbose_cursor = new VerboseCursor (this);
97 /* on the bottom, an image */
99 if (Profile->get_sae()) {
100 Image img (::get_icon (X_("saelogo")));
101 // logo_item = new ArdourCanvas::Pixbuf (_track_canvas->root(), 0.0, 0.0, img.get_pixbuf());
102 // logo_item->property_height_in_pixels() = true;
103 // logo_item->property_width_in_pixels() = true;
104 // logo_item->property_height_set() = true;
105 // logo_item->property_width_set() = true;
106 // logo_item->show ();
109 /*a group to hold global rects like punch/loop indicators */
110 global_rect_group = new ArdourCanvas::Container (hv_scroll_group);
111 CANVAS_DEBUG_NAME (global_rect_group, "global rect group");
113 transport_loop_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
114 CANVAS_DEBUG_NAME (transport_loop_range_rect, "loop rect");
115 transport_loop_range_rect->hide();
117 transport_punch_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
118 CANVAS_DEBUG_NAME (transport_punch_range_rect, "punch rect");
119 transport_punch_range_rect->hide();
121 /*a group to hold time (measure) lines */
122 time_line_group = new ArdourCanvas::Container (hv_scroll_group);
123 CANVAS_DEBUG_NAME (time_line_group, "time line group");
125 _trackview_group = new ArdourCanvas::Container (hv_scroll_group);
126 CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
128 // used as rubberband rect
129 rubberband_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
130 rubberband_rect->hide();
132 /* a group to hold stuff while it gets dragged around. Must be the
133 * uppermost (last) group with hv_scroll_group as a parent
135 _drag_motion_group = new ArdourCanvas::Container (hv_scroll_group);
136 CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion");
138 /* TIME BAR CANVAS */
140 _time_markers_group = new ArdourCanvas::Container (h_scroll_group);
141 CANVAS_DEBUG_NAME (_time_markers_group, "time bars");
143 cd_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, 0.0));
144 CANVAS_DEBUG_NAME (cd_marker_group, "cd marker group");
145 /* the vide is temporarily placed a the same location as the
146 cd_marker_group, but is moved later.
148 videotl_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0));
149 CANVAS_DEBUG_NAME (videotl_group, "videotl group");
150 marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, timebar_height + 1.0));
151 CANVAS_DEBUG_NAME (marker_group, "marker group");
152 transport_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 2.0) + 1.0));
153 CANVAS_DEBUG_NAME (transport_marker_group, "transport marker group");
154 range_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 3.0) + 1.0));
155 CANVAS_DEBUG_NAME (range_marker_group, "range marker group");
156 tempo_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 4.0) + 1.0));
157 CANVAS_DEBUG_NAME (tempo_group, "tempo group");
158 meter_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0));
159 CANVAS_DEBUG_NAME (meter_group, "meter group");
161 meter_bar = new ArdourCanvas::Rectangle (meter_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
162 CANVAS_DEBUG_NAME (meter_bar, "meter Bar");
163 meter_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
165 tempo_bar = new ArdourCanvas::Rectangle (tempo_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
166 CANVAS_DEBUG_NAME (tempo_bar, "Tempo Bar");
167 tempo_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
169 range_marker_bar = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
170 CANVAS_DEBUG_NAME (range_marker_bar, "Range Marker Bar");
171 range_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
173 transport_marker_bar = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
174 CANVAS_DEBUG_NAME (transport_marker_bar, "transport Marker Bar");
175 transport_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
177 marker_bar = new ArdourCanvas::Rectangle (marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
178 CANVAS_DEBUG_NAME (marker_bar, "Marker Bar");
179 marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
181 cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
182 CANVAS_DEBUG_NAME (cd_marker_bar, "CD Marker Bar");
183 cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
185 ARDOUR_UI::instance()->video_timeline = new VideoTimeLine(this, videotl_group, (timebar_height * videotl_bar_height));
187 cd_marker_bar_drag_rect = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
188 CANVAS_DEBUG_NAME (cd_marker_bar_drag_rect, "cd marker drag");
189 cd_marker_bar_drag_rect->set_outline (false);
190 cd_marker_bar_drag_rect->hide ();
192 range_bar_drag_rect = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
193 CANVAS_DEBUG_NAME (range_bar_drag_rect, "range drag");
194 range_bar_drag_rect->set_outline (false);
195 range_bar_drag_rect->hide ();
197 transport_bar_drag_rect = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
198 CANVAS_DEBUG_NAME (transport_bar_drag_rect, "transport drag");
199 transport_bar_drag_rect->set_outline (false);
200 transport_bar_drag_rect->hide ();
202 transport_punchin_line = new ArdourCanvas::Line (hv_scroll_group);
203 transport_punchin_line->set_x0 (0);
204 transport_punchin_line->set_y0 (0);
205 transport_punchin_line->set_x1 (0);
206 transport_punchin_line->set_y1 (ArdourCanvas::COORD_MAX);
207 transport_punchin_line->hide ();
209 transport_punchout_line = new ArdourCanvas::Line (hv_scroll_group);
210 transport_punchout_line->set_x0 (0);
211 transport_punchout_line->set_y0 (0);
212 transport_punchout_line->set_x1 (0);
213 transport_punchout_line->set_y1 (ArdourCanvas::COORD_MAX);
214 transport_punchout_line->hide();
216 tempo_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_tempo_bar_event), tempo_bar));
217 meter_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_meter_bar_event), meter_bar));
218 marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_marker_bar_event), marker_bar));
219 cd_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_cd_marker_bar_event), cd_marker_bar));
220 videotl_group->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_videotl_bar_event), videotl_group));
221 range_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_range_marker_bar_event), range_marker_bar));
222 transport_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_transport_marker_bar_event), transport_marker_bar));
224 playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event);
227 logo_item->lower_to_bottom ();
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 ()
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 (current_canvas_cursor) {
369 set_canvas_cursor (current_canvas_cursor);
374 /** This is called when something is dropped onto the track canvas */
376 Editor::track_canvas_drag_data_received (const RefPtr<Gdk::DragContext>& context,
378 const SelectionData& data,
379 guint info, guint time)
381 if (data.get_target() == "regions") {
382 drop_regions (context, x, y, data, info, time);
384 drop_paths (context, x, y, data, info, time);
389 Editor::idle_drop_paths (vector<string> paths, framepos_t frame, double ypos, bool copy)
391 drop_paths_part_two (paths, frame, ypos, copy);
396 Editor::drop_paths_part_two (const vector<string>& paths, framepos_t frame, double ypos, bool copy)
398 RouteTimeAxisView* tv;
400 /* MIDI files must always be imported, because we consider them
401 * writable. So split paths into two vectors, and follow the import
402 * path on the MIDI part.
405 vector<string> midi_paths;
406 vector<string> audio_paths;
408 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
409 if (SMFSource::safe_midi_file_extension (*i)) {
410 midi_paths.push_back (*i);
412 audio_paths.push_back (*i);
417 std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (ypos, false);
418 if (tvp.first == 0) {
420 /* drop onto canvas background: create new tracks */
424 do_import (midi_paths, Editing::ImportDistinctFiles, ImportAsTrack, SrcBest, frame);
426 if (Profile->get_sae() || Config->get_only_copy_imported_files() || copy) {
427 do_import (audio_paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack, SrcBest, frame);
429 do_embed (audio_paths, Editing::ImportDistinctFiles, ImportAsTrack, frame);
432 } else if ((tv = dynamic_cast<RouteTimeAxisView*> (tvp.first)) != 0) {
434 /* check that its a track, not a bus */
437 /* select the track, then embed/import */
440 do_import (midi_paths, Editing::ImportSerializeFiles, ImportToTrack, SrcBest, frame);
442 if (Profile->get_sae() || Config->get_only_copy_imported_files() || copy) {
443 do_import (audio_paths, Editing::ImportSerializeFiles, Editing::ImportToTrack, SrcBest, frame);
445 do_embed (audio_paths, Editing::ImportSerializeFiles, ImportToTrack, frame);
452 Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
454 const SelectionData& data,
455 guint info, guint time)
457 vector<string> paths;
462 if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
464 /* D-n-D coordinates are window-relative, so convert to canvas coordinates
467 ev.type = GDK_BUTTON_RELEASE;
471 frame = window_event_sample (&ev, 0, &cy);
475 bool copy = ((context->get_actions() & (Gdk::ACTION_COPY | Gdk::ACTION_LINK | Gdk::ACTION_MOVE)) == Gdk::ACTION_COPY);
477 /* We are not allowed to call recursive main event loops from within
478 the main event loop with GTK/Quartz. Since import/embed wants
479 to push up a progress dialog, defer all this till we go idle.
481 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun (*this, &Editor::idle_drop_paths), paths, frame, cy, copy));
483 drop_paths_part_two (paths, frame, cy, copy);
487 context->drag_finish (true, false, time);
490 /** @param allow_horiz true to allow horizontal autoscroll, otherwise false.
492 * @param allow_vert true to allow vertical autoscroll, otherwise false.
496 Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
498 if (!Config->get_autoscroll_editor () || autoscroll_active ()) {
502 /* define a rectangular boundary for scrolling. If the mouse moves
503 * outside of this area and/or continue to be outside of this area,
504 * then we will continuously auto-scroll the canvas in the appropriate
507 * the boundary is defined in coordinates relative to the toplevel
508 * window since that is what we're going to call ::get_pointer() on
509 * during autoscrolling to determine if we're still outside the
513 ArdourCanvas::Rect scrolling_boundary;
514 Gtk::Allocation alloc;
517 alloc = controls_layout.get_allocation ();
519 alloc = _track_canvas_viewport->get_allocation ();
521 /* reduce height by the height of the timebars, which happens
522 to correspond to the position of the hv_scroll_group.
525 alloc.set_height (alloc.get_height() - hv_scroll_group->position().y);
526 alloc.set_y (alloc.get_y() + hv_scroll_group->position().y);
528 /* now reduce it again so that we start autoscrolling before we
529 * move off the top or bottom of the canvas
532 alloc.set_height (alloc.get_height() - 20);
533 alloc.set_y (alloc.get_y() + 10);
535 /* the effective width of the autoscroll boundary so
536 that we start scrolling before we hit the edge.
538 this helps when the window is slammed up against the
539 right edge of the screen, making it hard to scroll
543 if (alloc.get_width() > 20) {
544 alloc.set_width (alloc.get_width() - 20);
545 alloc.set_x (alloc.get_x() + 10);
550 scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), alloc.get_x() + alloc.get_width(), alloc.get_y() + alloc.get_height());
553 Gdk::ModifierType mask;
555 get_window()->get_pointer (x, y, mask);
557 if ((allow_horiz && ((x < scrolling_boundary.x0 && leftmost_frame > 0) || x >= scrolling_boundary.x1)) ||
558 (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) {
559 start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary);
564 Editor::autoscroll_active () const
566 return autoscroll_connection.connected ();
570 Editor::autoscroll_canvas ()
573 Gdk::ModifierType mask;
574 frameoffset_t dx = 0;
575 bool no_stop = false;
577 get_window()->get_pointer (x, y, mask);
580 bool vertical_motion = false;
582 if (autoscroll_horizontal_allowed) {
584 framepos_t new_frame = leftmost_frame;
588 if (x > autoscroll_boundary.x1) {
590 /* bring it back into view */
591 dx = x - autoscroll_boundary.x1;
592 dx += 10 + (2 * (autoscroll_cnt/2));
594 dx = pixel_to_sample (dx);
596 if (leftmost_frame < max_framepos - dx) {
597 new_frame = leftmost_frame + dx;
599 new_frame = max_framepos;
604 } else if (x < autoscroll_boundary.x0) {
606 dx = autoscroll_boundary.x0 - x;
607 dx += 10 + (2 * (autoscroll_cnt/2));
609 dx = pixel_to_sample (dx);
611 if (leftmost_frame >= dx) {
612 new_frame = leftmost_frame - dx;
620 if (new_frame != leftmost_frame) {
621 vc.time_origin = new_frame;
622 vc.add (VisualChange::TimeOrigin);
626 if (autoscroll_vertical_allowed) {
628 // const double vertical_pos = vertical_adjustment.get_value();
629 const int speed_factor = 10;
633 if (y < autoscroll_boundary.y0) {
635 /* scroll to make higher tracks visible */
637 if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
638 scroll_up_one_track ();
639 vertical_motion = true;
642 } else if (y > autoscroll_boundary.y1) {
644 if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
645 scroll_down_one_track ();
646 vertical_motion = true;
653 if (vc.pending || vertical_motion) {
655 /* change horizontal first */
661 /* now send a motion event to notify anyone who cares
662 that we have moved to a new location (because we scrolled)
667 ev.type = GDK_MOTION_NOTIFY;
668 ev.state = Gdk::BUTTON1_MASK;
670 /* the motion handler expects events in canvas coordinate space */
672 /* we asked for the mouse position above (::get_pointer()) via
673 * our own top level window (we being the Editor). Convert into
674 * coordinates within the canvas window.
680 translate_coordinates (*_track_canvas, x, y, cx, cy);
682 /* clamp x and y to remain within the autoscroll boundary,
683 * which is defined in window coordinates
686 x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1);
687 y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1);
689 /* now convert from Editor window coordinates to canvas
693 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
697 motion_handler (0, (GdkEvent*) &ev, true);
699 } else if (no_stop) {
701 /* not changing visual state but pointer is outside the scrolling boundary
702 * so we still need to deliver a fake motion event
707 ev.type = GDK_MOTION_NOTIFY;
708 ev.state = Gdk::BUTTON1_MASK;
710 /* the motion handler expects events in canvas coordinate space */
712 /* first convert from Editor window coordinates to canvas
719 /* clamp x and y to remain within the visible area. except
720 * .. if horizontal scrolling is allowed, always allow us to
724 if (autoscroll_horizontal_allowed) {
725 x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1);
727 x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
729 y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
731 translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
733 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
737 motion_handler (0, (GdkEvent*) &ev, true);
740 stop_canvas_autoscroll ();
746 return true; /* call me again */
750 Editor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary)
756 stop_canvas_autoscroll ();
759 autoscroll_horizontal_allowed = allow_horiz;
760 autoscroll_vertical_allowed = allow_vert;
761 autoscroll_boundary = boundary;
763 /* do the first scroll right now
766 autoscroll_canvas ();
768 /* scroll again at very very roughly 30FPS */
770 autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::autoscroll_canvas), 30);
774 Editor::stop_canvas_autoscroll ()
776 autoscroll_connection.disconnect ();
780 Editor::left_track_canvas (GdkEventCrossing */*ev*/)
783 within_track_canvas = false;
784 set_entered_track (0);
785 set_entered_regionview (0);
786 reset_canvas_action_sensitivity (false);
791 Editor::entered_track_canvas (GdkEventCrossing */*ev*/)
793 within_track_canvas = true;
794 reset_canvas_action_sensitivity (true);
799 Editor::ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top)
801 if (track.hidden()) {
805 /* compute visible area of trackview group, as offsets from top of
809 double const current_view_min_y = vertical_adjustment.get_value();
810 double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size();
812 double const track_min_y = track.y_position ();
813 double const track_max_y = track.y_position () + track.effective_height ();
816 (track_min_y >= current_view_min_y &&
817 track_max_y < current_view_max_y)) {
818 /* already visible, and caller did not ask to place it at the
819 * top of the track canvas
827 new_value = track_min_y;
829 if (track_min_y < current_view_min_y) {
830 // Track is above the current view
831 new_value = track_min_y;
832 } else if (track_max_y > current_view_max_y) {
833 // Track is below the current view
834 new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size();
836 new_value = track_min_y;
840 vertical_adjustment.set_value(new_value);
843 /** Called when the main vertical_adjustment has changed */
845 Editor::tie_vertical_scrolling ()
847 if (pending_visual_change.idle_handler_id < 0) {
848 _summary->set_overlays_dirty ();
853 Editor::set_horizontal_position (double p)
855 horizontal_adjustment.set_value (p);
857 leftmost_frame = (framepos_t) floor (p * samples_per_pixel);
859 update_fixed_rulers ();
860 redisplay_tempo (true);
862 if (pending_visual_change.idle_handler_id < 0) {
863 _summary->set_overlays_dirty ();
866 update_video_timeline();
870 Editor::color_handler()
872 ArdourCanvas::Color base = ARDOUR_UI::config()->get_RulerBase();
873 ArdourCanvas::Color text = ARDOUR_UI::config()->get_RulerText();
874 timecode_ruler->set_fill_color (base);
875 timecode_ruler->set_outline_color (text);
876 minsec_ruler->set_fill_color (base);
877 minsec_ruler->set_outline_color (text);
878 samples_ruler->set_fill_color (base);
879 samples_ruler->set_outline_color (text);
880 bbt_ruler->set_fill_color (base);
881 bbt_ruler->set_outline_color (text);
883 playhead_cursor->set_color (ARDOUR_UI::config()->get_PlayHead());
885 meter_bar->set_fill_color (ARDOUR_UI::config()->get_MeterBar());
886 meter_bar->set_outline_color (ARDOUR_UI::config()->get_MarkerBarSeparator());
888 tempo_bar->set_fill_color (ARDOUR_UI::config()->get_TempoBar());
889 tempo_bar->set_outline_color (ARDOUR_UI::config()->get_MarkerBarSeparator());
891 marker_bar->set_fill_color (ARDOUR_UI::config()->get_MarkerBar());
892 marker_bar->set_outline_color (ARDOUR_UI::config()->get_MarkerBarSeparator());
894 cd_marker_bar->set_fill_color (ARDOUR_UI::config()->get_CDMarkerBar());
895 cd_marker_bar->set_outline_color (ARDOUR_UI::config()->get_MarkerBarSeparator());
897 range_marker_bar->set_fill_color (ARDOUR_UI::config()->get_RangeMarkerBar());
898 range_marker_bar->set_outline_color (ARDOUR_UI::config()->get_MarkerBarSeparator());
900 transport_marker_bar->set_fill_color (ARDOUR_UI::config()->get_TransportMarkerBar());
901 transport_marker_bar->set_outline_color (ARDOUR_UI::config()->get_MarkerBarSeparator());
903 cd_marker_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_RangeDragBarRect());
904 cd_marker_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_RangeDragBarRect());
906 range_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_RangeDragBarRect());
907 range_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_RangeDragBarRect());
909 transport_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_TransportDragRect());
910 transport_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_TransportDragRect());
912 transport_loop_range_rect->set_fill_color (ARDOUR_UI::config()->get_TransportLoopRect());
913 transport_loop_range_rect->set_outline_color (ARDOUR_UI::config()->get_TransportLoopRect());
915 transport_punch_range_rect->set_fill_color (ARDOUR_UI::config()->get_TransportPunchRect());
916 transport_punch_range_rect->set_outline_color (ARDOUR_UI::config()->get_TransportPunchRect());
918 transport_punchin_line->set_outline_color (ARDOUR_UI::config()->get_PunchLine());
919 transport_punchout_line->set_outline_color (ARDOUR_UI::config()->get_PunchLine());
921 rubberband_rect->set_outline_color (ARDOUR_UI::config()->get_RubberBandRect());
922 rubberband_rect->set_fill_color ((guint32) ARDOUR_UI::config()->get_RubberBandRect());
924 location_marker_color = ARDOUR_UI::config()->get_LocationMarker();
925 location_range_color = ARDOUR_UI::config()->get_LocationRange();
926 location_cd_marker_color = ARDOUR_UI::config()->get_LocationCDMarker();
927 location_loop_color = ARDOUR_UI::config()->get_LocationLoop();
928 location_punch_color = ARDOUR_UI::config()->get_LocationPunch();
930 refresh_location_display ();
932 redisplay_tempo (true);
935 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
940 Editor::horizontal_position () const
942 return sample_to_pixel (leftmost_frame);
946 Editor::track_canvas_key_press (GdkEventKey*)
952 Editor::track_canvas_key_release (GdkEventKey*)
958 Editor::clamp_verbose_cursor_x (double x)
963 x = min (_visible_canvas_width - 200.0, x);
969 Editor::clamp_verbose_cursor_y (double y)
972 y = min (_visible_canvas_height - 50, y);
976 ArdourCanvas::GtkCanvasViewport*
977 Editor::get_track_canvas() const
979 return _track_canvas_viewport;
983 Editor::set_canvas_cursor (Gdk::Cursor* cursor, bool save)
986 current_canvas_cursor = cursor;
989 Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
992 win->set_cursor (*cursor);
997 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
1000 _cursor_stack.push (cursor);
1001 set_canvas_cursor (cursor, false);
1006 Editor::pop_canvas_cursor ()
1008 if (!_cursor_stack.empty()) {
1009 Gdk::Cursor* cursor = _cursor_stack.top ();
1010 _cursor_stack.pop ();
1011 set_canvas_cursor (cursor, false);
1016 Editor::which_grabber_cursor () const
1018 Gdk::Cursor* c = _cursors->grabber;
1020 if (_internal_editing) {
1021 switch (mouse_mode) {
1023 c = _cursors->midi_pencil;
1027 c = _cursors->grabber_note;
1031 c = _cursors->midi_resize;
1035 c = _cursors->grabber_note;
1044 switch (_edit_point) {
1046 c = _cursors->grabber_edit_point;
1049 boost::shared_ptr<Movable> m = _movable.lock();
1050 if (m && m->locked()) {
1051 c = _cursors->speaker;
1061 Editor::which_trim_cursor (bool left) const
1063 if (!entered_regionview) {
1067 Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
1071 if (ct & Trimmable::FrontTrimEarlier) {
1072 return _cursors->left_side_trim;
1074 return _cursors->left_side_trim_right_only;
1077 if (ct & Trimmable::EndTrimLater) {
1078 return _cursors->right_side_trim;
1080 return _cursors->right_side_trim_left_only;
1086 Editor::which_mode_cursor () const
1088 Gdk::Cursor* mode_cursor = 0;
1090 switch (mouse_mode) {
1092 mode_cursor = _cursors->selector;
1093 if (_internal_editing) {
1094 mode_cursor = which_grabber_cursor();
1099 mode_cursor = _cursors->scissors;
1103 /* don't use mode cursor, pick a grabber cursor based on the item */
1107 mode_cursor = _cursors->midi_pencil;
1111 mode_cursor = _cursors->cross_hair;
1115 mode_cursor = _cursors->time_fx; // just use playhead
1119 mode_cursor = _cursors->speaker;
1123 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
1124 if (!_internal_editing && get_smart_mode() ) {
1127 get_pointer_position (x, y);
1129 if (x >= 0 && y >= 0) {
1131 vector<ArdourCanvas::Item const *> items;
1133 /* Note how we choose a specific scroll group to get
1134 * items from. This could be problematic.
1137 hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
1139 // first item will be the upper most
1141 if (!items.empty()) {
1142 const ArdourCanvas::Item* i = items.front();
1144 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
1145 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
1146 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
1147 mode_cursor = _cursors->up_down;
1158 Editor::which_track_cursor () const
1160 Gdk::Cursor* cursor = 0;
1162 assert (mouse_mode == MouseObject || get_smart_mode());
1164 if (!_internal_editing) {
1165 switch (_join_object_range_state) {
1166 case JOIN_OBJECT_RANGE_NONE:
1167 case JOIN_OBJECT_RANGE_OBJECT:
1168 cursor = which_grabber_cursor ();
1170 case JOIN_OBJECT_RANGE_RANGE:
1171 cursor = _cursors->selector;
1180 Editor::reset_canvas_cursor ()
1182 if (!is_drawable()) {
1186 Gdk::Cursor* cursor = which_mode_cursor ();
1189 cursor = which_grabber_cursor ();
1193 set_canvas_cursor (cursor);
1201 Editor::choose_canvas_cursor_on_entry (GdkEventCrossing* /*event*/, ItemType type)
1203 Gdk::Cursor* cursor = 0;
1205 if (_drags->active()) {
1209 cursor = which_mode_cursor ();
1211 if (mouse_mode == MouseObject || get_smart_mode ()) {
1213 /* find correct cursor to use in object/smart mode */
1217 case RegionViewNameHighlight:
1218 case RegionViewName:
1221 case AutomationTrackItem:
1222 cursor = which_track_cursor ();
1224 case PlayheadCursorItem:
1225 switch (_edit_point) {
1227 cursor = _cursors->grabber_edit_point;
1230 cursor = _cursors->grabber;
1235 cursor = _cursors->selector;
1237 case ControlPointItem:
1238 cursor = _cursors->fader;
1241 cursor = which_track_cursor ();
1243 case AutomationLineItem:
1244 cursor = _cursors->cross_hair;
1246 case StartSelectionTrimItem:
1247 cursor = _cursors->left_side_trim;
1249 case EndSelectionTrimItem:
1250 cursor = _cursors->right_side_trim;
1253 cursor = _cursors->fade_in;
1255 case FadeInHandleItem:
1256 cursor = _cursors->fade_in;
1258 case FadeInTrimHandleItem:
1259 cursor = _cursors->fade_in;
1262 cursor = _cursors->fade_out;
1264 case FadeOutHandleItem:
1265 cursor = _cursors->fade_out;
1267 case FadeOutTrimHandleItem:
1268 cursor = _cursors->fade_out;
1271 cursor = which_grabber_cursor();
1273 case FeatureLineItem:
1274 cursor = _cursors->cross_hair;
1276 case LeftFrameHandle:
1277 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
1278 cursor = which_trim_cursor (true); //alternatively, one could argue that we _should_ allow trims here, and disallow range selection
1280 case RightFrameHandle:
1281 if ( effective_mouse_mode() == MouseObject ) //see above
1282 cursor = which_trim_cursor (false);
1284 case StartCrossFadeItem:
1285 cursor = _cursors->fade_in;
1287 case EndCrossFadeItem:
1288 cursor = _cursors->fade_out;
1290 case CrossfadeViewItem:
1291 cursor = _cursors->cross_hair;
1297 } else if (mouse_mode == MouseGain) {
1299 /* ControlPointItem is not really specific to region gain mode
1300 but it is the same cursor so don't worry about this for now.
1301 The result is that we'll see the fader cursor if we enter
1302 non-region-gain-line control points while in MouseGain
1303 mode, even though we can't edit them in this mode.
1308 case ControlPointItem:
1309 cursor = _cursors->fader;
1317 /* These items use the timebar cursor at all times */
1318 case TimecodeRulerItem:
1319 case MinsecRulerItem:
1321 case SamplesRulerItem:
1322 cursor = _cursors->timebar;
1325 /* These items use the grabber cursor at all times */
1326 case MeterMarkerItem:
1327 case TempoMarkerItem:
1332 case RangeMarkerBarItem:
1333 case CdMarkerBarItem:
1335 case TransportMarkerBarItem:
1337 cursor = which_grabber_cursor();
1345 set_canvas_cursor (cursor, false);
1350 Editor::trackviews_height() const
1352 if (!_trackview_group) {
1356 return _visible_canvas_height - _trackview_group->canvas_origin().y;