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 "ui_config.h"
55 #include "verbose_cursor.h"
60 using namespace ARDOUR;
61 using namespace ARDOUR_UI_UTILS;
65 using namespace Gtkmm2ext;
66 using namespace Editing;
69 Editor::initialize_canvas ()
71 _track_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, vertical_adjustment);
72 _track_canvas = _track_canvas_viewport->canvas ();
74 _track_canvas->set_background_color (UIConfiguration::instance().color ("arrange base"));
76 /* scroll group for items that should not automatically scroll
77 * (e.g verbose cursor). It shares the canvas coordinate space.
79 no_scroll_group = new ArdourCanvas::Container (_track_canvas->root());
81 ArdourCanvas::ScrollGroup* hsg;
82 ArdourCanvas::ScrollGroup* hg;
83 ArdourCanvas::ScrollGroup* cg;
85 h_scroll_group = hg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
86 CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll");
87 _track_canvas->add_scroller (*hg);
89 hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_track_canvas->root(),
90 ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically|
91 ArdourCanvas::ScrollGroup::ScrollsHorizontally));
92 CANVAS_DEBUG_NAME (hv_scroll_group, "canvas hv scroll");
93 _track_canvas->add_scroller (*hsg);
95 cursor_scroll_group = cg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
96 CANVAS_DEBUG_NAME (cursor_scroll_group, "canvas cursor scroll");
97 _track_canvas->add_scroller (*cg);
99 _verbose_cursor = new VerboseCursor (this);
101 /* on the bottom, an image */
103 if (Profile->get_sae()) {
104 Image img (::get_icon (X_("saelogo")));
105 // logo_item = new ArdourCanvas::Pixbuf (_track_canvas->root(), 0.0, 0.0, img.get_pixbuf());
106 // logo_item->property_height_in_pixels() = true;
107 // logo_item->property_width_in_pixels() = true;
108 // logo_item->property_height_set() = true;
109 // logo_item->property_width_set() = true;
110 // logo_item->show ();
113 /*a group to hold global rects like punch/loop indicators */
114 global_rect_group = new ArdourCanvas::Container (hv_scroll_group);
115 CANVAS_DEBUG_NAME (global_rect_group, "global rect group");
117 transport_loop_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
118 CANVAS_DEBUG_NAME (transport_loop_range_rect, "loop rect");
119 transport_loop_range_rect->hide();
121 transport_punch_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
122 CANVAS_DEBUG_NAME (transport_punch_range_rect, "punch rect");
123 transport_punch_range_rect->hide();
125 /*a group to hold time (measure) lines */
126 time_line_group = new ArdourCanvas::Container (h_scroll_group);
127 CANVAS_DEBUG_NAME (time_line_group, "time line group");
129 _trackview_group = new ArdourCanvas::Container (hv_scroll_group);
130 CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
132 // used as rubberband rect
133 rubberband_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
134 rubberband_rect->hide();
136 /* a group to hold stuff while it gets dragged around. Must be the
137 * uppermost (last) group with hv_scroll_group as a parent
139 _drag_motion_group = new ArdourCanvas::Container (hv_scroll_group);
140 CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion");
142 /* TIME BAR CANVAS */
144 _time_markers_group = new ArdourCanvas::Container (h_scroll_group);
145 CANVAS_DEBUG_NAME (_time_markers_group, "time bars");
147 cd_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, 0.0));
148 CANVAS_DEBUG_NAME (cd_marker_group, "cd marker group");
149 /* the vide is temporarily placed a the same location as the
150 cd_marker_group, but is moved later.
152 videotl_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0));
153 CANVAS_DEBUG_NAME (videotl_group, "videotl group");
154 marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, timebar_height + 1.0));
155 CANVAS_DEBUG_NAME (marker_group, "marker group");
156 transport_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 2.0) + 1.0));
157 CANVAS_DEBUG_NAME (transport_marker_group, "transport marker group");
158 range_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 3.0) + 1.0));
159 CANVAS_DEBUG_NAME (range_marker_group, "range marker group");
160 tempo_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 4.0) + 1.0));
161 CANVAS_DEBUG_NAME (tempo_group, "tempo group");
162 meter_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0));
163 CANVAS_DEBUG_NAME (meter_group, "meter group");
165 meter_bar = new ArdourCanvas::Rectangle (meter_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
166 CANVAS_DEBUG_NAME (meter_bar, "meter Bar");
167 meter_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
169 tempo_bar = new ArdourCanvas::Rectangle (tempo_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
170 CANVAS_DEBUG_NAME (tempo_bar, "Tempo Bar");
171 tempo_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
173 range_marker_bar = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
174 CANVAS_DEBUG_NAME (range_marker_bar, "Range Marker Bar");
175 range_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
177 transport_marker_bar = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
178 CANVAS_DEBUG_NAME (transport_marker_bar, "transport Marker Bar");
179 transport_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
181 marker_bar = new ArdourCanvas::Rectangle (marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
182 CANVAS_DEBUG_NAME (marker_bar, "Marker Bar");
183 marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
185 cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
186 CANVAS_DEBUG_NAME (cd_marker_bar, "CD Marker Bar");
187 cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
189 ARDOUR_UI::instance()->video_timeline = new VideoTimeLine(this, videotl_group, (timebar_height * videotl_bar_height));
191 cd_marker_bar_drag_rect = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
192 CANVAS_DEBUG_NAME (cd_marker_bar_drag_rect, "cd marker drag");
193 cd_marker_bar_drag_rect->set_outline (false);
194 cd_marker_bar_drag_rect->hide ();
196 range_bar_drag_rect = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
197 CANVAS_DEBUG_NAME (range_bar_drag_rect, "range drag");
198 range_bar_drag_rect->set_outline (false);
199 range_bar_drag_rect->hide ();
201 transport_bar_drag_rect = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
202 CANVAS_DEBUG_NAME (transport_bar_drag_rect, "transport drag");
203 transport_bar_drag_rect->set_outline (false);
204 transport_bar_drag_rect->hide ();
206 transport_punchin_line = new ArdourCanvas::Line (hv_scroll_group);
207 transport_punchin_line->set_x0 (0);
208 transport_punchin_line->set_y0 (0);
209 transport_punchin_line->set_x1 (0);
210 transport_punchin_line->set_y1 (ArdourCanvas::COORD_MAX);
211 transport_punchin_line->hide ();
213 transport_punchout_line = new ArdourCanvas::Line (hv_scroll_group);
214 transport_punchout_line->set_x0 (0);
215 transport_punchout_line->set_y0 (0);
216 transport_punchout_line->set_x1 (0);
217 transport_punchout_line->set_y1 (ArdourCanvas::COORD_MAX);
218 transport_punchout_line->hide();
220 tempo_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_tempo_bar_event), tempo_bar));
221 meter_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_meter_bar_event), meter_bar));
222 marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_marker_bar_event), marker_bar));
223 cd_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_cd_marker_bar_event), cd_marker_bar));
224 videotl_group->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_videotl_bar_event), videotl_group));
225 range_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_range_marker_bar_event), range_marker_bar));
226 transport_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_transport_marker_bar_event), transport_marker_bar));
228 playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event);
231 logo_item->lower_to_bottom ();
234 _canvas_drop_zone = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, 0.0));
235 /* this thing is transparent */
236 _canvas_drop_zone->set_fill (false);
237 _canvas_drop_zone->set_outline (false);
238 _canvas_drop_zone->Event.connect (sigc::mem_fun (*this, &Editor::canvas_drop_zone_event));
240 /* these signals will initially be delivered to the canvas itself, but if they end up remaining unhandled, they are passed to Editor-level
244 _track_canvas->signal_scroll_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_scroll_event), true));
245 _track_canvas->signal_motion_notify_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_motion_notify_event));
246 _track_canvas->signal_button_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_press_event));
247 _track_canvas->signal_button_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_release_event));
248 _track_canvas->signal_drag_motion().connect (sigc::mem_fun (*this, &Editor::track_canvas_drag_motion));
249 _track_canvas->signal_key_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_press));
250 _track_canvas->signal_key_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_release));
252 _track_canvas->set_name ("EditorMainCanvas");
253 _track_canvas->add_events (Gdk::POINTER_MOTION_HINT_MASK | Gdk::SCROLL_MASK | Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
254 _track_canvas->signal_leave_notify_event().connect (sigc::mem_fun(*this, &Editor::left_track_canvas), false);
255 _track_canvas->signal_enter_notify_event().connect (sigc::mem_fun(*this, &Editor::entered_track_canvas), false);
256 _track_canvas->set_flags (CAN_FOCUS);
258 /* set up drag-n-drop */
260 vector<TargetEntry> target_table;
262 // Drag-N-Drop from the region list can generate this target
263 target_table.push_back (TargetEntry ("regions"));
265 target_table.push_back (TargetEntry ("text/plain"));
266 target_table.push_back (TargetEntry ("text/uri-list"));
267 target_table.push_back (TargetEntry ("application/x-rootwin-drop"));
269 _track_canvas->drag_dest_set (target_table);
270 _track_canvas->signal_drag_data_received().connect (sigc::mem_fun(*this, &Editor::track_canvas_drag_data_received));
272 _track_canvas_viewport->signal_size_allocate().connect (sigc::mem_fun(*this, &Editor::track_canvas_viewport_allocate));
274 initialize_rulers ();
276 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &Editor::color_handler));
282 Editor::track_canvas_viewport_allocate (Gtk::Allocation alloc)
284 _canvas_viewport_allocation = alloc;
285 track_canvas_viewport_size_allocated ();
289 Editor::track_canvas_viewport_size_allocated ()
291 bool height_changed = _visible_canvas_height != _canvas_viewport_allocation.get_height();
293 _visible_canvas_width = _canvas_viewport_allocation.get_width ();
294 _visible_canvas_height = _canvas_viewport_allocation.get_height ();
296 _canvas_drop_zone->set_y1 (_canvas_drop_zone->y0() + (_visible_canvas_height - 20.0));
300 if (height_changed) {
302 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
303 i->second->canvas_height_set (_visible_canvas_height);
306 vertical_adjustment.set_page_size (_visible_canvas_height);
307 if ((vertical_adjustment.get_value() + _visible_canvas_height) >= vertical_adjustment.get_upper()) {
309 We're increasing the size of the canvas while the bottom is visible.
310 We scroll down to keep in step with the controls layout.
312 vertical_adjustment.set_value (_full_canvas_height - _visible_canvas_height);
315 set_visible_track_count (_visible_track_count);
318 update_fixed_rulers();
319 redisplay_tempo (false);
320 _summary->set_overlays_dirty ();
324 Editor::reset_controls_layout_width ()
326 GtkRequisition req = { 0, 0 };
329 edit_controls_vbox.size_request (req);
332 if (_group_tabs->is_mapped()) {
333 _group_tabs->size_request (req);
337 /* the controls layout has no horizontal scrolling, its visible
338 width is always equal to the total width of its contents.
341 controls_layout.property_width() = w;
342 controls_layout.property_width_request() = w;
346 Editor::reset_controls_layout_height (int32_t h)
348 /* ensure that the rect that represents the "bottom" of the canvas
349 * (the drag-n-drop zone) is, in fact, at the bottom.
352 _canvas_drop_zone->set_position (ArdourCanvas::Duple (0, h));
354 /* track controls layout must span the full height of "h" (all tracks)
355 * plus the bottom rect.
358 h += _canvas_drop_zone->height ();
360 /* set the height of the scrollable area (i.e. the sum of all contained widgets)
361 * for the controls layout. The size request is set elsewhere.
364 controls_layout.property_height() = h;
369 Editor::track_canvas_map_handler (GdkEventAny* /*ev*/)
371 if (!_cursor_stack.empty()) {
372 set_canvas_cursor (get_canvas_cursor());
374 PBD::error << "cursor stack is empty" << endmsg;
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 */
428 InstrumentSelector is; // instantiation builds instrument-list and sets default.
429 do_import (midi_paths, Editing::ImportDistinctFiles, ImportAsTrack, SrcBest, frame, is.selected_instrument());
431 if (Profile->get_sae() || UIConfiguration::instance().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() || UIConfiguration::instance().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 Gtk::Window* toplevel = dynamic_cast<Gtk::Window*>(contents().get_toplevel());
509 if (!UIConfiguration::instance()->get_autoscroll_editor () || autoscroll_active ()) {
513 /* define a rectangular boundary for scrolling. If the mouse moves
514 * outside of this area and/or continue to be outside of this area,
515 * then we will continuously auto-scroll the canvas in the appropriate
518 * the boundary is defined in coordinates relative to the toplevel
519 * window since that is what we're going to call ::get_pointer() on
520 * during autoscrolling to determine if we're still outside the
524 ArdourCanvas::Rect scrolling_boundary;
525 Gtk::Allocation alloc;
528 alloc = controls_layout.get_allocation ();
530 alloc = _track_canvas_viewport->get_allocation ();
532 /* reduce height by the height of the timebars, which happens
533 to correspond to the position of the hv_scroll_group.
536 alloc.set_height (alloc.get_height() - hv_scroll_group->position().y);
537 alloc.set_y (alloc.get_y() + hv_scroll_group->position().y);
539 /* now reduce it again so that we start autoscrolling before we
540 * move off the top or bottom of the canvas
543 alloc.set_height (alloc.get_height() - 20);
544 alloc.set_y (alloc.get_y() + 10);
546 /* the effective width of the autoscroll boundary so
547 that we start scrolling before we hit the edge.
549 this helps when the window is slammed up against the
550 right edge of the screen, making it hard to scroll
554 if (alloc.get_width() > 20) {
555 alloc.set_width (alloc.get_width() - 20);
556 alloc.set_x (alloc.get_x() + 10);
561 scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), alloc.get_x() + alloc.get_width(), alloc.get_y() + alloc.get_height());
564 Gdk::ModifierType mask;
566 toplevel->get_window()->get_pointer (x, y, mask);
568 if ((allow_horiz && ((x < scrolling_boundary.x0 && leftmost_frame > 0) || x >= scrolling_boundary.x1)) ||
569 (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) {
570 start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary);
575 Editor::autoscroll_active () const
577 return autoscroll_connection.connected ();
581 Editor::autoscroll_canvas ()
584 Gdk::ModifierType mask;
585 frameoffset_t dx = 0;
586 bool no_stop = false;
587 Gtk::Window* toplevel = dynamic_cast<Gtk::Window*>(contents().get_toplevel());
593 toplevel->get_window()->get_pointer (x, y, mask);
596 bool vertical_motion = false;
598 if (autoscroll_horizontal_allowed) {
600 framepos_t new_frame = leftmost_frame;
604 if (x > autoscroll_boundary.x1) {
606 /* bring it back into view */
607 dx = x - autoscroll_boundary.x1;
608 dx += 10 + (2 * (autoscroll_cnt/2));
610 dx = pixel_to_sample (dx);
612 if (leftmost_frame < max_framepos - dx) {
613 new_frame = leftmost_frame + dx;
615 new_frame = max_framepos;
620 } else if (x < autoscroll_boundary.x0) {
622 dx = autoscroll_boundary.x0 - x;
623 dx += 10 + (2 * (autoscroll_cnt/2));
625 dx = pixel_to_sample (dx);
627 if (leftmost_frame >= dx) {
628 new_frame = leftmost_frame - dx;
636 if (new_frame != leftmost_frame) {
637 vc.time_origin = new_frame;
638 vc.add (VisualChange::TimeOrigin);
642 if (autoscroll_vertical_allowed) {
644 // const double vertical_pos = vertical_adjustment.get_value();
645 const int speed_factor = 10;
649 if (y < autoscroll_boundary.y0) {
651 /* scroll to make higher tracks visible */
653 if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
654 scroll_up_one_track ();
655 vertical_motion = true;
658 } else if (y > autoscroll_boundary.y1) {
660 if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
661 scroll_down_one_track ();
662 vertical_motion = true;
669 if (vc.pending || vertical_motion) {
671 /* change horizontal first */
677 /* now send a motion event to notify anyone who cares
678 that we have moved to a new location (because we scrolled)
683 ev.type = GDK_MOTION_NOTIFY;
684 ev.state = Gdk::BUTTON1_MASK;
686 /* the motion handler expects events in canvas coordinate space */
688 /* we asked for the mouse position above (::get_pointer()) via
689 * our own top level window (we being the Editor). Convert into
690 * coordinates within the canvas window.
696 toplevel->translate_coordinates (*_track_canvas, x, y, cx, cy);
698 /* clamp x and y to remain within the autoscroll boundary,
699 * which is defined in window coordinates
702 x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1);
703 y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1);
705 /* now convert from Editor window coordinates to canvas
709 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
714 motion_handler (0, (GdkEvent*) &ev, true);
716 } else if (no_stop) {
718 /* not changing visual state but pointer is outside the scrolling boundary
719 * so we still need to deliver a fake motion event
724 ev.type = GDK_MOTION_NOTIFY;
725 ev.state = Gdk::BUTTON1_MASK;
727 /* the motion handler expects events in canvas coordinate space */
729 /* first convert from Editor window coordinates to canvas
736 /* clamp x and y to remain within the visible area. except
737 * .. if horizontal scrolling is allowed, always allow us to
741 if (autoscroll_horizontal_allowed) {
742 x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1);
744 x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
746 y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
748 toplevel->translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
750 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
755 motion_handler (0, (GdkEvent*) &ev, true);
758 stop_canvas_autoscroll ();
764 return true; /* call me again */
768 Editor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary)
774 stop_canvas_autoscroll ();
777 autoscroll_horizontal_allowed = allow_horiz;
778 autoscroll_vertical_allowed = allow_vert;
779 autoscroll_boundary = boundary;
781 /* do the first scroll right now
784 autoscroll_canvas ();
786 /* scroll again at very very roughly 30FPS */
788 autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::autoscroll_canvas), 30);
792 Editor::stop_canvas_autoscroll ()
794 autoscroll_connection.disconnect ();
797 Editor::EnterContext*
798 Editor::get_enter_context(ItemType type)
800 for (ssize_t i = _enter_stack.size() - 1; i >= 0; --i) {
801 if (_enter_stack[i].item_type == type) {
802 return &_enter_stack[i];
809 Editor::left_track_canvas (GdkEventCrossing */*ev*/)
812 within_track_canvas = false;
813 set_entered_track (0);
814 set_entered_regionview (0);
815 reset_canvas_action_sensitivity (false);
820 Editor::entered_track_canvas (GdkEventCrossing */*ev*/)
822 within_track_canvas = true;
823 reset_canvas_action_sensitivity (true);
828 Editor::ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top)
830 if (track.hidden()) {
834 /* compute visible area of trackview group, as offsets from top of
838 double const current_view_min_y = vertical_adjustment.get_value();
839 double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size();
841 double const track_min_y = track.y_position ();
842 double const track_max_y = track.y_position () + track.effective_height ();
845 (track_min_y >= current_view_min_y &&
846 track_max_y < current_view_max_y)) {
847 /* already visible, and caller did not ask to place it at the
848 * top of the track canvas
856 new_value = track_min_y;
858 if (track_min_y < current_view_min_y) {
859 // Track is above the current view
860 new_value = track_min_y;
861 } else if (track_max_y > current_view_max_y) {
862 // Track is below the current view
863 new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size();
865 new_value = track_min_y;
869 vertical_adjustment.set_value(new_value);
872 /** Called when the main vertical_adjustment has changed */
874 Editor::tie_vertical_scrolling ()
876 if (pending_visual_change.idle_handler_id < 0) {
877 _summary->set_overlays_dirty ();
882 Editor::set_horizontal_position (double p)
884 horizontal_adjustment.set_value (p);
886 leftmost_frame = (framepos_t) floor (p * samples_per_pixel);
888 update_fixed_rulers ();
889 redisplay_tempo (true);
891 if (pending_visual_change.idle_handler_id < 0) {
892 _summary->set_overlays_dirty ();
895 update_video_timeline();
899 Editor::color_handler()
901 ArdourCanvas::Color base = UIConfiguration::instance().color ("ruler base");
902 ArdourCanvas::Color text = UIConfiguration::instance().color ("ruler text");
903 timecode_ruler->set_fill_color (base);
904 timecode_ruler->set_outline_color (text);
905 minsec_ruler->set_fill_color (base);
906 minsec_ruler->set_outline_color (text);
907 samples_ruler->set_fill_color (base);
908 samples_ruler->set_outline_color (text);
909 bbt_ruler->set_fill_color (base);
910 bbt_ruler->set_outline_color (text);
912 playhead_cursor->set_color (UIConfiguration::instance().color ("play head"));
914 meter_bar->set_fill_color (UIConfiguration::instance().color_mod ("meter bar", "marker bar"));
915 meter_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
917 tempo_bar->set_fill_color (UIConfiguration::instance().color_mod ("tempo bar", "marker bar"));
918 tempo_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
920 marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("marker bar", "marker bar"));
921 marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
923 cd_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("cd marker bar", "marker bar"));
924 cd_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
926 range_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("range marker bar", "marker bar"));
927 range_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
929 transport_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("transport marker bar", "marker bar"));
930 transport_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
932 cd_marker_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect"));
933 cd_marker_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect"));
935 range_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect"));
936 range_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect"));
938 transport_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("transport drag rect"));
939 transport_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("transport drag rect"));
941 transport_loop_range_rect->set_fill_color (UIConfiguration::instance().color_mod ("transport loop rect", "loop rectangle"));
942 transport_loop_range_rect->set_outline_color (UIConfiguration::instance().color ("transport loop rect"));
944 transport_punch_range_rect->set_fill_color (UIConfiguration::instance().color ("transport punch rect"));
945 transport_punch_range_rect->set_outline_color (UIConfiguration::instance().color ("transport punch rect"));
947 transport_punchin_line->set_outline_color (UIConfiguration::instance().color ("punch line"));
948 transport_punchout_line->set_outline_color (UIConfiguration::instance().color ("punch line"));
950 rubberband_rect->set_outline_color (UIConfiguration::instance().color ("rubber band rect"));
951 rubberband_rect->set_fill_color (UIConfiguration::instance().color_mod ("rubber band rect", "selection rect"));
953 location_marker_color = UIConfiguration::instance().color ("location marker");
954 location_range_color = UIConfiguration::instance().color ("location range");
955 location_cd_marker_color = UIConfiguration::instance().color ("location cd marker");
956 location_loop_color = UIConfiguration::instance().color ("location loop");
957 location_punch_color = UIConfiguration::instance().color ("location punch");
959 refresh_location_display ();
961 /* redraw the whole thing */
962 _track_canvas->set_background_color (UIConfiguration::instance().color ("arrange base"));
963 _track_canvas->queue_draw ();
966 redisplay_tempo (true);
969 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
974 Editor::horizontal_position () const
976 return sample_to_pixel (leftmost_frame);
980 Editor::track_canvas_key_press (GdkEventKey*)
986 Editor::track_canvas_key_release (GdkEventKey*)
992 Editor::clamp_verbose_cursor_x (double x)
997 x = min (_visible_canvas_width - 200.0, x);
1003 Editor::clamp_verbose_cursor_y (double y)
1006 y = min (_visible_canvas_height - 50, y);
1010 ArdourCanvas::GtkCanvasViewport*
1011 Editor::get_track_canvas() const
1013 return _track_canvas_viewport;
1017 Editor::get_canvas_cursor () const
1019 /* The top of the cursor stack is always the currently visible cursor. */
1020 return _cursor_stack.back();
1024 Editor::set_canvas_cursor (Gdk::Cursor* cursor)
1026 Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
1028 if (win && !_cursors->is_invalid (cursor)) {
1029 /* glibmm 2.4 doesn't allow null cursor pointer because it uses
1030 a Gdk::Cursor& as the argument to Gdk::Window::set_cursor().
1031 But a null pointer just means "use parent window cursor",
1032 and so should be allowed. Gtkmm 3.x has fixed this API.
1034 For now, drop down and use C API
1036 gdk_window_set_cursor (win->gobj(), cursor ? cursor->gobj() : 0);
1041 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
1043 if (!_cursors->is_invalid (cursor)) {
1044 _cursor_stack.push_back (cursor);
1045 set_canvas_cursor (cursor);
1047 return _cursor_stack.size() - 1;
1051 Editor::pop_canvas_cursor ()
1054 if (_cursor_stack.size() <= 1) {
1055 PBD::error << "attempt to pop default cursor" << endmsg;
1059 _cursor_stack.pop_back();
1060 if (_cursor_stack.back()) {
1061 /* Popped to an existing cursor, we're done. Otherwise, the
1062 context that created this cursor has been destroyed, so we need
1063 to skip to the next down the stack. */
1064 set_canvas_cursor (_cursor_stack.back());
1071 Editor::which_grabber_cursor () const
1073 Gdk::Cursor* c = _cursors->grabber;
1075 switch (_edit_point) {
1077 c = _cursors->grabber_edit_point;
1080 boost::shared_ptr<Movable> m = _movable.lock();
1081 if (m && m->locked()) {
1082 c = _cursors->speaker;
1091 Editor::which_trim_cursor (bool left) const
1093 if (!entered_regionview) {
1097 Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
1101 if (ct & Trimmable::FrontTrimEarlier) {
1102 return _cursors->left_side_trim;
1104 return _cursors->left_side_trim_right_only;
1107 if (ct & Trimmable::EndTrimLater) {
1108 return _cursors->right_side_trim;
1110 return _cursors->right_side_trim_left_only;
1116 Editor::which_mode_cursor () const
1118 Gdk::Cursor* mode_cursor = MouseCursors::invalid_cursor ();
1120 switch (mouse_mode) {
1122 mode_cursor = _cursors->selector;
1126 mode_cursor = _cursors->scissors;
1131 /* don't use mode cursor, pick a grabber cursor based on the item */
1135 mode_cursor = _cursors->midi_pencil;
1139 mode_cursor = _cursors->time_fx; // just use playhead
1143 mode_cursor = _cursors->speaker;
1147 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
1148 if (get_smart_mode()) {
1151 get_pointer_position (x, y);
1153 if (x >= 0 && y >= 0) {
1155 vector<ArdourCanvas::Item const *> items;
1157 /* Note how we choose a specific scroll group to get
1158 * items from. This could be problematic.
1161 hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
1163 // first item will be the upper most
1165 if (!items.empty()) {
1166 const ArdourCanvas::Item* i = items.front();
1168 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
1169 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
1170 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
1171 mode_cursor = _cursors->up_down;
1182 Editor::which_track_cursor () const
1184 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
1186 switch (_join_object_range_state) {
1187 case JOIN_OBJECT_RANGE_NONE:
1188 case JOIN_OBJECT_RANGE_OBJECT:
1189 cursor = which_grabber_cursor ();
1191 case JOIN_OBJECT_RANGE_RANGE:
1192 cursor = _cursors->selector;
1200 Editor::which_canvas_cursor(ItemType type) const
1202 Gdk::Cursor* cursor = which_mode_cursor ();
1204 if (mouse_mode == MouseRange) {
1206 case StartSelectionTrimItem:
1207 cursor = _cursors->left_side_trim;
1209 case EndSelectionTrimItem:
1210 cursor = _cursors->right_side_trim;
1217 if ((mouse_mode == MouseObject || get_smart_mode ()) ||
1218 mouse_mode == MouseContent) {
1220 /* find correct cursor to use in object/smart mode */
1224 /* We don't choose a cursor for these items on top of a region view,
1225 because this would push a new context on the enter stack which
1226 means switching the region context for things like smart mode
1227 won't actualy change the cursor. */
1228 // case RegionViewNameHighlight:
1229 // case RegionViewName:
1232 case AutomationTrackItem:
1233 cursor = which_track_cursor ();
1235 case PlayheadCursorItem:
1236 switch (_edit_point) {
1238 cursor = _cursors->grabber_edit_point;
1241 cursor = _cursors->grabber;
1246 cursor = _cursors->selector;
1248 case ControlPointItem:
1249 cursor = _cursors->fader;
1252 cursor = _cursors->cross_hair;
1254 case AutomationLineItem:
1255 cursor = _cursors->cross_hair;
1257 case StartSelectionTrimItem:
1258 cursor = _cursors->left_side_trim;
1260 case EndSelectionTrimItem:
1261 cursor = _cursors->right_side_trim;
1264 cursor = _cursors->fade_in;
1266 case FadeInHandleItem:
1267 cursor = _cursors->fade_in;
1269 case FadeInTrimHandleItem:
1270 cursor = _cursors->fade_in;
1273 cursor = _cursors->fade_out;
1275 case FadeOutHandleItem:
1276 cursor = _cursors->fade_out;
1278 case FadeOutTrimHandleItem:
1279 cursor = _cursors->fade_out;
1281 case FeatureLineItem:
1282 cursor = _cursors->cross_hair;
1284 case LeftFrameHandle:
1285 if ( effective_mouse_mode() == MouseObject ) // (smart mode): if the user is in the btm half, show the trim cursor
1286 cursor = which_trim_cursor (true);
1288 cursor = _cursors->selector; // (smart mode): in the top half, just show the selection (range) cursor
1290 case RightFrameHandle:
1291 if ( effective_mouse_mode() == MouseObject ) //see above
1292 cursor = which_trim_cursor (false);
1294 cursor = _cursors->selector;
1296 case StartCrossFadeItem:
1297 cursor = _cursors->fade_in;
1299 case EndCrossFadeItem:
1300 cursor = _cursors->fade_out;
1302 case CrossfadeViewItem:
1303 cursor = _cursors->cross_hair;
1306 cursor = _cursors->grabber_note;
1311 } else if (mouse_mode == MouseDraw) {
1313 /* ControlPointItem is not really specific to region gain mode
1314 but it is the same cursor so don't worry about this for now.
1315 The result is that we'll see the fader cursor if we enter
1316 non-region-gain-line control points while in MouseDraw
1317 mode, even though we can't edit them in this mode.
1322 case ControlPointItem:
1323 cursor = _cursors->fader;
1326 cursor = _cursors->grabber_note;
1333 /* These items use the timebar cursor at all times */
1334 case TimecodeRulerItem:
1335 case MinsecRulerItem:
1337 case SamplesRulerItem:
1338 cursor = _cursors->timebar;
1341 /* These items use the grabber cursor at all times */
1342 case MeterMarkerItem:
1343 case TempoMarkerItem:
1348 case RangeMarkerBarItem:
1349 case CdMarkerBarItem:
1351 case TransportMarkerBarItem:
1353 cursor = which_grabber_cursor();
1364 Editor::choose_canvas_cursor_on_entry (ItemType type)
1366 if (_drags->active()) {
1370 Gdk::Cursor* cursor = which_canvas_cursor(type);
1372 if (!_cursors->is_invalid (cursor)) {
1373 // Push a new enter context
1374 const EnterContext ctx = { type, CursorContext::create(*this, cursor) };
1375 _enter_stack.push_back(ctx);
1380 Editor::update_all_enter_cursors ()
1382 for (std::vector<EnterContext>::iterator i = _enter_stack.begin(); i != _enter_stack.end(); ++i) {
1383 i->cursor_ctx->change(which_canvas_cursor(i->item_type));
1388 Editor::trackviews_height() const
1390 if (!_trackview_group) {
1394 return _visible_canvas_height - _trackview_group->canvas_origin().y;