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;
83 ArdourCanvas::ScrollGroup* vg;
85 hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_track_canvas->root(),
86 ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically|
87 ArdourCanvas::ScrollGroup::ScrollsHorizontally));
88 CANVAS_DEBUG_NAME (hv_scroll_group, "canvas hv scroll");
89 _track_canvas->add_scroller (*hsg);
91 v_scroll_group = vg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsVertically);
92 CANVAS_DEBUG_NAME (v_scroll_group, "canvas v scroll");
93 _track_canvas->add_scroller (*vg);
95 h_scroll_group = hg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
96 CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll");
97 _track_canvas->add_scroller (*hg);
99 _verbose_cursor = new VerboseCursor (this);
101 /* on the bottom, an image */
103 if (Profile->get_sae()) {
104 Image img (::get_icon (X_("saelogo")));
105 // logo_item = new ArdourCanvas::Pixbuf (_track_canvas->root(), 0.0, 0.0, img.get_pixbuf());
106 // logo_item->property_height_in_pixels() = true;
107 // logo_item->property_width_in_pixels() = true;
108 // logo_item->property_height_set() = true;
109 // logo_item->property_width_set() = true;
110 // logo_item->show ();
113 /*a group to hold global rects like punch/loop indicators */
114 global_rect_group = new ArdourCanvas::Container (hv_scroll_group);
115 CANVAS_DEBUG_NAME (global_rect_group, "global rect group");
117 transport_loop_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
118 CANVAS_DEBUG_NAME (transport_loop_range_rect, "loop rect");
119 transport_loop_range_rect->hide();
121 transport_punch_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
122 CANVAS_DEBUG_NAME (transport_punch_range_rect, "punch rect");
123 transport_punch_range_rect->hide();
125 /*a group to hold time (measure) lines */
126 time_line_group = new ArdourCanvas::Container (h_scroll_group);
127 CANVAS_DEBUG_NAME (time_line_group, "time line group");
129 _trackview_group = new ArdourCanvas::Container (hv_scroll_group);
130 CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
132 // used as rubberband rect
133 rubberband_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
134 rubberband_rect->hide();
136 /* a group to hold stuff while it gets dragged around. Must be the
137 * uppermost (last) group with hv_scroll_group as a parent
139 _drag_motion_group = new ArdourCanvas::Container (hv_scroll_group);
140 CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion");
142 /* TIME BAR CANVAS */
144 _time_markers_group = new ArdourCanvas::Container (h_scroll_group);
145 CANVAS_DEBUG_NAME (_time_markers_group, "time bars");
147 cd_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, 0.0));
148 CANVAS_DEBUG_NAME (cd_marker_group, "cd marker group");
149 /* the vide is temporarily placed a the same location as the
150 cd_marker_group, but is moved later.
152 videotl_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0));
153 CANVAS_DEBUG_NAME (videotl_group, "videotl group");
154 marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, timebar_height + 1.0));
155 CANVAS_DEBUG_NAME (marker_group, "marker group");
156 transport_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 2.0) + 1.0));
157 CANVAS_DEBUG_NAME (transport_marker_group, "transport marker group");
158 range_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 3.0) + 1.0));
159 CANVAS_DEBUG_NAME (range_marker_group, "range marker group");
160 tempo_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 4.0) + 1.0));
161 CANVAS_DEBUG_NAME (tempo_group, "tempo group");
162 meter_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0));
163 CANVAS_DEBUG_NAME (meter_group, "meter group");
165 meter_bar = new ArdourCanvas::Rectangle (meter_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
166 CANVAS_DEBUG_NAME (meter_bar, "meter Bar");
167 meter_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
169 tempo_bar = new ArdourCanvas::Rectangle (tempo_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
170 CANVAS_DEBUG_NAME (tempo_bar, "Tempo Bar");
171 tempo_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
173 range_marker_bar = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
174 CANVAS_DEBUG_NAME (range_marker_bar, "Range Marker Bar");
175 range_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
177 transport_marker_bar = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
178 CANVAS_DEBUG_NAME (transport_marker_bar, "transport Marker Bar");
179 transport_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
181 marker_bar = new ArdourCanvas::Rectangle (marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
182 CANVAS_DEBUG_NAME (marker_bar, "Marker Bar");
183 marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
185 cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
186 CANVAS_DEBUG_NAME (cd_marker_bar, "CD Marker Bar");
187 cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
189 ARDOUR_UI::instance()->video_timeline = new VideoTimeLine(this, videotl_group, (timebar_height * videotl_bar_height));
191 cd_marker_bar_drag_rect = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
192 CANVAS_DEBUG_NAME (cd_marker_bar_drag_rect, "cd marker drag");
193 cd_marker_bar_drag_rect->set_outline (false);
194 cd_marker_bar_drag_rect->hide ();
196 range_bar_drag_rect = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
197 CANVAS_DEBUG_NAME (range_bar_drag_rect, "range drag");
198 range_bar_drag_rect->set_outline (false);
199 range_bar_drag_rect->hide ();
201 transport_bar_drag_rect = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
202 CANVAS_DEBUG_NAME (transport_bar_drag_rect, "transport drag");
203 transport_bar_drag_rect->set_outline (false);
204 transport_bar_drag_rect->hide ();
206 transport_punchin_line = new ArdourCanvas::Line (hv_scroll_group);
207 transport_punchin_line->set_x0 (0);
208 transport_punchin_line->set_y0 (0);
209 transport_punchin_line->set_x1 (0);
210 transport_punchin_line->set_y1 (ArdourCanvas::COORD_MAX);
211 transport_punchin_line->hide ();
213 transport_punchout_line = new ArdourCanvas::Line (hv_scroll_group);
214 transport_punchout_line->set_x0 (0);
215 transport_punchout_line->set_y0 (0);
216 transport_punchout_line->set_x1 (0);
217 transport_punchout_line->set_y1 (ArdourCanvas::COORD_MAX);
218 transport_punchout_line->hide();
220 tempo_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_tempo_bar_event), tempo_bar));
221 meter_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_meter_bar_event), meter_bar));
222 marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_marker_bar_event), marker_bar));
223 cd_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_cd_marker_bar_event), cd_marker_bar));
224 videotl_group->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_videotl_bar_event), videotl_group));
225 range_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_range_marker_bar_event), range_marker_bar));
226 transport_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_transport_marker_bar_event), transport_marker_bar));
228 playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event);
231 logo_item->lower_to_bottom ();
235 _canvas_drop_zone = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, 0.0));
236 /* this thing is transparent */
237 _canvas_drop_zone->set_fill (false);
238 _canvas_drop_zone->set_outline (false);
239 _canvas_drop_zone->Event.connect (sigc::mem_fun (*this, &Editor::canvas_drop_zone_event));
241 /* these signals will initially be delivered to the canvas itself, but if they end up remaining unhandled, they are passed to Editor-level
245 _track_canvas->signal_scroll_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_scroll_event), true));
246 _track_canvas->signal_motion_notify_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_motion_notify_event));
247 _track_canvas->signal_button_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_press_event));
248 _track_canvas->signal_button_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_release_event));
249 _track_canvas->signal_drag_motion().connect (sigc::mem_fun (*this, &Editor::track_canvas_drag_motion));
250 _track_canvas->signal_key_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_press));
251 _track_canvas->signal_key_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_release));
253 _track_canvas->set_name ("EditorMainCanvas");
254 _track_canvas->add_events (Gdk::POINTER_MOTION_HINT_MASK | Gdk::SCROLL_MASK | Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
255 _track_canvas->signal_leave_notify_event().connect (sigc::mem_fun(*this, &Editor::left_track_canvas), false);
256 _track_canvas->signal_enter_notify_event().connect (sigc::mem_fun(*this, &Editor::entered_track_canvas), false);
257 _track_canvas->set_flags (CAN_FOCUS);
259 /* set up drag-n-drop */
261 vector<TargetEntry> target_table;
263 // Drag-N-Drop from the region list can generate this target
264 target_table.push_back (TargetEntry ("regions"));
266 target_table.push_back (TargetEntry ("text/plain"));
267 target_table.push_back (TargetEntry ("text/uri-list"));
268 target_table.push_back (TargetEntry ("application/x-rootwin-drop"));
270 _track_canvas->drag_dest_set (target_table);
271 _track_canvas->signal_drag_data_received().connect (sigc::mem_fun(*this, &Editor::track_canvas_drag_data_received));
273 _track_canvas_viewport->signal_size_allocate().connect (sigc::mem_fun(*this, &Editor::track_canvas_viewport_allocate));
275 initialize_rulers ();
277 ColorsChanged.connect (sigc::mem_fun (*this, &Editor::color_handler));
283 Editor::track_canvas_viewport_allocate (Gtk::Allocation alloc)
285 _canvas_viewport_allocation = alloc;
286 track_canvas_viewport_size_allocated ();
290 Editor::track_canvas_viewport_size_allocated ()
292 bool height_changed = _visible_canvas_height != _canvas_viewport_allocation.get_height();
294 _visible_canvas_width = _canvas_viewport_allocation.get_width ();
295 _visible_canvas_height = _canvas_viewport_allocation.get_height ();
297 _canvas_drop_zone->set_y1 (_canvas_drop_zone->y0() + (_visible_canvas_height - 20.0));
301 if (height_changed) {
303 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
304 i->second->canvas_height_set (_visible_canvas_height);
307 vertical_adjustment.set_page_size (_visible_canvas_height);
308 if ((vertical_adjustment.get_value() + _visible_canvas_height) >= vertical_adjustment.get_upper()) {
310 We're increasing the size of the canvas while the bottom is visible.
311 We scroll down to keep in step with the controls layout.
313 vertical_adjustment.set_value (_full_canvas_height - _visible_canvas_height);
316 set_visible_track_count (_visible_track_count);
319 update_fixed_rulers();
320 redisplay_tempo (false);
321 _summary->set_overlays_dirty ();
325 Editor::reset_controls_layout_width ()
327 GtkRequisition req = { 0, 0 };
330 edit_controls_vbox.size_request (req);
333 if (_group_tabs->is_mapped()) {
334 _group_tabs->size_request (req);
338 /* the controls layout has no horizontal scrolling, its visible
339 width is always equal to the total width of its contents.
342 controls_layout.property_width() = w;
343 controls_layout.property_width_request() = w;
347 Editor::reset_controls_layout_height (int32_t h)
349 /* ensure that the rect that represents the "bottom" of the canvas
350 * (the drag-n-drop zone) is, in fact, at the bottom.
353 _canvas_drop_zone->set_position (ArdourCanvas::Duple (0, h));
355 /* track controls layout must span the full height of "h" (all tracks)
356 * plus the bottom rect.
359 h += _canvas_drop_zone->height ();
361 /* set the height of the scrollable area (i.e. the sum of all contained widgets)
362 * for the controls layout. The size request is set elsewhere.
365 controls_layout.property_height() = h;
370 Editor::track_canvas_map_handler (GdkEventAny* /*ev*/)
372 if (!_cursor_stack.empty()) {
373 set_canvas_cursor (get_canvas_cursor());
375 PBD::error << "cursor stack is empty" << endmsg;
380 /** This is called when something is dropped onto the track canvas */
382 Editor::track_canvas_drag_data_received (const RefPtr<Gdk::DragContext>& context,
384 const SelectionData& data,
385 guint info, guint time)
387 if (data.get_target() == "regions") {
388 drop_regions (context, x, y, data, info, time);
390 drop_paths (context, x, y, data, info, time);
395 Editor::idle_drop_paths (vector<string> paths, framepos_t frame, double ypos, bool copy)
397 drop_paths_part_two (paths, frame, ypos, copy);
402 Editor::drop_paths_part_two (const vector<string>& paths, framepos_t frame, double ypos, bool copy)
404 RouteTimeAxisView* tv;
406 /* MIDI files must always be imported, because we consider them
407 * writable. So split paths into two vectors, and follow the import
408 * path on the MIDI part.
411 vector<string> midi_paths;
412 vector<string> audio_paths;
414 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
415 if (SMFSource::safe_midi_file_extension (*i)) {
416 midi_paths.push_back (*i);
418 audio_paths.push_back (*i);
423 std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (ypos, false);
424 if (tvp.first == 0) {
426 /* drop onto canvas background: create new tracks */
430 do_import (midi_paths, Editing::ImportDistinctFiles, ImportAsTrack, SrcBest, frame);
432 if (Profile->get_sae() || ARDOUR_UI::config()->get_only_copy_imported_files() || copy) {
433 do_import (audio_paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack, SrcBest, frame);
435 do_embed (audio_paths, Editing::ImportDistinctFiles, ImportAsTrack, frame);
438 } else if ((tv = dynamic_cast<RouteTimeAxisView*> (tvp.first)) != 0) {
440 /* check that its a track, not a bus */
443 /* select the track, then embed/import */
446 do_import (midi_paths, Editing::ImportSerializeFiles, ImportToTrack, SrcBest, frame);
448 if (Profile->get_sae() || ARDOUR_UI::config()->get_only_copy_imported_files() || copy) {
449 do_import (audio_paths, Editing::ImportSerializeFiles, Editing::ImportToTrack, SrcBest, frame);
451 do_embed (audio_paths, Editing::ImportSerializeFiles, ImportToTrack, frame);
458 Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
460 const SelectionData& data,
461 guint info, guint time)
463 vector<string> paths;
468 if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
470 /* D-n-D coordinates are window-relative, so convert to canvas coordinates
473 ev.type = GDK_BUTTON_RELEASE;
477 frame = window_event_sample (&ev, 0, &cy);
481 bool copy = ((context->get_actions() & (Gdk::ACTION_COPY | Gdk::ACTION_LINK | Gdk::ACTION_MOVE)) == Gdk::ACTION_COPY);
483 /* We are not allowed to call recursive main event loops from within
484 the main event loop with GTK/Quartz. Since import/embed wants
485 to push up a progress dialog, defer all this till we go idle.
487 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun (*this, &Editor::idle_drop_paths), paths, frame, cy, copy));
489 drop_paths_part_two (paths, frame, cy, copy);
493 context->drag_finish (true, false, time);
496 /** @param allow_horiz true to allow horizontal autoscroll, otherwise false.
498 * @param allow_vert true to allow vertical autoscroll, otherwise false.
502 Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
504 if (!ARDOUR_UI::config()->get_autoscroll_editor () || autoscroll_active ()) {
508 /* define a rectangular boundary for scrolling. If the mouse moves
509 * outside of this area and/or continue to be outside of this area,
510 * then we will continuously auto-scroll the canvas in the appropriate
513 * the boundary is defined in coordinates relative to the toplevel
514 * window since that is what we're going to call ::get_pointer() on
515 * during autoscrolling to determine if we're still outside the
519 ArdourCanvas::Rect scrolling_boundary;
520 Gtk::Allocation alloc;
523 alloc = controls_layout.get_allocation ();
525 alloc = _track_canvas_viewport->get_allocation ();
527 /* reduce height by the height of the timebars, which happens
528 to correspond to the position of the hv_scroll_group.
531 alloc.set_height (alloc.get_height() - hv_scroll_group->position().y);
532 alloc.set_y (alloc.get_y() + hv_scroll_group->position().y);
534 /* now reduce it again so that we start autoscrolling before we
535 * move off the top or bottom of the canvas
538 alloc.set_height (alloc.get_height() - 20);
539 alloc.set_y (alloc.get_y() + 10);
541 /* the effective width of the autoscroll boundary so
542 that we start scrolling before we hit the edge.
544 this helps when the window is slammed up against the
545 right edge of the screen, making it hard to scroll
549 if (alloc.get_width() > 20) {
550 alloc.set_width (alloc.get_width() - 20);
551 alloc.set_x (alloc.get_x() + 10);
556 scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), alloc.get_x() + alloc.get_width(), alloc.get_y() + alloc.get_height());
559 Gdk::ModifierType mask;
561 get_window()->get_pointer (x, y, mask);
563 if ((allow_horiz && ((x < scrolling_boundary.x0 && leftmost_frame > 0) || x >= scrolling_boundary.x1)) ||
564 (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) {
565 start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary);
570 Editor::autoscroll_active () const
572 return autoscroll_connection.connected ();
576 Editor::autoscroll_canvas ()
579 Gdk::ModifierType mask;
580 frameoffset_t dx = 0;
581 bool no_stop = false;
583 get_window()->get_pointer (x, y, mask);
586 bool vertical_motion = false;
588 if (autoscroll_horizontal_allowed) {
590 framepos_t new_frame = leftmost_frame;
594 if (x > autoscroll_boundary.x1) {
596 /* bring it back into view */
597 dx = x - autoscroll_boundary.x1;
598 dx += 10 + (2 * (autoscroll_cnt/2));
600 dx = pixel_to_sample (dx);
602 if (leftmost_frame < max_framepos - dx) {
603 new_frame = leftmost_frame + dx;
605 new_frame = max_framepos;
610 } else if (x < autoscroll_boundary.x0) {
612 dx = autoscroll_boundary.x0 - x;
613 dx += 10 + (2 * (autoscroll_cnt/2));
615 dx = pixel_to_sample (dx);
617 if (leftmost_frame >= dx) {
618 new_frame = leftmost_frame - dx;
626 if (new_frame != leftmost_frame) {
627 vc.time_origin = new_frame;
628 vc.add (VisualChange::TimeOrigin);
632 if (autoscroll_vertical_allowed) {
634 // const double vertical_pos = vertical_adjustment.get_value();
635 const int speed_factor = 10;
639 if (y < autoscroll_boundary.y0) {
641 /* scroll to make higher tracks visible */
643 if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
644 scroll_up_one_track ();
645 vertical_motion = true;
648 } else if (y > autoscroll_boundary.y1) {
650 if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
651 scroll_down_one_track ();
652 vertical_motion = true;
659 if (vc.pending || vertical_motion) {
661 /* change horizontal first */
667 /* now send a motion event to notify anyone who cares
668 that we have moved to a new location (because we scrolled)
673 ev.type = GDK_MOTION_NOTIFY;
674 ev.state = Gdk::BUTTON1_MASK;
676 /* the motion handler expects events in canvas coordinate space */
678 /* we asked for the mouse position above (::get_pointer()) via
679 * our own top level window (we being the Editor). Convert into
680 * coordinates within the canvas window.
686 translate_coordinates (*_track_canvas, x, y, cx, cy);
688 /* clamp x and y to remain within the autoscroll boundary,
689 * which is defined in window coordinates
692 x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1);
693 y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1);
695 /* now convert from Editor window coordinates to canvas
699 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
703 motion_handler (0, (GdkEvent*) &ev, true);
705 } else if (no_stop) {
707 /* not changing visual state but pointer is outside the scrolling boundary
708 * so we still need to deliver a fake motion event
713 ev.type = GDK_MOTION_NOTIFY;
714 ev.state = Gdk::BUTTON1_MASK;
716 /* the motion handler expects events in canvas coordinate space */
718 /* first convert from Editor window coordinates to canvas
725 /* clamp x and y to remain within the visible area. except
726 * .. if horizontal scrolling is allowed, always allow us to
730 if (autoscroll_horizontal_allowed) {
731 x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1);
733 x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
735 y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
737 translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
739 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
743 motion_handler (0, (GdkEvent*) &ev, true);
746 stop_canvas_autoscroll ();
752 return true; /* call me again */
756 Editor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary)
762 stop_canvas_autoscroll ();
765 autoscroll_horizontal_allowed = allow_horiz;
766 autoscroll_vertical_allowed = allow_vert;
767 autoscroll_boundary = boundary;
769 /* do the first scroll right now
772 autoscroll_canvas ();
774 /* scroll again at very very roughly 30FPS */
776 autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::autoscroll_canvas), 30);
780 Editor::stop_canvas_autoscroll ()
782 autoscroll_connection.disconnect ();
785 Editor::EnterContext*
786 Editor::get_enter_context(ItemType type)
788 for (ssize_t i = _enter_stack.size() - 1; i >= 0; --i) {
789 if (_enter_stack[i].item_type == type) {
790 return &_enter_stack[i];
797 Editor::left_track_canvas (GdkEventCrossing */*ev*/)
800 within_track_canvas = false;
801 set_entered_track (0);
802 set_entered_regionview (0);
803 reset_canvas_action_sensitivity (false);
808 Editor::entered_track_canvas (GdkEventCrossing */*ev*/)
810 within_track_canvas = true;
811 reset_canvas_action_sensitivity (true);
816 Editor::ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top)
818 if (track.hidden()) {
822 /* compute visible area of trackview group, as offsets from top of
826 double const current_view_min_y = vertical_adjustment.get_value();
827 double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size();
829 double const track_min_y = track.y_position ();
830 double const track_max_y = track.y_position () + track.effective_height ();
833 (track_min_y >= current_view_min_y &&
834 track_max_y < current_view_max_y)) {
835 /* already visible, and caller did not ask to place it at the
836 * top of the track canvas
844 new_value = track_min_y;
846 if (track_min_y < current_view_min_y) {
847 // Track is above the current view
848 new_value = track_min_y;
849 } else if (track_max_y > current_view_max_y) {
850 // Track is below the current view
851 new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size();
853 new_value = track_min_y;
857 vertical_adjustment.set_value(new_value);
860 /** Called when the main vertical_adjustment has changed */
862 Editor::tie_vertical_scrolling ()
864 if (pending_visual_change.idle_handler_id < 0) {
865 _summary->set_overlays_dirty ();
870 Editor::set_horizontal_position (double p)
872 horizontal_adjustment.set_value (p);
874 leftmost_frame = (framepos_t) floor (p * samples_per_pixel);
876 update_fixed_rulers ();
877 redisplay_tempo (true);
879 if (pending_visual_change.idle_handler_id < 0) {
880 _summary->set_overlays_dirty ();
883 update_video_timeline();
887 Editor::color_handler()
889 ArdourCanvas::Color base = ARDOUR_UI::config()->color ("ruler base");
890 ArdourCanvas::Color text = ARDOUR_UI::config()->color ("ruler text");
891 timecode_ruler->set_fill_color (base);
892 timecode_ruler->set_outline_color (text);
893 minsec_ruler->set_fill_color (base);
894 minsec_ruler->set_outline_color (text);
895 samples_ruler->set_fill_color (base);
896 samples_ruler->set_outline_color (text);
897 bbt_ruler->set_fill_color (base);
898 bbt_ruler->set_outline_color (text);
900 playhead_cursor->set_color (ARDOUR_UI::config()->color ("play head"));
902 meter_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("meter bar", "marker bar"));
903 meter_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
905 tempo_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("tempo bar", "marker bar"));
906 tempo_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
908 marker_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("marker bar", "marker bar"));
909 marker_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
911 cd_marker_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("cd marker bar", "marker bar"));
912 cd_marker_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
914 range_marker_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("range marker bar", "marker bar"));
915 range_marker_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
917 transport_marker_bar->set_fill_color (ARDOUR_UI::config()->color_mod ("transport marker bar", "marker bar"));
918 transport_marker_bar->set_outline_color (ARDOUR_UI::config()->color ("marker bar separator"));
920 cd_marker_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag bar rect"));
921 cd_marker_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag bar rect"));
923 range_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag bar rect"));
924 range_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag bar rect"));
926 transport_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->color ("transport drag rect"));
927 transport_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->color ("transport drag rect"));
929 transport_loop_range_rect->set_fill_color (ARDOUR_UI::config()->color_mod ("transport loop rect", "loop rectangle"));
930 transport_loop_range_rect->set_outline_color (ARDOUR_UI::config()->color ("transport loop rect"));
932 transport_punch_range_rect->set_fill_color (ARDOUR_UI::config()->color ("transport punch rect"));
933 transport_punch_range_rect->set_outline_color (ARDOUR_UI::config()->color ("transport punch rect"));
935 transport_punchin_line->set_outline_color (ARDOUR_UI::config()->color ("punch line"));
936 transport_punchout_line->set_outline_color (ARDOUR_UI::config()->color ("punch line"));
938 rubberband_rect->set_outline_color (ARDOUR_UI::config()->color ("rubber band rect"));
939 rubberband_rect->set_fill_color (ARDOUR_UI::config()->color_mod ("rubber band rect", "selection rect"));
941 location_marker_color = ARDOUR_UI::config()->color ("location marker");
942 location_range_color = ARDOUR_UI::config()->color ("location range");
943 location_cd_marker_color = ARDOUR_UI::config()->color ("location cd marker");
944 location_loop_color = ARDOUR_UI::config()->color ("location loop");
945 location_punch_color = ARDOUR_UI::config()->color ("location punch");
947 refresh_location_display ();
949 /* redraw the whole thing */
950 _track_canvas->set_background_color (ARDOUR_UI::config()->color ("arrange base"));
951 _track_canvas->queue_draw ();
954 redisplay_tempo (true);
957 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
962 Editor::horizontal_position () const
964 return sample_to_pixel (leftmost_frame);
968 Editor::track_canvas_key_press (GdkEventKey*)
974 Editor::track_canvas_key_release (GdkEventKey*)
980 Editor::clamp_verbose_cursor_x (double x)
985 x = min (_visible_canvas_width - 200.0, x);
991 Editor::clamp_verbose_cursor_y (double y)
994 y = min (_visible_canvas_height - 50, y);
998 ArdourCanvas::GtkCanvasViewport*
999 Editor::get_track_canvas() const
1001 return _track_canvas_viewport;
1005 Editor::get_canvas_cursor () const
1007 /* The top of the cursor stack is always the currently visible cursor. */
1008 return _cursor_stack.back();
1012 Editor::set_canvas_cursor (Gdk::Cursor* cursor)
1014 Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
1016 if (win && cursor) {
1017 win->set_cursor (*cursor);
1022 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
1025 _cursor_stack.push_back (cursor);
1026 set_canvas_cursor (cursor);
1028 return _cursor_stack.size() - 1;
1032 Editor::pop_canvas_cursor ()
1035 if (_cursor_stack.size() <= 1) {
1036 PBD::error << "attempt to pop default cursor" << endmsg;
1040 _cursor_stack.pop_back();
1041 if (_cursor_stack.back()) {
1042 /* Popped to an existing cursor, we're done. Otherwise, the
1043 context that created this cursor has been destroyed, so we need
1044 to skip to the next down the stack. */
1045 set_canvas_cursor (_cursor_stack.back());
1052 Editor::which_grabber_cursor () const
1054 Gdk::Cursor* c = _cursors->grabber;
1056 switch (_edit_point) {
1058 c = _cursors->grabber_edit_point;
1061 boost::shared_ptr<Movable> m = _movable.lock();
1062 if (m && m->locked()) {
1063 c = _cursors->speaker;
1072 Editor::which_trim_cursor (bool left) const
1074 if (!entered_regionview) {
1078 Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
1082 if (ct & Trimmable::FrontTrimEarlier) {
1083 return _cursors->left_side_trim;
1085 return _cursors->left_side_trim_right_only;
1088 if (ct & Trimmable::EndTrimLater) {
1089 return _cursors->right_side_trim;
1091 return _cursors->right_side_trim_left_only;
1097 Editor::which_mode_cursor () const
1099 Gdk::Cursor* mode_cursor = 0;
1101 switch (mouse_mode) {
1103 mode_cursor = _cursors->selector;
1107 mode_cursor = _cursors->scissors;
1112 /* don't use mode cursor, pick a grabber cursor based on the item */
1116 mode_cursor = _cursors->midi_pencil;
1120 mode_cursor = _cursors->time_fx; // just use playhead
1124 mode_cursor = _cursors->speaker;
1128 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
1129 if (get_smart_mode()) {
1132 get_pointer_position (x, y);
1134 if (x >= 0 && y >= 0) {
1136 vector<ArdourCanvas::Item const *> items;
1138 /* Note how we choose a specific scroll group to get
1139 * items from. This could be problematic.
1142 hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
1144 // first item will be the upper most
1146 if (!items.empty()) {
1147 const ArdourCanvas::Item* i = items.front();
1149 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
1150 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
1151 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
1152 mode_cursor = _cursors->up_down;
1163 Editor::which_track_cursor () const
1165 Gdk::Cursor* cursor = 0;
1167 switch (_join_object_range_state) {
1168 case JOIN_OBJECT_RANGE_NONE:
1169 case JOIN_OBJECT_RANGE_OBJECT:
1170 cursor = which_grabber_cursor ();
1172 case JOIN_OBJECT_RANGE_RANGE:
1173 cursor = _cursors->selector;
1181 Editor::which_canvas_cursor(ItemType type) const
1183 Gdk::Cursor* cursor = which_mode_cursor ();
1185 if ((mouse_mode == MouseObject || get_smart_mode ()) ||
1186 mouse_mode == MouseContent) {
1188 /* find correct cursor to use in object/smart mode */
1192 /* We don't choose a cursor for these items on top of a region view,
1193 because this would push a new context on the enter stack which
1194 means switching the region context for things like smart mode
1195 won't actualy change the cursor. */
1196 // case RegionViewNameHighlight:
1197 // case RegionViewName:
1200 case AutomationTrackItem:
1201 cursor = which_track_cursor ();
1203 case PlayheadCursorItem:
1204 switch (_edit_point) {
1206 cursor = _cursors->grabber_edit_point;
1209 cursor = _cursors->grabber;
1214 cursor = _cursors->selector;
1216 case ControlPointItem:
1217 cursor = _cursors->fader;
1220 cursor = which_track_cursor ();
1222 case AutomationLineItem:
1223 cursor = _cursors->cross_hair;
1225 case StartSelectionTrimItem:
1226 cursor = _cursors->left_side_trim;
1228 case EndSelectionTrimItem:
1229 cursor = _cursors->right_side_trim;
1232 cursor = _cursors->fade_in;
1234 case FadeInHandleItem:
1235 cursor = _cursors->fade_in;
1237 case FadeInTrimHandleItem:
1238 cursor = _cursors->fade_in;
1241 cursor = _cursors->fade_out;
1243 case FadeOutHandleItem:
1244 cursor = _cursors->fade_out;
1246 case FadeOutTrimHandleItem:
1247 cursor = _cursors->fade_out;
1249 case FeatureLineItem:
1250 cursor = _cursors->cross_hair;
1252 case LeftFrameHandle:
1253 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
1254 cursor = which_trim_cursor (true); //alternatively, one could argue that we _should_ allow trims here, and disallow range selection
1256 case RightFrameHandle:
1257 if ( effective_mouse_mode() == MouseObject ) //see above
1258 cursor = which_trim_cursor (false);
1260 case StartCrossFadeItem:
1261 cursor = _cursors->fade_in;
1263 case EndCrossFadeItem:
1264 cursor = _cursors->fade_out;
1266 case CrossfadeViewItem:
1267 cursor = _cursors->cross_hair;
1270 cursor = _cursors->grabber_note;
1275 } else if (mouse_mode == MouseDraw) {
1277 /* ControlPointItem is not really specific to region gain mode
1278 but it is the same cursor so don't worry about this for now.
1279 The result is that we'll see the fader cursor if we enter
1280 non-region-gain-line control points while in MouseDraw
1281 mode, even though we can't edit them in this mode.
1286 case ControlPointItem:
1287 cursor = _cursors->fader;
1290 cursor = _cursors->grabber_note;
1297 /* These items use the timebar cursor at all times */
1298 case TimecodeRulerItem:
1299 case MinsecRulerItem:
1301 case SamplesRulerItem:
1302 cursor = _cursors->timebar;
1305 /* These items use the grabber cursor at all times */
1306 case MeterMarkerItem:
1307 case TempoMarkerItem:
1312 case RangeMarkerBarItem:
1313 case CdMarkerBarItem:
1315 case TransportMarkerBarItem:
1317 cursor = which_grabber_cursor();
1328 Editor::choose_canvas_cursor_on_entry (ItemType type)
1330 if (_drags->active()) {
1334 Gdk::Cursor* cursor = which_canvas_cursor(type);
1337 // Push a new enter context
1338 const EnterContext ctx = { type, CursorContext::create(*this, cursor) };
1339 _enter_stack.push_back(ctx);
1344 Editor::update_all_enter_cursors ()
1346 for (std::vector<EnterContext>::iterator i = _enter_stack.begin(); i != _enter_stack.end(); ++i) {
1347 i->cursor_ctx->change(which_canvas_cursor(i->item_type));
1352 Editor::trackviews_height() const
1354 if (!_trackview_group) {
1358 return _visible_canvas_height - _trackview_group->canvas_origin().y;