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"
43 #include "rgb_macros.h"
45 #include "audio_time_axis.h"
46 #include "editor_drag.h"
47 #include "region_view.h"
48 #include "editor_group_tabs.h"
49 #include "editor_summary.h"
50 #include "video_timeline.h"
52 #include "editor_cursors.h"
53 #include "mouse_cursors.h"
54 #include "note_base.h"
55 #include "ui_config.h"
56 #include "verbose_cursor.h"
61 using namespace ARDOUR;
62 using namespace ARDOUR_UI_UTILS;
66 using namespace Gtkmm2ext;
67 using namespace Editing;
70 Editor::initialize_canvas ()
72 _track_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, vertical_adjustment);
73 _track_canvas = _track_canvas_viewport->canvas ();
75 _track_canvas->set_background_color (UIConfiguration::instance().color ("arrange base"));
77 /* scroll group for items that should not automatically scroll
78 * (e.g verbose cursor). It shares the canvas coordinate space.
80 no_scroll_group = new ArdourCanvas::Container (_track_canvas->root());
82 ArdourCanvas::ScrollGroup* hsg;
83 ArdourCanvas::ScrollGroup* hg;
84 ArdourCanvas::ScrollGroup* cg;
86 h_scroll_group = hg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
87 CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll");
88 _track_canvas->add_scroller (*hg);
90 hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_track_canvas->root(),
91 ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically|
92 ArdourCanvas::ScrollGroup::ScrollsHorizontally));
93 CANVAS_DEBUG_NAME (hv_scroll_group, "canvas hv scroll");
94 _track_canvas->add_scroller (*hsg);
96 cursor_scroll_group = cg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
97 CANVAS_DEBUG_NAME (cursor_scroll_group, "canvas cursor scroll");
98 _track_canvas->add_scroller (*cg);
100 _verbose_cursor = new VerboseCursor (this);
102 /*a group to hold global rects like punch/loop indicators */
103 global_rect_group = new ArdourCanvas::Container (hv_scroll_group);
104 CANVAS_DEBUG_NAME (global_rect_group, "global rect group");
106 transport_loop_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
107 CANVAS_DEBUG_NAME (transport_loop_range_rect, "loop rect");
108 transport_loop_range_rect->hide();
110 transport_punch_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
111 CANVAS_DEBUG_NAME (transport_punch_range_rect, "punch rect");
112 transport_punch_range_rect->hide();
114 /*a group to hold time (measure) lines */
115 time_line_group = new ArdourCanvas::Container (h_scroll_group);
116 CANVAS_DEBUG_NAME (time_line_group, "time line group");
118 _trackview_group = new ArdourCanvas::Container (hv_scroll_group);
119 CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
121 // used as rubberband rect
122 rubberband_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
123 rubberband_rect->hide();
125 /* a group to hold stuff while it gets dragged around. Must be the
126 * uppermost (last) group with hv_scroll_group as a parent
128 _drag_motion_group = new ArdourCanvas::Container (hv_scroll_group);
129 CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion");
131 /* TIME BAR CANVAS */
133 _time_markers_group = new ArdourCanvas::Container (h_scroll_group);
134 CANVAS_DEBUG_NAME (_time_markers_group, "time bars");
136 cd_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, 0.0));
137 CANVAS_DEBUG_NAME (cd_marker_group, "cd marker group");
138 /* the vide is temporarily placed a the same location as the
139 cd_marker_group, but is moved later.
141 videotl_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0));
142 CANVAS_DEBUG_NAME (videotl_group, "videotl group");
143 marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, timebar_height + 1.0));
144 CANVAS_DEBUG_NAME (marker_group, "marker group");
145 transport_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 2.0) + 1.0));
146 CANVAS_DEBUG_NAME (transport_marker_group, "transport marker group");
147 range_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 3.0) + 1.0));
148 CANVAS_DEBUG_NAME (range_marker_group, "range marker group");
149 tempo_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 4.0) + 1.0));
150 CANVAS_DEBUG_NAME (tempo_group, "tempo group");
151 meter_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0));
152 CANVAS_DEBUG_NAME (meter_group, "meter group");
154 meter_bar = new ArdourCanvas::Rectangle (meter_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
155 CANVAS_DEBUG_NAME (meter_bar, "meter Bar");
156 meter_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
158 tempo_bar = new ArdourCanvas::Rectangle (tempo_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
159 CANVAS_DEBUG_NAME (tempo_bar, "Tempo Bar");
160 tempo_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
162 range_marker_bar = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
163 CANVAS_DEBUG_NAME (range_marker_bar, "Range Marker Bar");
164 range_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
166 transport_marker_bar = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
167 CANVAS_DEBUG_NAME (transport_marker_bar, "transport Marker Bar");
168 transport_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
170 marker_bar = new ArdourCanvas::Rectangle (marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
171 CANVAS_DEBUG_NAME (marker_bar, "Marker Bar");
172 marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
174 cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
175 CANVAS_DEBUG_NAME (cd_marker_bar, "CD Marker Bar");
176 cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
178 ARDOUR_UI::instance()->video_timeline = new VideoTimeLine(this, videotl_group, (timebar_height * videotl_bar_height));
180 cd_marker_bar_drag_rect = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
181 CANVAS_DEBUG_NAME (cd_marker_bar_drag_rect, "cd marker drag");
182 cd_marker_bar_drag_rect->set_outline (false);
183 cd_marker_bar_drag_rect->hide ();
185 range_bar_drag_rect = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
186 CANVAS_DEBUG_NAME (range_bar_drag_rect, "range drag");
187 range_bar_drag_rect->set_outline (false);
188 range_bar_drag_rect->hide ();
190 transport_bar_drag_rect = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
191 CANVAS_DEBUG_NAME (transport_bar_drag_rect, "transport drag");
192 transport_bar_drag_rect->set_outline (false);
193 transport_bar_drag_rect->hide ();
195 transport_punchin_line = new ArdourCanvas::Line (hv_scroll_group);
196 transport_punchin_line->set_x0 (0);
197 transport_punchin_line->set_y0 (0);
198 transport_punchin_line->set_x1 (0);
199 transport_punchin_line->set_y1 (ArdourCanvas::COORD_MAX);
200 transport_punchin_line->hide ();
202 transport_punchout_line = new ArdourCanvas::Line (hv_scroll_group);
203 transport_punchout_line->set_x0 (0);
204 transport_punchout_line->set_y0 (0);
205 transport_punchout_line->set_x1 (0);
206 transport_punchout_line->set_y1 (ArdourCanvas::COORD_MAX);
207 transport_punchout_line->hide();
209 tempo_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_tempo_bar_event), tempo_bar));
210 meter_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_meter_bar_event), meter_bar));
211 marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_marker_bar_event), marker_bar));
212 cd_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_cd_marker_bar_event), cd_marker_bar));
213 videotl_group->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_videotl_bar_event), videotl_group));
214 range_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_range_marker_bar_event), range_marker_bar));
215 transport_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_transport_marker_bar_event), transport_marker_bar));
217 playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event);
219 _canvas_drop_zone = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, 0.0));
220 /* this thing is transparent */
221 _canvas_drop_zone->set_fill (false);
222 _canvas_drop_zone->set_outline (false);
223 _canvas_drop_zone->Event.connect (sigc::mem_fun (*this, &Editor::canvas_drop_zone_event));
225 /* these signals will initially be delivered to the canvas itself, but if they end up remaining unhandled, they are passed to Editor-level
229 _track_canvas->signal_scroll_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_scroll_event), true));
230 _track_canvas->signal_motion_notify_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_motion_notify_event));
231 _track_canvas->signal_button_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_press_event));
232 _track_canvas->signal_button_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_release_event));
233 _track_canvas->signal_drag_motion().connect (sigc::mem_fun (*this, &Editor::track_canvas_drag_motion));
234 _track_canvas->signal_key_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_press));
235 _track_canvas->signal_key_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_release));
237 _track_canvas->set_name ("EditorMainCanvas");
238 _track_canvas->add_events (Gdk::POINTER_MOTION_HINT_MASK | Gdk::SCROLL_MASK | Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
239 _track_canvas->signal_leave_notify_event().connect (sigc::mem_fun(*this, &Editor::left_track_canvas), false);
240 _track_canvas->signal_enter_notify_event().connect (sigc::mem_fun(*this, &Editor::entered_track_canvas), false);
241 _track_canvas->set_flags (CAN_FOCUS);
243 /* set up drag-n-drop */
245 vector<TargetEntry> target_table;
247 // Drag-N-Drop from the region list can generate this target
248 target_table.push_back (TargetEntry ("regions"));
250 target_table.push_back (TargetEntry ("text/plain"));
251 target_table.push_back (TargetEntry ("text/uri-list"));
252 target_table.push_back (TargetEntry ("application/x-rootwin-drop"));
254 _track_canvas->drag_dest_set (target_table);
255 _track_canvas->signal_drag_data_received().connect (sigc::mem_fun(*this, &Editor::track_canvas_drag_data_received));
257 _track_canvas_viewport->signal_size_allocate().connect (sigc::mem_fun(*this, &Editor::track_canvas_viewport_allocate));
259 initialize_rulers ();
261 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &Editor::color_handler));
267 Editor::track_canvas_viewport_allocate (Gtk::Allocation alloc)
269 _canvas_viewport_allocation = alloc;
270 track_canvas_viewport_size_allocated ();
274 Editor::track_canvas_viewport_size_allocated ()
276 bool height_changed = _visible_canvas_height != _canvas_viewport_allocation.get_height();
278 _visible_canvas_width = _canvas_viewport_allocation.get_width ();
279 _visible_canvas_height = _canvas_viewport_allocation.get_height ();
281 _canvas_drop_zone->set_y1 (_canvas_drop_zone->y0() + (_visible_canvas_height - 20.0));
285 if (height_changed) {
287 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
288 i->second->canvas_height_set (_visible_canvas_height);
291 vertical_adjustment.set_page_size (_visible_canvas_height);
292 if ((vertical_adjustment.get_value() + _visible_canvas_height) >= vertical_adjustment.get_upper()) {
294 We're increasing the size of the canvas while the bottom is visible.
295 We scroll down to keep in step with the controls layout.
297 vertical_adjustment.set_value (_full_canvas_height - _visible_canvas_height);
300 set_visible_track_count (_visible_track_count);
303 update_fixed_rulers();
304 redisplay_tempo (false);
305 _summary->set_overlays_dirty ();
309 Editor::reset_controls_layout_width ()
311 GtkRequisition req = { 0, 0 };
314 edit_controls_vbox.size_request (req);
317 if (_group_tabs->is_visible()) {
318 _group_tabs->size_request (req);
322 /* the controls layout has no horizontal scrolling, its visible
323 width is always equal to the total width of its contents.
326 controls_layout.property_width() = w;
327 controls_layout.property_width_request() = w;
331 Editor::reset_controls_layout_height (int32_t h)
333 /* ensure that the rect that represents the "bottom" of the canvas
334 * (the drag-n-drop zone) is, in fact, at the bottom.
337 _canvas_drop_zone->set_position (ArdourCanvas::Duple (0, h));
339 /* track controls layout must span the full height of "h" (all tracks)
340 * plus the bottom rect.
343 h += _canvas_drop_zone->height ();
345 /* set the height of the scrollable area (i.e. the sum of all contained widgets)
346 * for the controls layout. The size request is set elsewhere.
349 controls_layout.property_height() = h;
354 Editor::track_canvas_map_handler (GdkEventAny* /*ev*/)
356 if (!_cursor_stack.empty()) {
357 set_canvas_cursor (get_canvas_cursor());
359 PBD::error << "cursor stack is empty" << endmsg;
364 /** This is called when something is dropped onto the track canvas */
366 Editor::track_canvas_drag_data_received (const RefPtr<Gdk::DragContext>& context,
368 const SelectionData& data,
369 guint info, guint time)
371 if (data.get_target() == "regions") {
372 drop_regions (context, x, y, data, info, time);
374 drop_paths (context, x, y, data, info, time);
379 Editor::idle_drop_paths (vector<string> paths, framepos_t frame, double ypos, bool copy)
381 drop_paths_part_two (paths, frame, ypos, copy);
386 Editor::drop_paths_part_two (const vector<string>& paths, framepos_t frame, double ypos, bool copy)
388 RouteTimeAxisView* tv;
390 /* MIDI files must always be imported, because we consider them
391 * writable. So split paths into two vectors, and follow the import
392 * path on the MIDI part.
395 vector<string> midi_paths;
396 vector<string> audio_paths;
398 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
399 if (SMFSource::safe_midi_file_extension (*i)) {
400 midi_paths.push_back (*i);
402 audio_paths.push_back (*i);
407 std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (ypos, false);
408 if (tvp.first == 0) {
410 /* drop onto canvas background: create new tracks */
413 InstrumentSelector is; // instantiation builds instrument-list and sets default.
414 do_import (midi_paths, Editing::ImportDistinctFiles, ImportAsTrack, SrcBest, SMFTrackName, SMFTempoIgnore, frame, is.selected_instrument());
416 if (UIConfiguration::instance().get_only_copy_imported_files() || copy) {
417 do_import (audio_paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack,
418 SrcBest, SMFTrackName, SMFTempoIgnore, frame);
420 do_embed (audio_paths, Editing::ImportDistinctFiles, ImportAsTrack, frame);
423 } else if ((tv = dynamic_cast<RouteTimeAxisView*> (tvp.first)) != 0) {
425 /* check that its a track, not a bus */
428 /* select the track, then embed/import */
431 do_import (midi_paths, Editing::ImportSerializeFiles, ImportToTrack,
432 SrcBest, SMFTrackName, SMFTempoIgnore, frame);
434 if (UIConfiguration::instance().get_only_copy_imported_files() || copy) {
435 do_import (audio_paths, Editing::ImportSerializeFiles, Editing::ImportToTrack,
436 SrcBest, SMFTrackName, SMFTempoIgnore, frame);
438 do_embed (audio_paths, Editing::ImportSerializeFiles, ImportToTrack, frame);
445 Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
447 const SelectionData& data,
448 guint info, guint time)
450 vector<string> paths;
454 if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
456 /* D-n-D coordinates are window-relative, so convert to canvas coordinates
459 ev.type = GDK_BUTTON_RELEASE;
463 MusicFrame when (window_event_sample (&ev, 0, &cy), 0);
466 bool copy = ((context->get_actions() & (Gdk::ACTION_COPY | Gdk::ACTION_LINK | Gdk::ACTION_MOVE)) == Gdk::ACTION_COPY);
468 /* We are not allowed to call recursive main event loops from within
469 the main event loop with GTK/Quartz. Since import/embed wants
470 to push up a progress dialog, defer all this till we go idle.
472 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun (*this, &Editor::idle_drop_paths), paths, when.frame, cy, copy));
474 drop_paths_part_two (paths, when.frame, cy, copy);
478 context->drag_finish (true, false, time);
481 /** @param allow_horiz true to allow horizontal autoscroll, otherwise false.
483 * @param allow_vert true to allow vertical autoscroll, otherwise false.
487 Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
489 Gtk::Window* toplevel = dynamic_cast<Gtk::Window*>(contents().get_toplevel());
495 if (!UIConfiguration::instance().get_autoscroll_editor () || autoscroll_active ()) {
499 /* define a rectangular boundary for scrolling. If the mouse moves
500 * outside of this area and/or continue to be outside of this area,
501 * then we will continuously auto-scroll the canvas in the appropriate
504 * the boundary is defined in coordinates relative to the toplevel
505 * window since that is what we're going to call ::get_pointer() on
506 * during autoscrolling to determine if we're still outside the
510 ArdourCanvas::Rect scrolling_boundary;
511 Gtk::Allocation alloc;
514 alloc = controls_layout.get_allocation ();
518 controls_layout.get_parent()->translate_coordinates (*toplevel,
519 alloc.get_x(), alloc.get_y(),
522 scrolling_boundary = ArdourCanvas::Rect (wx, wy, wx + alloc.get_width(), wy + alloc.get_height());
526 alloc = _track_canvas_viewport->get_allocation ();
528 /* reduce height by the height of the timebars, which happens
529 to correspond to the position of the hv_scroll_group.
532 alloc.set_height (alloc.get_height() - hv_scroll_group->position().y);
533 alloc.set_y (alloc.get_y() + hv_scroll_group->position().y);
535 /* now reduce it again so that we start autoscrolling before we
536 * move off the top or bottom of the canvas
539 alloc.set_height (alloc.get_height() - 20);
540 alloc.set_y (alloc.get_y() + 10);
542 /* the effective width of the autoscroll boundary so
543 that we start scrolling before we hit the edge.
545 this helps when the window is slammed up against the
546 right edge of the screen, making it hard to scroll
550 if (alloc.get_width() > 20) {
551 alloc.set_width (alloc.get_width() - 20);
552 alloc.set_x (alloc.get_x() + 10);
557 _track_canvas_viewport->get_parent()->translate_coordinates (*toplevel,
558 alloc.get_x(), alloc.get_y(),
561 scrolling_boundary = ArdourCanvas::Rect (wx, wy, wx + alloc.get_width(), wy + alloc.get_height());
565 Gdk::ModifierType mask;
567 toplevel->get_window()->get_pointer (x, y, mask);
569 if ((allow_horiz && ((x < scrolling_boundary.x0 && leftmost_frame > 0) || x >= scrolling_boundary.x1)) ||
570 (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) {
571 start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary);
576 Editor::autoscroll_active () const
578 return autoscroll_connection.connected ();
582 Editor::autoscroll_canvas ()
585 Gdk::ModifierType mask;
586 frameoffset_t dx = 0;
587 bool no_stop = false;
588 Gtk::Window* toplevel = dynamic_cast<Gtk::Window*>(contents().get_toplevel());
594 toplevel->get_window()->get_pointer (x, y, mask);
597 bool vertical_motion = false;
599 if (autoscroll_horizontal_allowed) {
601 framepos_t new_frame = leftmost_frame;
605 if (x > autoscroll_boundary.x1) {
607 /* bring it back into view */
608 dx = x - autoscroll_boundary.x1;
609 dx += 10 + (2 * (autoscroll_cnt/2));
611 dx = pixel_to_sample (dx);
613 if (leftmost_frame < max_framepos - dx) {
614 new_frame = leftmost_frame + dx;
616 new_frame = max_framepos;
621 } else if (x < autoscroll_boundary.x0) {
623 dx = autoscroll_boundary.x0 - x;
624 dx += 10 + (2 * (autoscroll_cnt/2));
626 dx = pixel_to_sample (dx);
628 if (leftmost_frame >= dx) {
629 new_frame = leftmost_frame - dx;
637 if (new_frame != leftmost_frame) {
638 vc.time_origin = new_frame;
639 vc.add (VisualChange::TimeOrigin);
643 if (autoscroll_vertical_allowed) {
645 // const double vertical_pos = vertical_adjustment.get_value();
646 const int speed_factor = 10;
650 if (y < autoscroll_boundary.y0) {
652 /* scroll to make higher tracks visible */
654 if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
655 scroll_up_one_track ();
656 vertical_motion = true;
660 } else if (y > autoscroll_boundary.y1) {
662 if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
663 scroll_down_one_track ();
664 vertical_motion = true;
671 if (vc.pending || vertical_motion) {
673 /* change horizontal first */
679 /* now send a motion event to notify anyone who cares
680 that we have moved to a new location (because we scrolled)
685 ev.type = GDK_MOTION_NOTIFY;
686 ev.state = Gdk::BUTTON1_MASK;
688 /* the motion handler expects events in canvas coordinate space */
690 /* we asked for the mouse position above (::get_pointer()) via
691 * our own top level window (we being the Editor). Convert into
692 * coordinates within the canvas window.
698 toplevel->translate_coordinates (*_track_canvas, x, y, cx, cy);
700 /* clamp x and y to remain within the autoscroll boundary,
701 * which is defined in window coordinates
704 x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1);
705 y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1);
707 /* now convert from Editor window coordinates to canvas
711 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
716 motion_handler (0, (GdkEvent*) &ev, true);
718 } else if (no_stop) {
720 /* not changing visual state but pointer is outside the scrolling boundary
721 * so we still need to deliver a fake motion event
726 ev.type = GDK_MOTION_NOTIFY;
727 ev.state = Gdk::BUTTON1_MASK;
729 /* the motion handler expects events in canvas coordinate space */
731 /* first convert from Editor window coordinates to canvas
738 /* clamp x and y to remain within the visible area. except
739 * .. if horizontal scrolling is allowed, always allow us to
743 if (autoscroll_horizontal_allowed) {
744 x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1);
746 x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
748 y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
750 toplevel->translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
752 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
757 motion_handler (0, (GdkEvent*) &ev, true);
760 stop_canvas_autoscroll ();
766 return true; /* call me again */
770 Editor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary)
776 stop_canvas_autoscroll ();
778 autoscroll_horizontal_allowed = allow_horiz;
779 autoscroll_vertical_allowed = allow_vert;
780 autoscroll_boundary = boundary;
782 /* do the first scroll right now
785 autoscroll_canvas ();
787 /* scroll again at very very roughly 30FPS */
789 autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::autoscroll_canvas), 30);
793 Editor::stop_canvas_autoscroll ()
795 autoscroll_connection.disconnect ();
799 Editor::EnterContext*
800 Editor::get_enter_context(ItemType type)
802 for (ssize_t i = _enter_stack.size() - 1; i >= 0; --i) {
803 if (_enter_stack[i].item_type == type) {
804 return &_enter_stack[i];
811 Editor::left_track_canvas (GdkEventCrossing* ev)
813 const bool was_within = within_track_canvas;
815 within_track_canvas = false;
816 set_entered_track (0);
817 set_entered_regionview (0);
818 reset_canvas_action_sensitivity (false);
821 if (ev->detail == GDK_NOTIFY_NONLINEAR ||
822 ev->detail == GDK_NOTIFY_NONLINEAR_VIRTUAL) {
823 /* context menu or something similar */
824 sensitize_the_right_region_actions (true, false);
826 sensitize_the_right_region_actions (false, true);
834 Editor::entered_track_canvas (GdkEventCrossing* ev)
836 const bool was_within = within_track_canvas;
837 within_track_canvas = true;
838 reset_canvas_action_sensitivity (true);
841 if (ev->detail == GDK_NOTIFY_NONLINEAR ||
842 ev->detail == GDK_NOTIFY_NONLINEAR_VIRTUAL) {
843 /* context menu or something similar */
844 sensitize_the_right_region_actions (true, false);
846 sensitize_the_right_region_actions (false, true);
854 Editor::ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top)
856 if (track.hidden()) {
860 /* compute visible area of trackview group, as offsets from top of
864 double const current_view_min_y = vertical_adjustment.get_value();
865 double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size();
867 double const track_min_y = track.y_position ();
868 double const track_max_y = track.y_position () + track.effective_height ();
871 (track_min_y >= current_view_min_y &&
872 track_max_y < current_view_max_y)) {
873 /* already visible, and caller did not ask to place it at the
874 * top of the track canvas
882 new_value = track_min_y;
884 if (track_min_y < current_view_min_y) {
885 // Track is above the current view
886 new_value = track_min_y;
887 } else if (track_max_y > current_view_max_y) {
888 // Track is below the current view
889 new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size();
891 new_value = track_min_y;
895 vertical_adjustment.set_value(new_value);
898 /** Called when the main vertical_adjustment has changed */
900 Editor::tie_vertical_scrolling ()
902 if (pending_visual_change.idle_handler_id < 0) {
903 _summary->set_overlays_dirty ();
908 Editor::set_horizontal_position (double p)
910 horizontal_adjustment.set_value (p);
912 leftmost_frame = (framepos_t) floor (p * samples_per_pixel);
914 update_fixed_rulers ();
915 redisplay_tempo (true);
917 if (pending_visual_change.idle_handler_id < 0) {
918 _summary->set_overlays_dirty ();
921 update_video_timeline();
925 Editor::color_handler()
927 ArdourCanvas::Color base = UIConfiguration::instance().color ("ruler base");
928 ArdourCanvas::Color text = UIConfiguration::instance().color ("ruler text");
929 timecode_ruler->set_fill_color (base);
930 timecode_ruler->set_outline_color (text);
931 minsec_ruler->set_fill_color (base);
932 minsec_ruler->set_outline_color (text);
933 samples_ruler->set_fill_color (base);
934 samples_ruler->set_outline_color (text);
935 bbt_ruler->set_fill_color (base);
936 bbt_ruler->set_outline_color (text);
938 playhead_cursor->set_color (UIConfiguration::instance().color ("play head"));
940 meter_bar->set_fill_color (UIConfiguration::instance().color_mod ("meter bar", "marker bar"));
941 meter_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
943 tempo_bar->set_fill_color (UIConfiguration::instance().color_mod ("tempo bar", "marker bar"));
944 tempo_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
946 marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("marker bar", "marker bar"));
947 marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
949 cd_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("cd marker bar", "marker bar"));
950 cd_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
952 range_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("range marker bar", "marker bar"));
953 range_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
955 transport_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("transport marker bar", "marker bar"));
956 transport_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
958 cd_marker_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect"));
959 cd_marker_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect"));
961 range_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect"));
962 range_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect"));
964 transport_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("transport drag rect"));
965 transport_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("transport drag rect"));
967 transport_loop_range_rect->set_fill_color (UIConfiguration::instance().color_mod ("transport loop rect", "loop rectangle"));
968 transport_loop_range_rect->set_outline_color (UIConfiguration::instance().color ("transport loop rect"));
970 transport_punch_range_rect->set_fill_color (UIConfiguration::instance().color ("transport punch rect"));
971 transport_punch_range_rect->set_outline_color (UIConfiguration::instance().color ("transport punch rect"));
973 transport_punchin_line->set_outline_color (UIConfiguration::instance().color ("punch line"));
974 transport_punchout_line->set_outline_color (UIConfiguration::instance().color ("punch line"));
976 rubberband_rect->set_outline_color (UIConfiguration::instance().color ("rubber band rect"));
977 rubberband_rect->set_fill_color (UIConfiguration::instance().color_mod ("rubber band rect", "selection rect"));
979 location_marker_color = UIConfiguration::instance().color ("location marker");
980 location_range_color = UIConfiguration::instance().color ("location range");
981 location_cd_marker_color = UIConfiguration::instance().color ("location cd marker");
982 location_loop_color = UIConfiguration::instance().color ("location loop");
983 location_punch_color = UIConfiguration::instance().color ("location punch");
985 refresh_location_display ();
987 NoteBase::set_colors ();
989 /* redraw the whole thing */
990 _track_canvas->set_background_color (UIConfiguration::instance().color ("arrange base"));
991 _track_canvas->queue_draw ();
994 redisplay_tempo (true);
997 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
1002 Editor::horizontal_position () const
1004 return sample_to_pixel (leftmost_frame);
1008 Editor::track_canvas_key_press (GdkEventKey*)
1014 Editor::track_canvas_key_release (GdkEventKey*)
1020 Editor::clamp_verbose_cursor_x (double x)
1025 x = min (_visible_canvas_width - 200.0, x);
1031 Editor::clamp_verbose_cursor_y (double y)
1034 y = min (_visible_canvas_height - 50, y);
1038 ArdourCanvas::GtkCanvasViewport*
1039 Editor::get_track_canvas() const
1041 return _track_canvas_viewport;
1045 Editor::get_canvas_cursor () const
1047 /* The top of the cursor stack is always the currently visible cursor. */
1048 return _cursor_stack.back();
1052 Editor::set_canvas_cursor (Gdk::Cursor* cursor)
1054 Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
1056 if (win && !_cursors->is_invalid (cursor)) {
1057 /* glibmm 2.4 doesn't allow null cursor pointer because it uses
1058 a Gdk::Cursor& as the argument to Gdk::Window::set_cursor().
1059 But a null pointer just means "use parent window cursor",
1060 and so should be allowed. Gtkmm 3.x has fixed this API.
1062 For now, drop down and use C API
1064 gdk_window_set_cursor (win->gobj(), cursor ? cursor->gobj() : 0);
1069 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
1071 if (!_cursors->is_invalid (cursor)) {
1072 _cursor_stack.push_back (cursor);
1073 set_canvas_cursor (cursor);
1075 return _cursor_stack.size() - 1;
1079 Editor::pop_canvas_cursor ()
1082 if (_cursor_stack.size() <= 1) {
1083 PBD::error << "attempt to pop default cursor" << endmsg;
1087 _cursor_stack.pop_back();
1088 if (_cursor_stack.back()) {
1089 /* Popped to an existing cursor, we're done. Otherwise, the
1090 context that created this cursor has been destroyed, so we need
1091 to skip to the next down the stack. */
1092 set_canvas_cursor (_cursor_stack.back());
1099 Editor::which_grabber_cursor () const
1101 Gdk::Cursor* c = _cursors->grabber;
1103 switch (_edit_point) {
1105 c = _cursors->grabber_edit_point;
1108 boost::shared_ptr<Movable> m = _movable.lock();
1109 if (m && m->locked()) {
1110 c = _cursors->speaker;
1119 Editor::which_trim_cursor (bool left) const
1121 if (!entered_regionview) {
1125 Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
1129 if (ct & Trimmable::FrontTrimEarlier) {
1130 return _cursors->left_side_trim;
1132 return _cursors->left_side_trim_right_only;
1135 if (ct & Trimmable::EndTrimLater) {
1136 return _cursors->right_side_trim;
1138 return _cursors->right_side_trim_left_only;
1144 Editor::which_mode_cursor () const
1146 Gdk::Cursor* mode_cursor = MouseCursors::invalid_cursor ();
1148 switch (mouse_mode) {
1150 mode_cursor = _cursors->selector;
1154 mode_cursor = _cursors->scissors;
1159 /* don't use mode cursor, pick a grabber cursor based on the item */
1163 mode_cursor = _cursors->midi_pencil;
1167 mode_cursor = _cursors->time_fx; // just use playhead
1171 mode_cursor = _cursors->speaker;
1175 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
1176 if (get_smart_mode()) {
1179 get_pointer_position (x, y);
1181 if (x >= 0 && y >= 0) {
1183 vector<ArdourCanvas::Item const *> items;
1185 /* Note how we choose a specific scroll group to get
1186 * items from. This could be problematic.
1189 hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
1191 // first item will be the upper most
1193 if (!items.empty()) {
1194 const ArdourCanvas::Item* i = items.front();
1196 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
1197 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
1198 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
1199 mode_cursor = _cursors->up_down;
1210 Editor::which_track_cursor () const
1212 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
1214 switch (_join_object_range_state) {
1215 case JOIN_OBJECT_RANGE_NONE:
1216 case JOIN_OBJECT_RANGE_OBJECT:
1217 cursor = which_grabber_cursor ();
1219 case JOIN_OBJECT_RANGE_RANGE:
1220 cursor = _cursors->selector;
1228 Editor::which_canvas_cursor(ItemType type) const
1230 Gdk::Cursor* cursor = which_mode_cursor ();
1232 if (mouse_mode == MouseRange) {
1234 case StartSelectionTrimItem:
1235 cursor = _cursors->left_side_trim;
1237 case EndSelectionTrimItem:
1238 cursor = _cursors->right_side_trim;
1245 if ((mouse_mode == MouseObject || get_smart_mode ()) ||
1246 mouse_mode == MouseContent) {
1248 /* find correct cursor to use in object/smart mode */
1252 /* We don't choose a cursor for these items on top of a region view,
1253 because this would push a new context on the enter stack which
1254 means switching the region context for things like smart mode
1255 won't actualy change the cursor. */
1256 // case RegionViewNameHighlight:
1257 // case RegionViewName:
1260 case AutomationTrackItem:
1261 cursor = which_track_cursor ();
1263 case PlayheadCursorItem:
1264 switch (_edit_point) {
1266 cursor = _cursors->grabber_edit_point;
1269 cursor = _cursors->grabber;
1274 cursor = _cursors->selector;
1276 case ControlPointItem:
1277 cursor = _cursors->fader;
1280 cursor = _cursors->cross_hair;
1282 case AutomationLineItem:
1283 cursor = _cursors->cross_hair;
1285 case StartSelectionTrimItem:
1286 cursor = _cursors->left_side_trim;
1288 case EndSelectionTrimItem:
1289 cursor = _cursors->right_side_trim;
1292 cursor = _cursors->fade_in;
1294 case FadeInHandleItem:
1295 cursor = _cursors->fade_in;
1297 case FadeInTrimHandleItem:
1298 cursor = _cursors->fade_in;
1301 cursor = _cursors->fade_out;
1303 case FadeOutHandleItem:
1304 cursor = _cursors->fade_out;
1306 case FadeOutTrimHandleItem:
1307 cursor = _cursors->fade_out;
1309 case FeatureLineItem:
1310 cursor = _cursors->cross_hair;
1312 case LeftFrameHandle:
1313 if ( effective_mouse_mode() == MouseObject ) // (smart mode): if the user is in the btm half, show the trim cursor
1314 cursor = which_trim_cursor (true);
1316 cursor = _cursors->selector; // (smart mode): in the top half, just show the selection (range) cursor
1318 case RightFrameHandle:
1319 if ( effective_mouse_mode() == MouseObject ) //see above
1320 cursor = which_trim_cursor (false);
1322 cursor = _cursors->selector;
1324 case StartCrossFadeItem:
1325 cursor = _cursors->fade_in;
1327 case EndCrossFadeItem:
1328 cursor = _cursors->fade_out;
1330 case CrossfadeViewItem:
1331 cursor = _cursors->cross_hair;
1334 cursor = _cursors->grabber_note;
1339 } else if (mouse_mode == MouseDraw) {
1341 /* ControlPointItem is not really specific to region gain mode
1342 but it is the same cursor so don't worry about this for now.
1343 The result is that we'll see the fader cursor if we enter
1344 non-region-gain-line control points while in MouseDraw
1345 mode, even though we can't edit them in this mode.
1350 case ControlPointItem:
1351 cursor = _cursors->fader;
1354 cursor = _cursors->grabber_note;
1361 /* These items use the timebar cursor at all times */
1362 case TimecodeRulerItem:
1363 case MinsecRulerItem:
1365 case SamplesRulerItem:
1366 cursor = _cursors->timebar;
1369 /* These items use the grabber cursor at all times */
1370 case MeterMarkerItem:
1371 case TempoMarkerItem:
1376 case RangeMarkerBarItem:
1377 case CdMarkerBarItem:
1379 case TransportMarkerBarItem:
1381 cursor = which_grabber_cursor();
1392 Editor::choose_canvas_cursor_on_entry (ItemType type)
1394 if (_drags->active()) {
1398 Gdk::Cursor* cursor = which_canvas_cursor(type);
1400 if (!_cursors->is_invalid (cursor)) {
1401 // Push a new enter context
1402 const EnterContext ctx = { type, CursorContext::create(*this, cursor) };
1403 _enter_stack.push_back(ctx);
1408 Editor::update_all_enter_cursors ()
1410 for (std::vector<EnterContext>::iterator i = _enter_stack.begin(); i != _enter_stack.end(); ++i) {
1411 i->cursor_ctx->change(which_canvas_cursor(i->item_type));
1416 Editor::trackviews_height() const
1418 if (!_trackview_group) {
1422 return _visible_canvas_height - _trackview_group->canvas_origin().y;