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 _track_canvas->set_background_color (ARDOUR_UI::config()->get_ArrangeBase());
74 /* scroll group for items that should not automatically scroll
75 * (e.g verbose cursor). It shares the canvas coordinate space.
77 no_scroll_group = new ArdourCanvas::Container (_track_canvas->root());
79 ArdourCanvas::ScrollGroup* hsg;
80 ArdourCanvas::ScrollGroup* hg;
81 ArdourCanvas::ScrollGroup* vg;
83 hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_track_canvas->root(),
84 ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically|
85 ArdourCanvas::ScrollGroup::ScrollsHorizontally));
86 CANVAS_DEBUG_NAME (hv_scroll_group, "canvas hv scroll");
87 _track_canvas->add_scroller (*hsg);
89 v_scroll_group = vg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsVertically);
90 CANVAS_DEBUG_NAME (v_scroll_group, "canvas v scroll");
91 _track_canvas->add_scroller (*vg);
93 h_scroll_group = hg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
94 CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll");
95 _track_canvas->add_scroller (*hg);
97 _verbose_cursor = new VerboseCursor (this);
99 /* on the bottom, an image */
101 if (Profile->get_sae()) {
102 Image img (::get_icon (X_("saelogo")));
103 // logo_item = new ArdourCanvas::Pixbuf (_track_canvas->root(), 0.0, 0.0, img.get_pixbuf());
104 // logo_item->property_height_in_pixels() = true;
105 // logo_item->property_width_in_pixels() = true;
106 // logo_item->property_height_set() = true;
107 // logo_item->property_width_set() = true;
108 // logo_item->show ();
111 /*a group to hold global rects like punch/loop indicators */
112 global_rect_group = new ArdourCanvas::Container (hv_scroll_group);
113 CANVAS_DEBUG_NAME (global_rect_group, "global rect group");
115 transport_loop_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
116 CANVAS_DEBUG_NAME (transport_loop_range_rect, "loop rect");
117 transport_loop_range_rect->hide();
119 transport_punch_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
120 CANVAS_DEBUG_NAME (transport_punch_range_rect, "punch rect");
121 transport_punch_range_rect->hide();
123 /*a group to hold time (measure) lines */
124 time_line_group = new ArdourCanvas::Container (hv_scroll_group);
125 CANVAS_DEBUG_NAME (time_line_group, "time line group");
127 _trackview_group = new ArdourCanvas::Container (hv_scroll_group);
128 CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
130 // used as rubberband rect
131 rubberband_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
132 rubberband_rect->hide();
134 /* a group to hold stuff while it gets dragged around. Must be the
135 * uppermost (last) group with hv_scroll_group as a parent
137 _drag_motion_group = new ArdourCanvas::Container (hv_scroll_group);
138 CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion");
140 /* TIME BAR CANVAS */
142 _time_markers_group = new ArdourCanvas::Container (h_scroll_group);
143 CANVAS_DEBUG_NAME (_time_markers_group, "time bars");
145 cd_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, 0.0));
146 CANVAS_DEBUG_NAME (cd_marker_group, "cd marker group");
147 /* the vide is temporarily placed a the same location as the
148 cd_marker_group, but is moved later.
150 videotl_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0));
151 CANVAS_DEBUG_NAME (videotl_group, "videotl group");
152 marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, timebar_height + 1.0));
153 CANVAS_DEBUG_NAME (marker_group, "marker group");
154 transport_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 2.0) + 1.0));
155 CANVAS_DEBUG_NAME (transport_marker_group, "transport marker group");
156 range_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 3.0) + 1.0));
157 CANVAS_DEBUG_NAME (range_marker_group, "range marker group");
158 tempo_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 4.0) + 1.0));
159 CANVAS_DEBUG_NAME (tempo_group, "tempo group");
160 meter_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0));
161 CANVAS_DEBUG_NAME (meter_group, "meter group");
163 meter_bar = new ArdourCanvas::Rectangle (meter_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
164 CANVAS_DEBUG_NAME (meter_bar, "meter Bar");
165 meter_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
167 tempo_bar = new ArdourCanvas::Rectangle (tempo_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
168 CANVAS_DEBUG_NAME (tempo_bar, "Tempo Bar");
169 tempo_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
171 range_marker_bar = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
172 CANVAS_DEBUG_NAME (range_marker_bar, "Range Marker Bar");
173 range_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
175 transport_marker_bar = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
176 CANVAS_DEBUG_NAME (transport_marker_bar, "transport Marker Bar");
177 transport_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
179 marker_bar = new ArdourCanvas::Rectangle (marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
180 CANVAS_DEBUG_NAME (marker_bar, "Marker Bar");
181 marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
183 cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
184 CANVAS_DEBUG_NAME (cd_marker_bar, "CD Marker Bar");
185 cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
187 ARDOUR_UI::instance()->video_timeline = new VideoTimeLine(this, videotl_group, (timebar_height * videotl_bar_height));
189 cd_marker_bar_drag_rect = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
190 CANVAS_DEBUG_NAME (cd_marker_bar_drag_rect, "cd marker drag");
191 cd_marker_bar_drag_rect->set_outline (false);
192 cd_marker_bar_drag_rect->hide ();
194 range_bar_drag_rect = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
195 CANVAS_DEBUG_NAME (range_bar_drag_rect, "range drag");
196 range_bar_drag_rect->set_outline (false);
197 range_bar_drag_rect->hide ();
199 transport_bar_drag_rect = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
200 CANVAS_DEBUG_NAME (transport_bar_drag_rect, "transport drag");
201 transport_bar_drag_rect->set_outline (false);
202 transport_bar_drag_rect->hide ();
204 transport_punchin_line = new ArdourCanvas::Line (hv_scroll_group);
205 transport_punchin_line->set_x0 (0);
206 transport_punchin_line->set_y0 (0);
207 transport_punchin_line->set_x1 (0);
208 transport_punchin_line->set_y1 (ArdourCanvas::COORD_MAX);
209 transport_punchin_line->hide ();
211 transport_punchout_line = new ArdourCanvas::Line (hv_scroll_group);
212 transport_punchout_line->set_x0 (0);
213 transport_punchout_line->set_y0 (0);
214 transport_punchout_line->set_x1 (0);
215 transport_punchout_line->set_y1 (ArdourCanvas::COORD_MAX);
216 transport_punchout_line->hide();
218 tempo_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_tempo_bar_event), tempo_bar));
219 meter_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_meter_bar_event), meter_bar));
220 marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_marker_bar_event), marker_bar));
221 cd_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_cd_marker_bar_event), cd_marker_bar));
222 videotl_group->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_videotl_bar_event), videotl_group));
223 range_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_range_marker_bar_event), range_marker_bar));
224 transport_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_transport_marker_bar_event), transport_marker_bar));
226 playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event);
229 logo_item->lower_to_bottom ();
233 _canvas_drop_zone = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, 0.0));
234 /* this thing is transparent */
235 _canvas_drop_zone->set_fill (false);
236 _canvas_drop_zone->set_outline (false);
237 _canvas_drop_zone->Event.connect (sigc::mem_fun (*this, &Editor::canvas_drop_zone_event));
239 /* these signals will initially be delivered to the canvas itself, but if they end up remaining unhandled, they are passed to Editor-level
243 _track_canvas->signal_scroll_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_scroll_event), true));
244 _track_canvas->signal_motion_notify_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_motion_notify_event));
245 _track_canvas->signal_button_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_press_event));
246 _track_canvas->signal_button_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_release_event));
247 _track_canvas->signal_drag_motion().connect (sigc::mem_fun (*this, &Editor::track_canvas_drag_motion));
248 _track_canvas->signal_key_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_press));
249 _track_canvas->signal_key_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_release));
251 _track_canvas->set_name ("EditorMainCanvas");
252 _track_canvas->add_events (Gdk::POINTER_MOTION_HINT_MASK | Gdk::SCROLL_MASK | Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
253 _track_canvas->signal_leave_notify_event().connect (sigc::mem_fun(*this, &Editor::left_track_canvas), false);
254 _track_canvas->signal_enter_notify_event().connect (sigc::mem_fun(*this, &Editor::entered_track_canvas), false);
255 _track_canvas->set_flags (CAN_FOCUS);
257 /* set up drag-n-drop */
259 vector<TargetEntry> target_table;
261 // Drag-N-Drop from the region list can generate this target
262 target_table.push_back (TargetEntry ("regions"));
264 target_table.push_back (TargetEntry ("text/plain"));
265 target_table.push_back (TargetEntry ("text/uri-list"));
266 target_table.push_back (TargetEntry ("application/x-rootwin-drop"));
268 _track_canvas->drag_dest_set (target_table);
269 _track_canvas->signal_drag_data_received().connect (sigc::mem_fun(*this, &Editor::track_canvas_drag_data_received));
271 _track_canvas_viewport->signal_size_allocate().connect (sigc::mem_fun(*this, &Editor::track_canvas_viewport_allocate));
273 initialize_rulers ();
275 ColorsChanged.connect (sigc::mem_fun (*this, &Editor::color_handler));
281 Editor::track_canvas_viewport_allocate (Gtk::Allocation alloc)
283 _canvas_viewport_allocation = alloc;
284 track_canvas_viewport_size_allocated ();
288 Editor::track_canvas_viewport_size_allocated ()
290 bool height_changed = _visible_canvas_height != _canvas_viewport_allocation.get_height();
292 _visible_canvas_width = _canvas_viewport_allocation.get_width ();
293 _visible_canvas_height = _canvas_viewport_allocation.get_height ();
295 _canvas_drop_zone->set_y1 (_canvas_drop_zone->y0() + (_visible_canvas_height - 20.0));
299 if (height_changed) {
301 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
302 i->second->canvas_height_set (_visible_canvas_height);
305 vertical_adjustment.set_page_size (_visible_canvas_height);
306 if ((vertical_adjustment.get_value() + _visible_canvas_height) >= vertical_adjustment.get_upper()) {
308 We're increasing the size of the canvas while the bottom is visible.
309 We scroll down to keep in step with the controls layout.
311 vertical_adjustment.set_value (_full_canvas_height - _visible_canvas_height);
314 set_visible_track_count (_visible_track_count);
317 update_fixed_rulers();
318 redisplay_tempo (false);
319 _summary->set_overlays_dirty ();
323 Editor::reset_controls_layout_width ()
325 GtkRequisition req = { 0, 0 };
328 edit_controls_vbox.size_request (req);
331 if (_group_tabs->is_mapped()) {
332 _group_tabs->size_request (req);
336 /* the controls layout has no horizontal scrolling, its visible
337 width is always equal to the total width of its contents.
340 controls_layout.property_width() = w;
341 controls_layout.property_width_request() = w;
345 Editor::reset_controls_layout_height (int32_t h)
347 /* ensure that the rect that represents the "bottom" of the canvas
348 * (the drag-n-drop zone) is, in fact, at the bottom.
351 _canvas_drop_zone->set_position (ArdourCanvas::Duple (0, h));
353 /* track controls layout must span the full height of "h" (all tracks)
354 * plus the bottom rect.
357 h += _canvas_drop_zone->height ();
359 /* set the height of the scrollable area (i.e. the sum of all contained widgets)
360 * for the controls layout. The size request is set elsewhere.
363 controls_layout.property_height() = h;
368 Editor::track_canvas_map_handler (GdkEventAny* /*ev*/)
370 if (current_canvas_cursor) {
371 set_canvas_cursor (current_canvas_cursor);
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() || 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() || 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 (!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 ();
782 Editor::left_track_canvas (GdkEventCrossing */*ev*/)
785 within_track_canvas = false;
786 set_entered_track (0);
787 set_entered_regionview (0);
788 reset_canvas_action_sensitivity (false);
793 Editor::entered_track_canvas (GdkEventCrossing */*ev*/)
795 within_track_canvas = true;
796 reset_canvas_action_sensitivity (true);
801 Editor::ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top)
803 if (track.hidden()) {
807 /* compute visible area of trackview group, as offsets from top of
811 double const current_view_min_y = vertical_adjustment.get_value();
812 double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size();
814 double const track_min_y = track.y_position ();
815 double const track_max_y = track.y_position () + track.effective_height ();
818 (track_min_y >= current_view_min_y &&
819 track_max_y < current_view_max_y)) {
820 /* already visible, and caller did not ask to place it at the
821 * top of the track canvas
829 new_value = track_min_y;
831 if (track_min_y < current_view_min_y) {
832 // Track is above the current view
833 new_value = track_min_y;
834 } else if (track_max_y > current_view_max_y) {
835 // Track is below the current view
836 new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size();
838 new_value = track_min_y;
842 vertical_adjustment.set_value(new_value);
845 /** Called when the main vertical_adjustment has changed */
847 Editor::tie_vertical_scrolling ()
849 if (pending_visual_change.idle_handler_id < 0) {
850 _summary->set_overlays_dirty ();
855 Editor::set_horizontal_position (double p)
857 horizontal_adjustment.set_value (p);
859 leftmost_frame = (framepos_t) floor (p * samples_per_pixel);
861 update_fixed_rulers ();
862 redisplay_tempo (true);
864 if (pending_visual_change.idle_handler_id < 0) {
865 _summary->set_overlays_dirty ();
868 update_video_timeline();
872 Editor::color_handler()
874 ArdourCanvas::Color base = ARDOUR_UI::config()->get_RulerBase();
875 ArdourCanvas::Color text = ARDOUR_UI::config()->get_RulerText();
876 timecode_ruler->set_fill_color (base);
877 timecode_ruler->set_outline_color (text);
878 minsec_ruler->set_fill_color (base);
879 minsec_ruler->set_outline_color (text);
880 samples_ruler->set_fill_color (base);
881 samples_ruler->set_outline_color (text);
882 bbt_ruler->set_fill_color (base);
883 bbt_ruler->set_outline_color (text);
885 playhead_cursor->set_color (ARDOUR_UI::config()->get_PlayHead());
887 meter_bar->set_fill_color (ARDOUR_UI::config()->get_MeterBar());
888 meter_bar->set_outline_color (ARDOUR_UI::config()->get_MarkerBarSeparator());
890 tempo_bar->set_fill_color (ARDOUR_UI::config()->get_TempoBar());
891 tempo_bar->set_outline_color (ARDOUR_UI::config()->get_MarkerBarSeparator());
893 marker_bar->set_fill_color (ARDOUR_UI::config()->get_MarkerBar());
894 marker_bar->set_outline_color (ARDOUR_UI::config()->get_MarkerBarSeparator());
896 cd_marker_bar->set_fill_color (ARDOUR_UI::config()->get_CDMarkerBar());
897 cd_marker_bar->set_outline_color (ARDOUR_UI::config()->get_MarkerBarSeparator());
899 range_marker_bar->set_fill_color (ARDOUR_UI::config()->get_RangeMarkerBar());
900 range_marker_bar->set_outline_color (ARDOUR_UI::config()->get_MarkerBarSeparator());
902 transport_marker_bar->set_fill_color (ARDOUR_UI::config()->get_TransportMarkerBar());
903 transport_marker_bar->set_outline_color (ARDOUR_UI::config()->get_MarkerBarSeparator());
905 cd_marker_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_RangeDragBarRect());
906 cd_marker_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_RangeDragBarRect());
908 range_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_RangeDragBarRect());
909 range_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_RangeDragBarRect());
911 transport_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_TransportDragRect());
912 transport_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_TransportDragRect());
914 transport_loop_range_rect->set_fill_color (ARDOUR_UI::config()->get_TransportLoopRect());
915 transport_loop_range_rect->set_outline_color (ARDOUR_UI::config()->get_TransportLoopRect());
917 transport_punch_range_rect->set_fill_color (ARDOUR_UI::config()->get_TransportPunchRect());
918 transport_punch_range_rect->set_outline_color (ARDOUR_UI::config()->get_TransportPunchRect());
920 transport_punchin_line->set_outline_color (ARDOUR_UI::config()->get_PunchLine());
921 transport_punchout_line->set_outline_color (ARDOUR_UI::config()->get_PunchLine());
923 rubberband_rect->set_outline_color (ARDOUR_UI::config()->get_RubberBandRect());
924 rubberband_rect->set_fill_color ((guint32) ARDOUR_UI::config()->get_RubberBandRect());
926 location_marker_color = ARDOUR_UI::config()->get_LocationMarker();
927 location_range_color = ARDOUR_UI::config()->get_LocationRange();
928 location_cd_marker_color = ARDOUR_UI::config()->get_LocationCDMarker();
929 location_loop_color = ARDOUR_UI::config()->get_LocationLoop();
930 location_punch_color = ARDOUR_UI::config()->get_LocationPunch();
932 refresh_location_display ();
934 /* redraw the whole thing */
935 _track_canvas->queue_draw ();
938 redisplay_tempo (true);
941 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
946 Editor::horizontal_position () const
948 return sample_to_pixel (leftmost_frame);
952 Editor::track_canvas_key_press (GdkEventKey*)
958 Editor::track_canvas_key_release (GdkEventKey*)
964 Editor::clamp_verbose_cursor_x (double x)
969 x = min (_visible_canvas_width - 200.0, x);
975 Editor::clamp_verbose_cursor_y (double y)
978 y = min (_visible_canvas_height - 50, y);
982 ArdourCanvas::GtkCanvasViewport*
983 Editor::get_track_canvas() const
985 return _track_canvas_viewport;
989 Editor::set_canvas_cursor (Gdk::Cursor* cursor, bool save)
992 current_canvas_cursor = cursor;
995 Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
998 win->set_cursor (*cursor);
1003 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
1006 _cursor_stack.push (cursor);
1007 set_canvas_cursor (cursor, false);
1012 Editor::pop_canvas_cursor ()
1014 if (!_cursor_stack.empty()) {
1015 Gdk::Cursor* cursor = _cursor_stack.top ();
1016 _cursor_stack.pop ();
1017 set_canvas_cursor (cursor, false);
1022 Editor::which_grabber_cursor () const
1024 Gdk::Cursor* c = _cursors->grabber;
1026 if (_internal_editing) {
1027 switch (mouse_mode) {
1029 c = _cursors->midi_pencil;
1033 c = _cursors->grabber_note;
1037 c = _cursors->midi_resize;
1041 c = _cursors->grabber_note;
1050 switch (_edit_point) {
1052 c = _cursors->grabber_edit_point;
1055 boost::shared_ptr<Movable> m = _movable.lock();
1056 if (m && m->locked()) {
1057 c = _cursors->speaker;
1067 Editor::which_trim_cursor (bool left) const
1069 if (!entered_regionview) {
1073 Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
1077 if (ct & Trimmable::FrontTrimEarlier) {
1078 return _cursors->left_side_trim;
1080 return _cursors->left_side_trim_right_only;
1083 if (ct & Trimmable::EndTrimLater) {
1084 return _cursors->right_side_trim;
1086 return _cursors->right_side_trim_left_only;
1092 Editor::which_mode_cursor () const
1094 Gdk::Cursor* mode_cursor = 0;
1096 switch (mouse_mode) {
1098 mode_cursor = _cursors->selector;
1099 if (_internal_editing) {
1100 mode_cursor = which_grabber_cursor();
1105 mode_cursor = _cursors->scissors;
1109 /* don't use mode cursor, pick a grabber cursor based on the item */
1113 mode_cursor = _cursors->midi_pencil;
1117 mode_cursor = _cursors->cross_hair;
1121 mode_cursor = _cursors->time_fx; // just use playhead
1125 mode_cursor = _cursors->speaker;
1129 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
1130 if (!_internal_editing && get_smart_mode() ) {
1133 get_pointer_position (x, y);
1135 if (x >= 0 && y >= 0) {
1137 vector<ArdourCanvas::Item const *> items;
1139 /* Note how we choose a specific scroll group to get
1140 * items from. This could be problematic.
1143 hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
1145 // first item will be the upper most
1147 if (!items.empty()) {
1148 const ArdourCanvas::Item* i = items.front();
1150 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
1151 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
1152 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
1153 mode_cursor = _cursors->up_down;
1164 Editor::which_track_cursor () const
1166 Gdk::Cursor* cursor = 0;
1168 assert (mouse_mode == MouseObject || get_smart_mode());
1170 if (!_internal_editing) {
1171 switch (_join_object_range_state) {
1172 case JOIN_OBJECT_RANGE_NONE:
1173 case JOIN_OBJECT_RANGE_OBJECT:
1174 cursor = which_grabber_cursor ();
1176 case JOIN_OBJECT_RANGE_RANGE:
1177 cursor = _cursors->selector;
1186 Editor::reset_canvas_cursor ()
1188 if (!is_drawable()) {
1192 Gdk::Cursor* cursor = which_mode_cursor ();
1195 cursor = which_grabber_cursor ();
1199 set_canvas_cursor (cursor);
1207 Editor::choose_canvas_cursor_on_entry (GdkEventCrossing* /*event*/, ItemType type)
1209 Gdk::Cursor* cursor = 0;
1211 if (_drags->active()) {
1215 cursor = which_mode_cursor ();
1217 if (mouse_mode == MouseObject || get_smart_mode ()) {
1219 /* find correct cursor to use in object/smart mode */
1223 case RegionViewNameHighlight:
1224 case RegionViewName:
1227 case AutomationTrackItem:
1228 cursor = which_track_cursor ();
1230 case PlayheadCursorItem:
1231 switch (_edit_point) {
1233 cursor = _cursors->grabber_edit_point;
1236 cursor = _cursors->grabber;
1241 cursor = _cursors->selector;
1243 case ControlPointItem:
1244 cursor = _cursors->fader;
1247 cursor = which_track_cursor ();
1249 case AutomationLineItem:
1250 cursor = _cursors->cross_hair;
1252 case StartSelectionTrimItem:
1253 cursor = _cursors->left_side_trim;
1255 case EndSelectionTrimItem:
1256 cursor = _cursors->right_side_trim;
1259 cursor = _cursors->fade_in;
1261 case FadeInHandleItem:
1262 cursor = _cursors->fade_in;
1264 case FadeInTrimHandleItem:
1265 cursor = _cursors->fade_in;
1268 cursor = _cursors->fade_out;
1270 case FadeOutHandleItem:
1271 cursor = _cursors->fade_out;
1273 case FadeOutTrimHandleItem:
1274 cursor = _cursors->fade_out;
1277 cursor = which_grabber_cursor();
1279 case FeatureLineItem:
1280 cursor = _cursors->cross_hair;
1282 case LeftFrameHandle:
1283 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
1284 cursor = which_trim_cursor (true); //alternatively, one could argue that we _should_ allow trims here, and disallow range selection
1286 case RightFrameHandle:
1287 if ( effective_mouse_mode() == MouseObject ) //see above
1288 cursor = which_trim_cursor (false);
1290 case StartCrossFadeItem:
1291 cursor = _cursors->fade_in;
1293 case EndCrossFadeItem:
1294 cursor = _cursors->fade_out;
1296 case CrossfadeViewItem:
1297 cursor = _cursors->cross_hair;
1303 } else if (mouse_mode == MouseGain) {
1305 /* ControlPointItem is not really specific to region gain mode
1306 but it is the same cursor so don't worry about this for now.
1307 The result is that we'll see the fader cursor if we enter
1308 non-region-gain-line control points while in MouseGain
1309 mode, even though we can't edit them in this mode.
1314 case ControlPointItem:
1315 cursor = _cursors->fader;
1323 /* These items use the timebar cursor at all times */
1324 case TimecodeRulerItem:
1325 case MinsecRulerItem:
1327 case SamplesRulerItem:
1328 cursor = _cursors->timebar;
1331 /* These items use the grabber cursor at all times */
1332 case MeterMarkerItem:
1333 case TempoMarkerItem:
1338 case RangeMarkerBarItem:
1339 case CdMarkerBarItem:
1341 case TransportMarkerBarItem:
1343 cursor = which_grabber_cursor();
1351 set_canvas_cursor (cursor, true);
1356 Editor::trackviews_height() const
1358 if (!_trackview_group) {
1362 return _visible_canvas_height - _trackview_group->canvas_origin().y;