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/utils.h"
36 #include "canvas/debug.h"
38 #include "ardour_ui.h"
39 #include "automation_time_axis.h"
41 #include "global_signals.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 "verbose_cursor.h"
59 using namespace ARDOUR;
60 using namespace ARDOUR_UI_UTILS;
64 using namespace Gtkmm2ext;
65 using namespace Editing;
68 Editor::initialize_canvas ()
70 _track_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, vertical_adjustment);
71 _track_canvas = _track_canvas_viewport->canvas ();
73 /* scroll group for items that should not automatically scroll
74 * (e.g verbose cursor). It shares the canvas coordinate space.
76 no_scroll_group = new ArdourCanvas::Container (_track_canvas->root());
78 ArdourCanvas::ScrollGroup* hsg;
79 ArdourCanvas::ScrollGroup* hg;
80 ArdourCanvas::ScrollGroup* vg;
82 hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_track_canvas->root(),
83 ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically|
84 ArdourCanvas::ScrollGroup::ScrollsHorizontally));
85 CANVAS_DEBUG_NAME (hv_scroll_group, "canvas hv scroll");
86 _track_canvas->add_scroller (*hsg);
88 v_scroll_group = vg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsVertically);
89 CANVAS_DEBUG_NAME (v_scroll_group, "canvas v scroll");
90 _track_canvas->add_scroller (*vg);
92 h_scroll_group = hg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
93 CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll");
94 _track_canvas->add_scroller (*hg);
96 _verbose_cursor = new VerboseCursor (this);
98 /* on the bottom, an image */
100 if (Profile->get_sae()) {
101 Image img (::get_icon (X_("saelogo")));
102 // logo_item = new ArdourCanvas::Pixbuf (_track_canvas->root(), 0.0, 0.0, img.get_pixbuf());
103 // logo_item->property_height_in_pixels() = true;
104 // logo_item->property_width_in_pixels() = true;
105 // logo_item->property_height_set() = true;
106 // logo_item->property_width_set() = true;
107 // logo_item->show ();
110 /*a group to hold global rects like punch/loop indicators */
111 global_rect_group = new ArdourCanvas::Container (hv_scroll_group);
112 CANVAS_DEBUG_NAME (global_rect_group, "global rect group");
114 transport_loop_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
115 CANVAS_DEBUG_NAME (transport_loop_range_rect, "loop rect");
116 transport_loop_range_rect->hide();
118 transport_punch_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
119 CANVAS_DEBUG_NAME (transport_punch_range_rect, "punch rect");
120 transport_punch_range_rect->hide();
122 /*a group to hold time (measure) lines */
123 time_line_group = new ArdourCanvas::Container (hv_scroll_group);
124 CANVAS_DEBUG_NAME (time_line_group, "time line group");
126 _trackview_group = new ArdourCanvas::Container (hv_scroll_group);
127 CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
129 // used to show zoom mode active zooming
130 zoom_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
132 zoom_rect->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_zoom_rect_event), (ArdourCanvas::Item*) 0));
134 // used as rubberband rect
135 rubberband_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
136 rubberband_rect->hide();
138 /* a group to hold stuff while it gets dragged around. Must be the
139 * uppermost (last) group with hv_scroll_group as a parent
141 _drag_motion_group = new ArdourCanvas::Container (hv_scroll_group);
142 CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion");
144 /* TIME BAR CANVAS */
146 /* this group is part of the canvas "top-level" hscroll group. This group
147 responds only to horizontal scroll, so vertical scrolling does not move
151 _time_markers_group = new ArdourCanvas::Container (h_scroll_group);
152 CANVAS_DEBUG_NAME (_time_markers_group, "time bars");
154 /* items (containers) in the time_markers_group. Each group holds a background rectangle ("bar"),
155 and may then also hold zero or more markers of various kinds, depending on the state of the
156 session (does it have any locations defined?) and visibility options. In some cases, eg. the ruler
157 group, it may hold other kinds of items also (e.g. a ruler).
160 cd_marker_group = new ArdourCanvas::Container (_time_markers_group);
161 CANVAS_DEBUG_NAME (cd_marker_group, "cd marker group");
162 videotl_group = new ArdourCanvas::Container (_time_markers_group);
163 CANVAS_DEBUG_NAME (videotl_group, "videotl group");
164 marker_group = new ArdourCanvas::Container (_time_markers_group);
165 CANVAS_DEBUG_NAME (marker_group, "marker group");
166 transport_marker_group = new ArdourCanvas::Container (_time_markers_group);
167 CANVAS_DEBUG_NAME (transport_marker_group, "transport marker group");
168 skip_group = new ArdourCanvas::Container (_time_markers_group);
169 CANVAS_DEBUG_NAME (skip_group, "skip group");
170 range_marker_group = new ArdourCanvas::Container (_time_markers_group);
171 CANVAS_DEBUG_NAME (range_marker_group, "range marker group");
172 tempo_group = new ArdourCanvas::Container (_time_markers_group);
173 CANVAS_DEBUG_NAME (tempo_group, "tempo group");
174 meter_group = new ArdourCanvas::Container (_time_markers_group);
175 CANVAS_DEBUG_NAME (meter_group, "meter group");
176 ruler_group = new ArdourCanvas::Container (_time_markers_group);
177 CANVAS_DEBUG_NAME (ruler_group, "ruler group");
179 /* bars (background rectangles for each kind of marker/ruler */
181 punch_loop_bar = new ArdourCanvas::Rectangle (ruler_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, ruler_height));
182 CANVAS_DEBUG_NAME (punch_loop_bar, "punch/loop Bar");
183 /* not outlined, so that there is no gap between it and the ruler in the same group */
184 punch_loop_bar->set_outline (false);
186 skip_bar = new ArdourCanvas::Rectangle (skip_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, skipbar_height));
187 CANVAS_DEBUG_NAME (skip_bar, "skip Bar");
188 skip_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
190 marker_bar = new ArdourCanvas::Rectangle (marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, marker_height));
191 CANVAS_DEBUG_NAME (marker_bar, "Marker Bar");
192 marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
194 /* Rectangles displayed on the bars during drag operations */
196 skip_drag_rect = new ArdourCanvas::Rectangle (skip_group, ArdourCanvas::Rect (0.0, 0.0, 100, skipbar_height));
197 CANVAS_DEBUG_NAME (skip_drag_rect, "skip drag");
198 skip_drag_rect->set_outline (false);
199 skip_drag_rect->hide ();
201 range_bar_drag_rect = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, marker_height));
202 CANVAS_DEBUG_NAME (range_bar_drag_rect, "range drag");
203 range_bar_drag_rect->set_outline (false);
204 range_bar_drag_rect->hide ();
206 /* drag bar for ruler is double height because it spans loop bar and the ruler */
207 transport_bar_drag_rect = new ArdourCanvas::Rectangle (ruler_group, ArdourCanvas::Rect (0.0, 0.0, 100, ruler_height + loopbar_height));
208 CANVAS_DEBUG_NAME (transport_bar_drag_rect, "transport drag");
209 transport_bar_drag_rect->set_outline (false);
210 transport_bar_drag_rect->hide ();
212 /* the following bars and rects are not used in Tracks Live */
214 meter_bar = new ArdourCanvas::Rectangle (meter_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
215 CANVAS_DEBUG_NAME (meter_bar, "meter Bar");
216 meter_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
218 tempo_bar = new ArdourCanvas::Rectangle (tempo_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
219 CANVAS_DEBUG_NAME (tempo_bar, "Tempo Bar");
220 tempo_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
222 range_marker_bar = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
223 CANVAS_DEBUG_NAME (range_marker_bar, "Range Marker Bar");
224 range_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
226 cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
227 CANVAS_DEBUG_NAME (cd_marker_bar, "CD Marker Bar");
228 cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
230 ARDOUR_UI::instance()->video_timeline = new VideoTimeLine(this, videotl_group, (timebar_height * videotl_bar_height));
232 cd_marker_bar_drag_rect = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
233 CANVAS_DEBUG_NAME (cd_marker_bar_drag_rect, "cd marker drag");
234 cd_marker_bar_drag_rect->set_outline (false);
235 cd_marker_bar_drag_rect->hide ();
237 /* end of bars + rects */
239 transport_punchin_line = new ArdourCanvas::Line (hv_scroll_group);
240 transport_punchin_line->set_x0 (0);
241 transport_punchin_line->set_y0 (0);
242 transport_punchin_line->set_x1 (0);
243 transport_punchin_line->set_y1 (ArdourCanvas::COORD_MAX);
244 transport_punchin_line->hide ();
246 transport_punchout_line = new ArdourCanvas::Line (hv_scroll_group);
247 transport_punchout_line->set_x0 (0);
248 transport_punchout_line->set_y0 (0);
249 transport_punchout_line->set_x1 (0);
250 transport_punchout_line->set_y1 (ArdourCanvas::COORD_MAX);
251 transport_punchout_line->hide();
253 tempo_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_tempo_bar_event), tempo_bar));
254 meter_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_meter_bar_event), meter_bar));
255 marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_marker_bar_event), marker_bar));
256 cd_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_cd_marker_bar_event), cd_marker_bar));
257 videotl_group->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_videotl_bar_event), videotl_group));
258 range_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_range_marker_bar_event), range_marker_bar));
259 punch_loop_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_punch_loop_bar_event), punch_loop_bar));
260 skip_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_skip_bar_event), skip_bar));
262 playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event);
265 logo_item->lower_to_bottom ();
269 _canvas_drop_zone = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, 0.0));
270 /* this thing is transparent */
271 _canvas_drop_zone->set_fill (false);
272 _canvas_drop_zone->set_outline (false);
273 _canvas_drop_zone->Event.connect (sigc::mem_fun (*this, &Editor::canvas_drop_zone_event));
275 /* these signals will initially be delivered to the canvas itself, but if they end up remaining unhandled, they are passed to Editor-level
279 _track_canvas->signal_scroll_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_scroll_event), true));
280 _track_canvas->signal_motion_notify_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_motion_notify_event));
281 _track_canvas->signal_button_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_press_event));
282 _track_canvas->signal_button_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_release_event));
283 _track_canvas->signal_drag_motion().connect (sigc::mem_fun (*this, &Editor::track_canvas_drag_motion));
284 _track_canvas->signal_key_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_press));
285 _track_canvas->signal_key_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_release));
287 _track_canvas->set_name ("EditorMainCanvas");
288 _track_canvas->add_events (Gdk::POINTER_MOTION_HINT_MASK | Gdk::SCROLL_MASK | Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
289 _track_canvas->signal_leave_notify_event().connect (sigc::mem_fun(*this, &Editor::left_track_canvas), false);
290 _track_canvas->signal_enter_notify_event().connect (sigc::mem_fun(*this, &Editor::entered_track_canvas), false);
291 _track_canvas->set_flags (CAN_FOCUS);
293 /* set up drag-n-drop */
295 vector<TargetEntry> target_table;
297 // Drag-N-Drop from the region list can generate this target
298 target_table.push_back (TargetEntry ("regions"));
300 target_table.push_back (TargetEntry ("text/plain"));
301 target_table.push_back (TargetEntry ("text/uri-list"));
302 target_table.push_back (TargetEntry ("application/x-rootwin-drop"));
304 _track_canvas->drag_dest_set (target_table);
305 _track_canvas->signal_drag_data_received().connect (sigc::mem_fun(*this, &Editor::track_canvas_drag_data_received));
307 _track_canvas_viewport->signal_size_allocate().connect (sigc::mem_fun(*this, &Editor::track_canvas_viewport_allocate));
309 initialize_rulers ();
311 ColorsChanged.connect (sigc::mem_fun (*this, &Editor::color_handler));
317 Editor::track_canvas_viewport_allocate (Gtk::Allocation alloc)
319 _canvas_viewport_allocation = alloc;
320 track_canvas_viewport_size_allocated ();
324 Editor::update_horizontal_adjustment_limits ()
330 double lower_limit = sample_to_pixel(_session->locations()->session_range_location()->start() );
331 horizontal_adjustment.set_lower(lower_limit);
333 double session_end_marker_position = sample_to_pixel(_session->locations()->session_range_location()->end() );
334 double upper_limit = max (lower_limit + _visible_canvas_width, session_end_marker_position);
336 horizontal_adjustment.set_upper(upper_limit + 10); // 3 pixels offset
340 Editor::track_canvas_viewport_size_allocated ()
342 bool height_changed = _visible_canvas_height != _canvas_viewport_allocation.get_height();
344 bool width_changed = _visible_canvas_width != _canvas_viewport_allocation.get_width();
346 _visible_canvas_width = _canvas_viewport_allocation.get_width ();
347 _visible_canvas_height = _canvas_viewport_allocation.get_height ();
349 _canvas_drop_zone->set_y1 (_canvas_drop_zone->y0() + (_visible_canvas_height - 20.0));
353 if (height_changed) {
355 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
356 i->second->canvas_height_set (_visible_canvas_height);
359 vertical_adjustment.set_page_size (_visible_canvas_height);
360 vertical_adjustment.set_page_increment (_visible_canvas_height/2);
361 vertical_adjustment.set_step_increment (_visible_canvas_height/10);
363 if ((vertical_adjustment.get_value() + _visible_canvas_height) >= vertical_adjustment.get_upper()) {
365 We're increasing the size of the canvas while the bottom is visible.
366 We scroll down to keep in step with the controls layout.
368 vertical_adjustment.set_value (_full_canvas_height - _visible_canvas_height);
371 set_visible_track_count (_visible_track_count);
375 horizontal_adjustment.set_page_size (_visible_canvas_width);
376 horizontal_adjustment.set_page_increment (_visible_canvas_width/2);
377 horizontal_adjustment.set_step_increment (_visible_canvas_width/10);
379 // set adjustment horizontal value
380 update_horizontal_adjustment_limits ();
382 if ((horizontal_adjustment.get_value() + _visible_canvas_width) >= horizontal_adjustment.get_upper()) {
384 horizontal_adjustment.set_value (horizontal_adjustment.get_upper() - _visible_canvas_width);
388 update_fixed_rulers();
389 redisplay_tempo (false);
390 _summary->set_overlays_dirty ();
394 Editor::reset_controls_layout_width ()
399 edit_controls_vbox.size_request (req);
402 if (_group_tabs->is_mapped()) {
403 _group_tabs->size_request (req);
407 /* the controls layout has no horizontal scrolling, its visible
408 width is always equal to the total width of its contents.
411 controls_layout.property_width() = w;
412 controls_layout.property_width_request() = w;
416 Editor::reset_controls_layout_height (int32_t h)
418 /* ensure that the rect that represents the "bottom" of the canvas
419 * (the drag-n-drop zone) is, in fact, at the bottom.
422 _canvas_drop_zone->set_position (ArdourCanvas::Duple (0, h));
424 /* track controls layout must span the full height of "h" (all tracks)
425 * plus the bottom rect.
428 h += _canvas_drop_zone->height ();
430 /* set the height of the scrollable area (i.e. the sum of all contained widgets)
431 * for the controls layout. The size request is set elsewhere.
434 controls_layout.property_height() = h;
439 Editor::track_canvas_map_handler (GdkEventAny* /*ev*/)
441 if (current_canvas_cursor) {
442 set_canvas_cursor (current_canvas_cursor);
447 /** This is called when something is dropped onto the track canvas */
449 Editor::track_canvas_drag_data_received (const RefPtr<Gdk::DragContext>& context,
451 const SelectionData& data,
452 guint info, guint time)
454 if (data.get_target() == "regions") {
455 drop_regions (context, x, y, data, info, time);
457 drop_paths (context, x, y, data, info, time);
462 Editor::idle_drop_paths (vector<string> paths, framepos_t frame, double ypos, bool copy)
464 drop_paths_part_two (paths, frame, ypos, copy);
469 Editor::drop_paths_part_two (const vector<string>& paths, framepos_t frame, double ypos, bool copy)
471 RouteTimeAxisView* tv;
473 /* MIDI files must always be imported, because we consider them
474 * writable. So split paths into two vectors, and follow the import
475 * path on the MIDI part.
478 vector<string> midi_paths;
479 vector<string> audio_paths;
481 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
482 if (SMFSource::safe_midi_file_extension (*i)) {
483 midi_paths.push_back (*i);
485 audio_paths.push_back (*i);
490 std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (ypos, false);
491 if (tvp.first == 0) {
493 /* drop onto canvas background: create new tracks */
497 do_import (midi_paths, Editing::ImportDistinctFiles, ImportAsTrack, SrcBest, frame);
499 if (Profile->get_sae() || Config->get_only_copy_imported_files() || copy) {
500 do_import (audio_paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack, SrcBest, frame);
502 do_embed (audio_paths, Editing::ImportDistinctFiles, ImportAsTrack, frame);
505 } else if ((tv = dynamic_cast<RouteTimeAxisView*> (tvp.first)) != 0) {
507 /* check that its a track, not a bus */
510 /* select the track, then embed/import */
513 do_import (midi_paths, Editing::ImportSerializeFiles, ImportToTrack, SrcBest, frame);
515 if (Profile->get_sae() || Config->get_only_copy_imported_files() || copy) {
516 do_import (audio_paths, Editing::ImportSerializeFiles, Editing::ImportToTrack, SrcBest, frame);
518 do_embed (audio_paths, Editing::ImportSerializeFiles, ImportToTrack, frame);
525 Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
527 const SelectionData& data,
528 guint info, guint time)
530 vector<string> paths;
535 if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
537 /* D-n-D coordinates are window-relative, so convert to canvas coordinates
540 ev.type = GDK_BUTTON_RELEASE;
544 frame = window_event_sample (&ev, 0, &cy);
548 bool copy = ((context->get_actions() & (Gdk::ACTION_COPY | Gdk::ACTION_LINK | Gdk::ACTION_MOVE)) == Gdk::ACTION_COPY);
550 /* We are not allowed to call recursive main event loops from within
551 the main event loop with GTK/Quartz. Since import/embed wants
552 to push up a progress dialog, defer all this till we go idle.
554 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun (*this, &Editor::idle_drop_paths), paths, frame, cy, copy));
556 drop_paths_part_two (paths, frame, cy, copy);
560 context->drag_finish (true, false, time);
563 /** @param allow_horiz true to allow horizontal autoscroll, otherwise false.
565 * @param allow_vert true to allow vertical autoscroll, otherwise false.
569 Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
571 if (!Config->get_autoscroll_editor () || autoscroll_active ()) {
575 /* define a rectangular boundary for scrolling. If the mouse moves
576 * outside of this area and/or continue to be outside of this area,
577 * then we will continuously auto-scroll the canvas in the appropriate
580 * the boundary is defined in coordinates relative to the toplevel
581 * window since that is what we're going to call ::get_pointer() on
582 * during autoscrolling to determine if we're still outside the
586 ArdourCanvas::Rect scrolling_boundary;
587 Gtk::Allocation alloc;
591 alloc = controls_layout.get_allocation ();
593 alloc = _track_canvas_viewport->get_allocation ();
597 /* reduce height by the height of the timebars, which happens
598 to correspond to the position of the hv_scroll_group.
601 alloc.set_height (alloc.get_height() - hv_scroll_group->position().y);
602 alloc.set_y (alloc.get_y() + hv_scroll_group->position().y);
604 /* now reduce it again so that we start autoscrolling before we
605 * move off the top or bottom of the canvas
608 alloc.set_height (alloc.get_height() - 20);
609 alloc.set_y (alloc.get_y() + 10);
611 /* the effective width of the autoscroll boundary so
612 that we start scrolling before we hit the edge.
614 this helps when the window is slammed up against the
615 right edge of the screen, making it hard to scroll
619 if (alloc.get_width() > 20) {
620 alloc.set_width (alloc.get_width() - 20);
621 alloc.set_x (alloc.get_x() + 10);
626 scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), alloc.get_x() + alloc.get_width(), alloc.get_y() + alloc.get_height());
629 Gdk::ModifierType mask;
631 get_window()->get_pointer (x, y, mask);
633 if ((allow_horiz && ((x < scrolling_boundary.x0 && leftmost_frame > 0) || x >= scrolling_boundary.x1)) ||
634 (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) {
635 start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary);
640 Editor::start_autoscroll_for_headers ()
642 if (autoscroll_active () ) {
646 Gtk::Allocation alloc = controls_layout.get_allocation ();
647 ArdourCanvas::Rect scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), alloc.get_x() + alloc.get_width(), alloc.get_y() + alloc.get_height());
649 start_canvas_autoscroll (false/*horizontal disabled*/, true/*vertical enabled*/, scrolling_boundary);
653 Editor::autoscroll_active () const
655 return autoscroll_connection.connected ();
659 Editor::autoscroll_canvas ()
662 Gdk::ModifierType mask;
663 frameoffset_t dx = 0;
664 bool no_stop = false;
665 bool y_motion = false;
667 get_window()->get_pointer (x, y, mask);
670 bool vertical_motion = false;
672 if (autoscroll_horizontal_allowed) {
674 framepos_t new_frame = leftmost_frame;
678 if (x > autoscroll_boundary.x1) {
680 /* bring it back into view */
681 dx = x - autoscroll_boundary.x1;
682 dx += 10 + (2 * (autoscroll_cnt/2));
684 dx = pixel_to_sample (dx);
686 if (leftmost_frame < max_framepos - dx) {
687 new_frame = leftmost_frame + dx;
689 new_frame = max_framepos;
694 } else if (x < autoscroll_boundary.x0) {
696 dx = autoscroll_boundary.x0 - x;
697 dx += 10 + (2 * (autoscroll_cnt/2));
699 dx = pixel_to_sample (dx);
701 if (leftmost_frame >= dx) {
702 new_frame = leftmost_frame - dx;
710 if (new_frame != leftmost_frame) {
711 vc.time_origin = new_frame;
712 vc.add (VisualChange::TimeOrigin);
716 if (autoscroll_vertical_allowed) {
718 // const double vertical_pos = vertical_adjustment.get_value();
719 const int speed_factor = 10;
723 if (y < autoscroll_boundary.y0) {
725 /* scroll to make higher tracks visible */
727 if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
728 y_motion = scroll_up_one_track ();
729 vertical_motion = true;
732 } else if (y > autoscroll_boundary.y1) {
734 if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
735 y_motion = scroll_down_one_track ();
736 vertical_motion = true;
743 if (vc.pending || vertical_motion) {
745 /* change horizontal first */
751 /* now send a motion event to notify anyone who cares
752 that we have moved to a new location (because we scrolled)
757 ev.type = GDK_MOTION_NOTIFY;
758 ev.state = Gdk::BUTTON1_MASK;
760 /* the motion handler expects events in canvas coordinate space */
762 /* we asked for the mouse position above (::get_pointer()) via
763 * our own top level window (we being the Editor). Convert into
764 * coordinates within the canvas window.
770 translate_coordinates (*_track_canvas, x, y, cx, cy);
772 /* clamp x and y to remain within the autoscroll boundary,
773 * which is defined in window coordinates
776 x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1);
777 y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1);
779 /* now convert from Editor window coordinates to canvas
783 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
787 motion_handler (0, (GdkEvent*) &ev, true);
789 } else if (no_stop) {
791 /* not changing visual state but pointer is outside the scrolling boundary
792 * so we still need to deliver a fake motion event
797 ev.type = GDK_MOTION_NOTIFY;
798 ev.state = Gdk::BUTTON1_MASK;
800 /* the motion handler expects events in canvas coordinate space */
802 /* first convert from Editor window coordinates to canvas
809 /* clamp x and y to remain within the visible area. except
810 * .. if horizontal scrolling is allowed, always allow us to
814 if (autoscroll_horizontal_allowed) {
815 x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1);
817 x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
819 y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
821 translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
823 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
827 motion_handler (0, (GdkEvent*) &ev, true);
830 stop_canvas_autoscroll ();
836 return true; /* call me again */
840 Editor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary)
846 stop_canvas_autoscroll ();
849 autoscroll_horizontal_allowed = allow_horiz;
850 autoscroll_vertical_allowed = allow_vert;
851 autoscroll_boundary = boundary;
853 /* do the first scroll right now
856 autoscroll_canvas ();
858 /* scroll again at very very roughly 30FPS */
860 autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::autoscroll_canvas), 30);
864 Editor::stop_canvas_autoscroll ()
866 autoscroll_connection.disconnect ();
870 Editor::left_track_canvas (GdkEventCrossing */*ev*/)
873 within_track_canvas = false;
874 set_entered_track (0);
875 set_entered_regionview (0);
876 reset_canvas_action_sensitivity (false);
881 Editor::entered_track_canvas (GdkEventCrossing */*ev*/)
883 within_track_canvas = true;
884 reset_canvas_action_sensitivity (true);
889 Editor::ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top)
891 if (track.hidden()) {
895 /* compute visible area of trackview group, as offsets from top of
899 double const current_view_min_y = vertical_adjustment.get_value();
900 double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size();
902 double const track_min_y = track.y_position ();
903 double const track_max_y = track.y_position () + track.effective_height ();
906 (track_min_y >= current_view_min_y &&
907 track_max_y < current_view_max_y)) {
908 /* already visible, and caller did not ask to place it at the
909 * top of the track canvas
917 new_value = track_min_y;
919 if (track_min_y < current_view_min_y) {
920 // Track is above the current view
921 new_value = track_min_y;
922 } else if (track_max_y > current_view_max_y) {
923 // Track is below the current view
924 new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size();
926 new_value = track_min_y;
930 vertical_adjustment.set_value(new_value);
933 /** Called when the main vertical_adjustment has changed */
935 Editor::tie_vertical_scrolling ()
937 if (pending_visual_change.idle_handler_id < 0) {
938 _summary->set_overlays_dirty ();
943 Editor::tie_horizontal_scrolling ()
945 double p = horizontal_adjustment.get_value ();
947 leftmost_frame = (framepos_t) floor (p * samples_per_pixel);
949 update_fixed_rulers ();
950 redisplay_tempo (true);
952 if (pending_visual_change.idle_handler_id < 0) {
953 _summary->set_overlays_dirty ();
956 update_video_timeline();
960 Editor::color_handler()
962 ArdourCanvas::Color base = ARDOUR_UI::config()->get_canvasvar_RulerBase();
963 ArdourCanvas::Color text = ARDOUR_UI::config()->get_canvasvar_RulerText();
965 clock_ruler->set_fill_color (base);
966 clock_ruler->set_outline_color (text);
968 playhead_cursor->set_color (ARDOUR_UI::config()->get_canvasvar_PlayHead());
970 meter_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_MeterBar());
971 meter_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
973 tempo_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TempoBar());
974 tempo_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
976 marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_MarkerBar());
977 marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
979 cd_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_CDMarkerBar());
980 cd_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
982 range_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeMarkerBar());
983 range_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
985 /* same color as rulers */
986 punch_loop_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RulerBase());
988 skip_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SkipBar());
989 skip_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
991 cd_marker_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
992 cd_marker_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
994 range_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
995 range_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
997 skip_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SkipDragBarRect());
998 skip_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_SkipDragBarRect());
1000 transport_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportDragRect());
1001 transport_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportDragRect());
1003 transport_loop_range_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportLoopRect());
1004 transport_loop_range_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportLoopRect());
1006 transport_punch_range_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportPunchRect());
1007 transport_punch_range_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportPunchRect());
1009 transport_punchin_line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_PunchLine());
1010 transport_punchout_line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_PunchLine());
1012 zoom_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ZoomRect());
1013 zoom_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZoomRect());
1015 rubberband_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RubberBandRect());
1016 rubberband_rect->set_fill_color ((guint32) ARDOUR_UI::config()->get_canvasvar_RubberBandRect());
1018 refresh_location_display ();
1020 redisplay_tempo (true);
1023 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
1028 Editor::horizontal_position () const
1030 return sample_to_pixel (leftmost_frame);
1034 Editor::track_canvas_key_press (GdkEventKey*)
1036 /* XXX: event does not report the modifier key pressed down, AFAICS, so use the Keyboard object instead */
1037 if (mouse_mode == Editing::MouseZoom && Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
1038 set_canvas_cursor (_cursors->zoom_out, true);
1045 Editor::track_canvas_key_release (GdkEventKey*)
1047 if (mouse_mode == Editing::MouseZoom && !Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
1048 set_canvas_cursor (_cursors->zoom_in, true);
1055 Editor::clamp_verbose_cursor_x (double x)
1060 x = min (_visible_canvas_width - 200.0, x);
1066 Editor::clamp_verbose_cursor_y (double y)
1069 y = min (_visible_canvas_height - 50, y);
1073 ArdourCanvas::GtkCanvasViewport*
1074 Editor::get_track_canvas() const
1076 return _track_canvas_viewport;
1080 Editor::set_canvas_cursor (Gdk::Cursor* cursor, bool save)
1083 current_canvas_cursor = cursor;
1086 Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
1088 if (win && cursor) {
1089 win->set_cursor (*cursor);
1094 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
1097 _cursor_stack.push (cursor);
1098 set_canvas_cursor (cursor, false);
1103 Editor::pop_canvas_cursor ()
1105 if (!_cursor_stack.empty()) {
1106 Gdk::Cursor* cursor = _cursor_stack.top ();
1107 _cursor_stack.pop ();
1108 set_canvas_cursor (cursor, false);
1113 Editor::which_grabber_cursor () const
1115 Gdk::Cursor* c = _cursors->grabber;
1117 if (_internal_editing) {
1118 switch (mouse_mode) {
1120 c = _cursors->midi_pencil;
1124 c = _cursors->grabber_note;
1128 c = _cursors->midi_resize;
1132 c = _cursors->grabber_note;
1141 switch (_edit_point) {
1143 c = _cursors->grabber_edit_point;
1146 boost::shared_ptr<Movable> m = _movable.lock();
1147 if (m && m->locked()) {
1148 c = _cursors->speaker;
1158 Editor::which_trim_cursor (bool left) const
1160 if (!entered_regionview) {
1164 Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
1168 if (ct & Trimmable::FrontTrimEarlier) {
1169 return _cursors->left_side_trim;
1171 return _cursors->left_side_trim_right_only;
1174 if (ct & Trimmable::EndTrimLater) {
1175 return _cursors->right_side_trim;
1177 return _cursors->right_side_trim_left_only;
1183 Editor::which_mode_cursor () const
1185 Gdk::Cursor* mode_cursor = 0;
1187 switch (mouse_mode) {
1189 mode_cursor = _cursors->selector;
1190 if (_internal_editing) {
1191 mode_cursor = which_grabber_cursor();
1196 mode_cursor = _cursors->scissors;
1200 /* don't use mode cursor, pick a grabber cursor based on the item */
1204 mode_cursor = _cursors->midi_pencil;
1208 mode_cursor = _cursors->cross_hair;
1212 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
1213 mode_cursor = _cursors->zoom_out;
1215 mode_cursor = _cursors->zoom_in;
1220 mode_cursor = _cursors->time_fx; // just use playhead
1224 mode_cursor = _cursors->speaker;
1228 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
1229 if (!_internal_editing && get_smart_mode() ) {
1232 get_pointer_position (x, y);
1234 if (x >= 0 && y >= 0) {
1236 vector<ArdourCanvas::Item const *> items;
1238 /* Note how we choose a specific scroll group to get
1239 * items from. This could be problematic.
1242 hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
1244 // first item will be the upper most
1246 if (!items.empty()) {
1247 const ArdourCanvas::Item* i = items.front();
1249 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
1250 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
1251 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
1252 mode_cursor = _cursors->up_down;
1263 Editor::which_track_cursor () const
1265 Gdk::Cursor* cursor = 0;
1267 assert (mouse_mode == MouseObject || get_smart_mode());
1269 if (!_internal_editing) {
1270 switch (_join_object_range_state) {
1271 case JOIN_OBJECT_RANGE_NONE:
1272 case JOIN_OBJECT_RANGE_OBJECT:
1273 cursor = which_grabber_cursor ();
1275 case JOIN_OBJECT_RANGE_RANGE:
1276 cursor = _cursors->selector;
1285 Editor::reset_canvas_cursor ()
1287 if (!is_drawable()) {
1291 Gdk::Cursor* cursor = which_mode_cursor ();
1294 cursor = which_grabber_cursor ();
1298 set_canvas_cursor (cursor);
1306 Editor::choose_canvas_cursor_on_entry (GdkEventCrossing* /*event*/, ItemType type)
1308 Gdk::Cursor* cursor = 0;
1310 if (_drags->active()) {
1314 cursor = which_mode_cursor ();
1316 if (mouse_mode == MouseObject || get_smart_mode ()) {
1318 /* find correct cursor to use in object/smart mode */
1322 case RegionViewNameHighlight:
1323 case RegionViewName:
1326 case AutomationTrackItem:
1327 cursor = which_track_cursor ();
1329 case PlayheadCursorItem:
1330 switch (_edit_point) {
1332 cursor = _cursors->grabber_edit_point;
1335 cursor = _cursors->grabber;
1339 case ControlPointItem:
1340 cursor = _cursors->fader;
1343 cursor = which_track_cursor ();
1345 case AutomationLineItem:
1346 cursor = _cursors->cross_hair;
1348 case StartSelectionTrimItem:
1349 cursor = _cursors->left_side_trim;
1351 case EndSelectionTrimItem:
1352 cursor = _cursors->right_side_trim;
1355 cursor = _cursors->fade_in;
1357 case FadeInHandleItem:
1358 cursor = _cursors->fade_in;
1360 case FadeInTrimHandleItem:
1361 cursor = _cursors->fade_in;
1364 cursor = _cursors->fade_out;
1366 case FadeOutHandleItem:
1367 cursor = _cursors->fade_out;
1369 case FadeOutTrimHandleItem:
1370 cursor = _cursors->fade_out;
1373 cursor = which_grabber_cursor();
1375 case FeatureLineItem:
1376 cursor = _cursors->cross_hair;
1378 case LeftFrameHandle:
1379 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
1380 cursor = which_trim_cursor (true); //alternatively, one could argue that we _should_ allow trims here, and disallow range selection
1382 case RightFrameHandle:
1383 if ( effective_mouse_mode() == MouseObject ) //see above
1384 cursor = which_trim_cursor (false);
1386 case StartCrossFadeItem:
1387 cursor = _cursors->fade_in;
1389 case EndCrossFadeItem:
1390 cursor = _cursors->fade_out;
1392 case CrossfadeViewItem:
1393 cursor = _cursors->cross_hair;
1399 } else if (mouse_mode == MouseGain) {
1401 /* ControlPointItem is not really specific to region gain mode
1402 but it is the same cursor so don't worry about this for now.
1403 The result is that we'll see the fader cursor if we enter
1404 non-region-gain-line control points while in MouseGain
1405 mode, even though we can't edit them in this mode.
1410 case ControlPointItem:
1411 cursor = _cursors->fader;
1419 /* These items use the timebar cursor at all times */
1420 case TimecodeRulerItem:
1421 case MinsecRulerItem:
1423 case SamplesRulerItem:
1425 cursor = _cursors->timebar;
1428 /* These items use the grabber cursor at all times */
1429 case MeterMarkerItem:
1430 case TempoMarkerItem:
1435 case RangeMarkerBarItem:
1436 case CdMarkerBarItem:
1438 case PunchLoopBarItem:
1441 cursor = which_grabber_cursor();
1449 set_canvas_cursor (cursor, false);
1454 Editor::trackviews_height() const
1456 if (!_trackview_group) {
1460 return _visible_canvas_height - _trackview_group->canvas_origin().y;