2 Copyright (C) 2005 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
24 #include "gtkmm2ext/utils.h"
26 #include "ardour/profile.h"
27 #include "ardour/rc_configuration.h"
28 #include "ardour/smf_source.h"
30 #include "canvas/canvas.h"
31 #include "canvas/rectangle.h"
32 #include "canvas/pixbuf.h"
33 #include "canvas/scroll_group.h"
34 #include "canvas/text.h"
35 #include "canvas/debug.h"
37 #include "ardour_ui.h"
38 #include "automation_time_axis.h"
40 #include "global_signals.h"
42 #include "rgb_macros.h"
44 #include "audio_time_axis.h"
45 #include "editor_drag.h"
46 #include "region_view.h"
47 #include "editor_group_tabs.h"
48 #include "editor_summary.h"
49 #include "video_timeline.h"
51 #include "editor_cursors.h"
52 #include "mouse_cursors.h"
53 #include "verbose_cursor.h"
58 using namespace ARDOUR;
59 using namespace ARDOUR_UI_UTILS;
63 using namespace Gtkmm2ext;
64 using namespace Editing;
67 Editor::initialize_canvas ()
69 _track_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, vertical_adjustment);
70 _track_canvas = _track_canvas_viewport->canvas ();
72 /* scroll group for items that should not automatically scroll
73 * (e.g verbose cursor). It shares the canvas coordinate space.
75 no_scroll_group = new ArdourCanvas::Container (_track_canvas->root());
77 ArdourCanvas::ScrollGroup* hsg;
78 ArdourCanvas::ScrollGroup* hg;
79 ArdourCanvas::ScrollGroup* vg;
81 hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_track_canvas->root(),
82 ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically|
83 ArdourCanvas::ScrollGroup::ScrollsHorizontally));
84 CANVAS_DEBUG_NAME (hv_scroll_group, "canvas hv scroll");
85 _track_canvas->add_scroller (*hsg);
87 v_scroll_group = vg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsVertically);
88 CANVAS_DEBUG_NAME (v_scroll_group, "canvas v scroll");
89 _track_canvas->add_scroller (*vg);
91 h_scroll_group = hg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
92 CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll");
93 _track_canvas->add_scroller (*hg);
95 _verbose_cursor = new VerboseCursor (this);
97 /* on the bottom, an image */
99 if (Profile->get_sae()) {
100 Image img (::get_icon (X_("saelogo")));
101 // logo_item = new ArdourCanvas::Pixbuf (_track_canvas->root(), 0.0, 0.0, img.get_pixbuf());
102 // logo_item->property_height_in_pixels() = true;
103 // logo_item->property_width_in_pixels() = true;
104 // logo_item->property_height_set() = true;
105 // logo_item->property_width_set() = true;
106 // logo_item->show ();
109 /*a group to hold global rects like punch/loop indicators */
110 global_rect_group = new ArdourCanvas::Container (hv_scroll_group);
111 CANVAS_DEBUG_NAME (global_rect_group, "global rect group");
113 transport_loop_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
114 CANVAS_DEBUG_NAME (transport_loop_range_rect, "loop rect");
115 transport_loop_range_rect->hide();
117 transport_punch_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
118 CANVAS_DEBUG_NAME (transport_punch_range_rect, "punch rect");
119 transport_punch_range_rect->hide();
121 /*a group to hold time (measure) lines */
122 time_line_group = new ArdourCanvas::Container (hv_scroll_group);
123 CANVAS_DEBUG_NAME (time_line_group, "time line group");
125 _trackview_group = new ArdourCanvas::Container (hv_scroll_group);
126 CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
128 // used to show zoom mode active zooming
129 zoom_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
131 zoom_rect->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_zoom_rect_event), (ArdourCanvas::Item*) 0));
133 // used as rubberband rect
134 rubberband_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
135 rubberband_rect->hide();
137 /* a group to hold stuff while it gets dragged around. Must be the
138 * uppermost (last) group with hv_scroll_group as a parent
140 _drag_motion_group = new ArdourCanvas::Container (hv_scroll_group);
141 CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion");
143 /* TIME BAR CANVAS */
145 _time_markers_group = new ArdourCanvas::Container (h_scroll_group);
146 CANVAS_DEBUG_NAME (_time_markers_group, "time bars");
148 cd_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, 0.0));
149 CANVAS_DEBUG_NAME (cd_marker_group, "cd marker group");
150 /* the vide is temporarily placed a the same location as the
151 cd_marker_group, but is moved later.
153 videotl_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0));
154 CANVAS_DEBUG_NAME (videotl_group, "videotl group");
155 marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, timebar_height + 1.0));
156 CANVAS_DEBUG_NAME (marker_group, "marker group");
157 transport_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 2.0) + 1.0));
158 CANVAS_DEBUG_NAME (transport_marker_group, "transport marker group");
159 range_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 3.0) + 1.0));
160 CANVAS_DEBUG_NAME (range_marker_group, "range marker group");
161 tempo_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 4.0) + 1.0));
162 CANVAS_DEBUG_NAME (tempo_group, "tempo group");
163 meter_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0));
164 CANVAS_DEBUG_NAME (meter_group, "meter group");
166 meter_bar = new ArdourCanvas::Rectangle (meter_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
167 CANVAS_DEBUG_NAME (meter_bar, "meter Bar");
168 meter_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
170 tempo_bar = new ArdourCanvas::Rectangle (tempo_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
171 CANVAS_DEBUG_NAME (tempo_bar, "Tempo Bar");
172 tempo_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
174 range_marker_bar = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
175 CANVAS_DEBUG_NAME (range_marker_bar, "Range Marker Bar");
176 range_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
178 transport_marker_bar = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
179 CANVAS_DEBUG_NAME (transport_marker_bar, "transport Marker Bar");
180 transport_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
182 marker_bar = new ArdourCanvas::Rectangle (marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
183 CANVAS_DEBUG_NAME (marker_bar, "Marker Bar");
184 marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
186 cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
187 CANVAS_DEBUG_NAME (cd_marker_bar, "CD Marker Bar");
188 cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
190 ARDOUR_UI::instance()->video_timeline = new VideoTimeLine(this, videotl_group, (timebar_height * videotl_bar_height));
192 cd_marker_bar_drag_rect = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
193 CANVAS_DEBUG_NAME (cd_marker_bar_drag_rect, "cd marker drag");
194 cd_marker_bar_drag_rect->set_outline (false);
195 cd_marker_bar_drag_rect->hide ();
197 range_bar_drag_rect = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
198 CANVAS_DEBUG_NAME (range_bar_drag_rect, "range drag");
199 range_bar_drag_rect->set_outline (false);
200 range_bar_drag_rect->hide ();
202 transport_bar_drag_rect = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
203 CANVAS_DEBUG_NAME (transport_bar_drag_rect, "transport drag");
204 transport_bar_drag_rect->set_outline (false);
205 transport_bar_drag_rect->hide ();
207 transport_punchin_line = new ArdourCanvas::Line (hv_scroll_group);
208 transport_punchin_line->set_x0 (0);
209 transport_punchin_line->set_y0 (0);
210 transport_punchin_line->set_x1 (0);
211 transport_punchin_line->set_y1 (ArdourCanvas::COORD_MAX);
212 transport_punchin_line->hide ();
214 transport_punchout_line = new ArdourCanvas::Line (hv_scroll_group);
215 transport_punchout_line->set_x0 (0);
216 transport_punchout_line->set_y0 (0);
217 transport_punchout_line->set_x1 (0);
218 transport_punchout_line->set_y1 (ArdourCanvas::COORD_MAX);
219 transport_punchout_line->hide();
221 tempo_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_tempo_bar_event), tempo_bar));
222 meter_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_meter_bar_event), meter_bar));
223 marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_marker_bar_event), marker_bar));
224 cd_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_cd_marker_bar_event), cd_marker_bar));
225 videotl_group->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_videotl_bar_event), videotl_group));
226 range_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_range_marker_bar_event), range_marker_bar));
227 transport_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_transport_marker_bar_event), transport_marker_bar));
229 playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event);
232 logo_item->lower_to_bottom ();
236 _canvas_drop_zone = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, 0.0));
237 /* this thing is transparent */
238 _canvas_drop_zone->set_fill (false);
239 _canvas_drop_zone->set_outline (false);
240 _canvas_drop_zone->Event.connect (sigc::mem_fun (*this, &Editor::canvas_drop_zone_event));
242 /* these signals will initially be delivered to the canvas itself, but if they end up remaining unhandled, they are passed to Editor-level
246 _track_canvas->signal_scroll_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_scroll_event), true));
247 _track_canvas->signal_motion_notify_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_motion_notify_event));
248 _track_canvas->signal_button_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_press_event));
249 _track_canvas->signal_button_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_release_event));
250 _track_canvas->signal_drag_motion().connect (sigc::mem_fun (*this, &Editor::track_canvas_drag_motion));
251 _track_canvas->signal_key_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_press));
252 _track_canvas->signal_key_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_release));
254 _track_canvas->set_name ("EditorMainCanvas");
255 _track_canvas->add_events (Gdk::POINTER_MOTION_HINT_MASK | Gdk::SCROLL_MASK | Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
256 _track_canvas->signal_leave_notify_event().connect (sigc::mem_fun(*this, &Editor::left_track_canvas), false);
257 _track_canvas->signal_enter_notify_event().connect (sigc::mem_fun(*this, &Editor::entered_track_canvas), false);
258 _track_canvas->set_flags (CAN_FOCUS);
260 /* set up drag-n-drop */
262 vector<TargetEntry> target_table;
264 // Drag-N-Drop from the region list can generate this target
265 target_table.push_back (TargetEntry ("regions"));
267 target_table.push_back (TargetEntry ("text/plain"));
268 target_table.push_back (TargetEntry ("text/uri-list"));
269 target_table.push_back (TargetEntry ("application/x-rootwin-drop"));
271 _track_canvas->drag_dest_set (target_table);
272 _track_canvas->signal_drag_data_received().connect (sigc::mem_fun(*this, &Editor::track_canvas_drag_data_received));
274 _track_canvas_viewport->signal_size_allocate().connect (sigc::mem_fun(*this, &Editor::track_canvas_viewport_allocate));
276 initialize_rulers ();
278 ColorsChanged.connect (sigc::mem_fun (*this, &Editor::color_handler));
284 Editor::track_canvas_viewport_allocate (Gtk::Allocation alloc)
286 _canvas_viewport_allocation = alloc;
287 track_canvas_viewport_size_allocated ();
291 Editor::track_canvas_viewport_size_allocated ()
293 bool height_changed = _visible_canvas_height != _canvas_viewport_allocation.get_height();
295 _visible_canvas_width = _canvas_viewport_allocation.get_width ();
296 _visible_canvas_height = _canvas_viewport_allocation.get_height ();
298 _canvas_drop_zone->set_y1 (_canvas_drop_zone->y0() + (_visible_canvas_height - 20.0));
302 if (height_changed) {
304 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
305 i->second->canvas_height_set (_visible_canvas_height);
308 vertical_adjustment.set_page_size (_visible_canvas_height);
309 if ((vertical_adjustment.get_value() + _visible_canvas_height) >= vertical_adjustment.get_upper()) {
311 We're increasing the size of the canvas while the bottom is visible.
312 We scroll down to keep in step with the controls layout.
314 vertical_adjustment.set_value (_full_canvas_height - _visible_canvas_height);
317 set_visible_track_count (_visible_track_count);
320 update_fixed_rulers();
321 redisplay_tempo (false);
322 _summary->set_overlays_dirty ();
326 Editor::reset_controls_layout_width ()
331 edit_controls_vbox.size_request (req);
334 if (_group_tabs->is_mapped()) {
335 _group_tabs->size_request (req);
339 /* the controls layout has no horizontal scrolling, its visible
340 width is always equal to the total width of its contents.
343 controls_layout.property_width() = w;
344 controls_layout.property_width_request() = w;
348 Editor::reset_controls_layout_height (int32_t h)
350 /* ensure that the rect that represents the "bottom" of the canvas
351 * (the drag-n-drop zone) is, in fact, at the bottom.
354 _canvas_drop_zone->set_position (ArdourCanvas::Duple (0, h));
356 /* track controls layout must span the full height of "h" (all tracks)
357 * plus the bottom rect.
360 h += _canvas_drop_zone->height ();
362 /* set the height of the scrollable area (i.e. the sum of all contained widgets)
363 * for the controls layout. The size request is set elsewhere.
366 controls_layout.property_height() = h;
371 Editor::track_canvas_map_handler (GdkEventAny* /*ev*/)
373 if (current_canvas_cursor) {
374 set_canvas_cursor (current_canvas_cursor);
379 /** This is called when something is dropped onto the track canvas */
381 Editor::track_canvas_drag_data_received (const RefPtr<Gdk::DragContext>& context,
383 const SelectionData& data,
384 guint info, guint time)
386 if (data.get_target() == "regions") {
387 drop_regions (context, x, y, data, info, time);
389 drop_paths (context, x, y, data, info, time);
394 Editor::idle_drop_paths (vector<string> paths, framepos_t frame, double ypos, bool copy)
396 drop_paths_part_two (paths, frame, ypos, copy);
401 Editor::drop_paths_part_two (const vector<string>& paths, framepos_t frame, double ypos, bool copy)
403 RouteTimeAxisView* tv;
405 /* MIDI files must always be imported, because we consider them
406 * writable. So split paths into two vectors, and follow the import
407 * path on the MIDI part.
410 vector<string> midi_paths;
411 vector<string> audio_paths;
413 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
414 if (SMFSource::safe_midi_file_extension (*i)) {
415 midi_paths.push_back (*i);
417 audio_paths.push_back (*i);
422 std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (ypos, false);
423 if (tvp.first == 0) {
425 /* drop onto canvas background: create new tracks */
429 do_import (midi_paths, Editing::ImportDistinctFiles, ImportAsTrack, SrcBest, frame);
431 if (Profile->get_sae() || Config->get_only_copy_imported_files() || copy) {
432 do_import (audio_paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack, SrcBest, frame);
434 do_embed (audio_paths, Editing::ImportDistinctFiles, ImportAsTrack, frame);
437 } else if ((tv = dynamic_cast<RouteTimeAxisView*> (tvp.first)) != 0) {
439 /* check that its a track, not a bus */
442 /* select the track, then embed/import */
445 do_import (midi_paths, Editing::ImportSerializeFiles, ImportToTrack, SrcBest, frame);
447 if (Profile->get_sae() || Config->get_only_copy_imported_files() || copy) {
448 do_import (audio_paths, Editing::ImportSerializeFiles, Editing::ImportToTrack, SrcBest, frame);
450 do_embed (audio_paths, Editing::ImportSerializeFiles, ImportToTrack, frame);
457 Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
459 const SelectionData& data,
460 guint info, guint time)
462 vector<string> paths;
467 if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
469 /* D-n-D coordinates are window-relative, so convert to canvas coordinates
472 ev.type = GDK_BUTTON_RELEASE;
476 frame = window_event_sample (&ev, 0, &cy);
480 bool copy = ((context->get_actions() & (Gdk::ACTION_COPY | Gdk::ACTION_LINK | Gdk::ACTION_MOVE)) == Gdk::ACTION_COPY);
482 /* We are not allowed to call recursive main event loops from within
483 the main event loop with GTK/Quartz. Since import/embed wants
484 to push up a progress dialog, defer all this till we go idle.
486 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun (*this, &Editor::idle_drop_paths), paths, frame, cy, copy));
488 drop_paths_part_two (paths, frame, cy, copy);
492 context->drag_finish (true, false, time);
495 /** @param allow_horiz true to allow horizontal autoscroll, otherwise false.
497 * @param allow_vert true to allow vertical autoscroll, otherwise false.
501 Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
503 if (!Config->get_autoscroll_editor () || autoscroll_active ()) {
507 /* define a rectangular boundary for scrolling. If the mouse moves
508 * outside of this area and/or continue to be outside of this area,
509 * then we will continuously auto-scroll the canvas in the appropriate
512 * the boundary is defined in coordinates relative to the toplevel
513 * window since that is what we're going to call ::get_pointer() on
514 * during autoscrolling to determine if we're still outside the
518 ArdourCanvas::Rect scrolling_boundary;
519 Gtk::Allocation alloc;
522 alloc = controls_layout.get_allocation ();
524 alloc = _track_canvas_viewport->get_allocation ();
526 /* reduce height by the height of the timebars, which happens
527 to correspond to the position of the hv_scroll_group.
530 alloc.set_height (alloc.get_height() - hv_scroll_group->position().y);
531 alloc.set_y (alloc.get_y() + hv_scroll_group->position().y);
533 /* now reduce it again so that we start autoscrolling before we
534 * move off the top or bottom of the canvas
537 alloc.set_height (alloc.get_height() - 20);
538 alloc.set_y (alloc.get_y() + 10);
540 /* the effective width of the autoscroll boundary so
541 that we start scrolling before we hit the edge.
543 this helps when the window is slammed up against the
544 right edge of the screen, making it hard to scroll
548 if (alloc.get_width() > 20) {
549 alloc.set_width (alloc.get_width() - 20);
550 alloc.set_x (alloc.get_x() + 10);
555 scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), alloc.get_x() + alloc.get_width(), alloc.get_y() + alloc.get_height());
558 Gdk::ModifierType mask;
560 get_window()->get_pointer (x, y, mask);
562 if ((allow_horiz && ((x < scrolling_boundary.x0 && leftmost_frame > 0) || x >= scrolling_boundary.x1)) ||
563 (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) {
564 start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary);
569 Editor::autoscroll_active () const
571 return autoscroll_connection.connected ();
575 Editor::autoscroll_canvas ()
578 Gdk::ModifierType mask;
579 frameoffset_t dx = 0;
580 bool no_stop = false;
582 get_window()->get_pointer (x, y, mask);
585 bool vertical_motion = false;
586 bool y_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 ();
786 Editor::left_track_canvas (GdkEventCrossing */*ev*/)
789 within_track_canvas = false;
790 set_entered_track (0);
791 set_entered_regionview (0);
792 reset_canvas_action_sensitivity (false);
797 Editor::entered_track_canvas (GdkEventCrossing */*ev*/)
799 within_track_canvas = true;
800 reset_canvas_action_sensitivity (true);
805 Editor::ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top)
807 if (track.hidden()) {
811 /* compute visible area of trackview group, as offsets from top of
815 double const current_view_min_y = vertical_adjustment.get_value();
816 double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size();
818 double const track_min_y = track.y_position ();
819 double const track_max_y = track.y_position () + track.effective_height ();
822 (track_min_y >= current_view_min_y &&
823 track_max_y < current_view_max_y)) {
824 /* already visible, and caller did not ask to place it at the
825 * top of the track canvas
833 new_value = track_min_y;
835 if (track_min_y < current_view_min_y) {
836 // Track is above the current view
837 new_value = track_min_y;
838 } else if (track_max_y > current_view_max_y) {
839 // Track is below the current view
840 new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size();
842 new_value = track_min_y;
846 vertical_adjustment.set_value(new_value);
849 /** Called when the main vertical_adjustment has changed */
851 Editor::tie_vertical_scrolling ()
853 if (pending_visual_change.idle_handler_id < 0) {
854 _summary->set_overlays_dirty ();
859 Editor::set_horizontal_position (double p)
861 horizontal_adjustment.set_value (p);
863 leftmost_frame = (framepos_t) floor (p * samples_per_pixel);
865 update_fixed_rulers ();
866 redisplay_tempo (true);
868 if (pending_visual_change.idle_handler_id < 0) {
869 _summary->set_overlays_dirty ();
872 update_video_timeline();
876 Editor::color_handler()
878 ArdourCanvas::Color base = ARDOUR_UI::config()->get_canvasvar_RulerBase();
879 ArdourCanvas::Color text = ARDOUR_UI::config()->get_canvasvar_RulerText();
880 timecode_ruler->set_fill_color (base);
881 timecode_ruler->set_outline_color (text);
882 minsec_ruler->set_fill_color (base);
883 minsec_ruler->set_outline_color (text);
884 samples_ruler->set_fill_color (base);
885 samples_ruler->set_outline_color (text);
886 bbt_ruler->set_fill_color (base);
887 bbt_ruler->set_outline_color (text);
889 playhead_cursor->set_color (ARDOUR_UI::config()->get_canvasvar_PlayHead());
891 meter_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_MeterBar());
892 meter_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
894 tempo_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TempoBar());
895 tempo_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
897 marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_MarkerBar());
898 marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
900 cd_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_CDMarkerBar());
901 cd_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
903 range_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeMarkerBar());
904 range_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
906 transport_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportMarkerBar());
907 transport_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
909 cd_marker_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
910 cd_marker_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
912 range_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
913 range_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect());
915 transport_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportDragRect());
916 transport_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportDragRect());
918 transport_loop_range_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportLoopRect());
919 transport_loop_range_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportLoopRect());
921 transport_punch_range_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportPunchRect());
922 transport_punch_range_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportPunchRect());
924 transport_punchin_line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_PunchLine());
925 transport_punchout_line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_PunchLine());
927 zoom_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ZoomRect());
928 zoom_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZoomRect());
930 rubberband_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RubberBandRect());
931 rubberband_rect->set_fill_color ((guint32) ARDOUR_UI::config()->get_canvasvar_RubberBandRect());
933 location_marker_color = ARDOUR_UI::config()->get_canvasvar_LocationMarker();
934 location_range_color = ARDOUR_UI::config()->get_canvasvar_LocationRange();
935 location_cd_marker_color = ARDOUR_UI::config()->get_canvasvar_LocationCDMarker();
936 location_loop_color = ARDOUR_UI::config()->get_canvasvar_LocationLoop();
937 location_punch_color = ARDOUR_UI::config()->get_canvasvar_LocationPunch();
939 refresh_location_display ();
941 redisplay_tempo (true);
944 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
949 Editor::horizontal_position () const
951 return sample_to_pixel (leftmost_frame);
955 Editor::track_canvas_key_press (GdkEventKey*)
957 /* XXX: event does not report the modifier key pressed down, AFAICS, so use the Keyboard object instead */
958 if (mouse_mode == Editing::MouseZoom && Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
959 set_canvas_cursor (_cursors->zoom_out, true);
966 Editor::track_canvas_key_release (GdkEventKey*)
968 if (mouse_mode == Editing::MouseZoom && !Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
969 set_canvas_cursor (_cursors->zoom_in, true);
976 Editor::clamp_verbose_cursor_x (double x)
981 x = min (_visible_canvas_width - 200.0, x);
987 Editor::clamp_verbose_cursor_y (double y)
990 y = min (_visible_canvas_height - 50, y);
994 ArdourCanvas::GtkCanvasViewport*
995 Editor::get_track_canvas() const
997 return _track_canvas_viewport;
1001 Editor::set_canvas_cursor (Gdk::Cursor* cursor, bool save)
1004 current_canvas_cursor = cursor;
1007 Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
1009 if (win && cursor) {
1010 win->set_cursor (*cursor);
1015 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
1018 _cursor_stack.push (cursor);
1019 set_canvas_cursor (cursor, false);
1024 Editor::pop_canvas_cursor ()
1026 if (!_cursor_stack.empty()) {
1027 Gdk::Cursor* cursor = _cursor_stack.top ();
1028 _cursor_stack.pop ();
1029 set_canvas_cursor (cursor, false);
1034 Editor::which_grabber_cursor () const
1036 Gdk::Cursor* c = _cursors->grabber;
1038 if (_internal_editing) {
1039 switch (mouse_mode) {
1041 c = _cursors->midi_pencil;
1045 c = _cursors->grabber_note;
1049 c = _cursors->midi_resize;
1053 c = _cursors->grabber_note;
1062 switch (_edit_point) {
1064 c = _cursors->grabber_edit_point;
1067 boost::shared_ptr<Movable> m = _movable.lock();
1068 if (m && m->locked()) {
1069 c = _cursors->speaker;
1079 Editor::which_trim_cursor (bool left) const
1081 if (!entered_regionview) {
1085 Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
1089 if (ct & Trimmable::FrontTrimEarlier) {
1090 return _cursors->left_side_trim;
1092 return _cursors->left_side_trim_right_only;
1095 if (ct & Trimmable::EndTrimLater) {
1096 return _cursors->right_side_trim;
1098 return _cursors->right_side_trim_left_only;
1104 Editor::which_mode_cursor () const
1106 Gdk::Cursor* mode_cursor = 0;
1108 switch (mouse_mode) {
1110 mode_cursor = _cursors->selector;
1111 if (_internal_editing) {
1112 mode_cursor = which_grabber_cursor();
1117 mode_cursor = _cursors->scissors;
1121 /* don't use mode cursor, pick a grabber cursor based on the item */
1125 mode_cursor = _cursors->midi_pencil;
1129 mode_cursor = _cursors->cross_hair;
1133 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
1134 mode_cursor = _cursors->zoom_out;
1136 mode_cursor = _cursors->zoom_in;
1141 mode_cursor = _cursors->time_fx; // just use playhead
1145 mode_cursor = _cursors->speaker;
1149 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
1150 if (!_internal_editing && get_smart_mode() ) {
1153 get_pointer_position (x, y);
1155 if (x >= 0 && y >= 0) {
1157 vector<ArdourCanvas::Item const *> items;
1159 /* Note how we choose a specific scroll group to get
1160 * items from. This could be problematic.
1163 hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
1165 // first item will be the upper most
1167 if (!items.empty()) {
1168 const ArdourCanvas::Item* i = items.front();
1170 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
1171 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
1172 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
1173 mode_cursor = _cursors->up_down;
1184 Editor::which_track_cursor () const
1186 Gdk::Cursor* cursor = 0;
1188 assert (mouse_mode == MouseObject || get_smart_mode());
1190 if (!_internal_editing) {
1191 switch (_join_object_range_state) {
1192 case JOIN_OBJECT_RANGE_NONE:
1193 case JOIN_OBJECT_RANGE_OBJECT:
1194 cursor = which_grabber_cursor ();
1196 case JOIN_OBJECT_RANGE_RANGE:
1197 cursor = _cursors->selector;
1206 Editor::reset_canvas_cursor ()
1208 if (!is_drawable()) {
1212 Gdk::Cursor* cursor = which_mode_cursor ();
1215 cursor = which_grabber_cursor ();
1219 set_canvas_cursor (cursor);
1227 Editor::choose_canvas_cursor_on_entry (GdkEventCrossing* /*event*/, ItemType type)
1229 Gdk::Cursor* cursor = 0;
1231 if (_drags->active()) {
1235 cursor = which_mode_cursor ();
1237 if (mouse_mode == MouseObject || get_smart_mode ()) {
1239 /* find correct cursor to use in object/smart mode */
1243 case RegionViewNameHighlight:
1244 case RegionViewName:
1247 case AutomationTrackItem:
1248 cursor = which_track_cursor ();
1250 case PlayheadCursorItem:
1251 switch (_edit_point) {
1253 cursor = _cursors->grabber_edit_point;
1256 cursor = _cursors->grabber;
1261 cursor = _cursors->selector;
1263 case ControlPointItem:
1264 cursor = _cursors->fader;
1267 cursor = which_track_cursor ();
1269 case AutomationLineItem:
1270 cursor = _cursors->cross_hair;
1272 case StartSelectionTrimItem:
1273 cursor = _cursors->left_side_trim;
1275 case EndSelectionTrimItem:
1276 cursor = _cursors->right_side_trim;
1279 cursor = _cursors->fade_in;
1281 case FadeInHandleItem:
1282 cursor = _cursors->fade_in;
1284 case FadeInTrimHandleItem:
1285 cursor = _cursors->fade_in;
1288 cursor = _cursors->fade_out;
1290 case FadeOutHandleItem:
1291 cursor = _cursors->fade_out;
1293 case FadeOutTrimHandleItem:
1294 cursor = _cursors->fade_out;
1297 cursor = which_grabber_cursor();
1299 case FeatureLineItem:
1300 cursor = _cursors->cross_hair;
1302 case LeftFrameHandle:
1303 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
1304 cursor = which_trim_cursor (true); //alternatively, one could argue that we _should_ allow trims here, and disallow range selection
1306 case RightFrameHandle:
1307 if ( effective_mouse_mode() == MouseObject ) //see above
1308 cursor = which_trim_cursor (false);
1310 case StartCrossFadeItem:
1311 cursor = _cursors->fade_in;
1313 case EndCrossFadeItem:
1314 cursor = _cursors->fade_out;
1316 case CrossfadeViewItem:
1317 cursor = _cursors->cross_hair;
1323 } else if (mouse_mode == MouseGain) {
1325 /* ControlPointItem is not really specific to region gain mode
1326 but it is the same cursor so don't worry about this for now.
1327 The result is that we'll see the fader cursor if we enter
1328 non-region-gain-line control points while in MouseGain
1329 mode, even though we can't edit them in this mode.
1334 case ControlPointItem:
1335 cursor = _cursors->fader;
1343 /* These items use the timebar cursor at all times */
1344 case TimecodeRulerItem:
1345 case MinsecRulerItem:
1347 case SamplesRulerItem:
1348 cursor = _cursors->timebar;
1351 /* These items use the grabber cursor at all times */
1352 case MeterMarkerItem:
1353 case TempoMarkerItem:
1358 case RangeMarkerBarItem:
1359 case CdMarkerBarItem:
1361 case TransportMarkerBarItem:
1363 cursor = which_grabber_cursor();
1371 set_canvas_cursor (cursor, false);
1376 Editor::trackviews_height() const
1378 if (!_trackview_group) {
1382 return _visible_canvas_height - _trackview_group->canvas_origin().y;