1 /*
2 * Copyright (C) 2005-2017 Paul Davis <paul@linuxaudiosystems.com>
3 * Copyright (C) 2006-2015 David Robillard <d@drobilla.net>
4 * Copyright (C) 2007-2017 Tim Mayberry <mojofunk@gmail.com>
5 * Copyright (C) 2007 Doug McLain <doug@nostar.net>
6 * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
7 * Copyright (C) 2013-2019 Robin Gareus <robin@gareus.org>
8 * Copyright (C) 2014-2019 Ben Loftis <ben@harrisonconsoles.com>
9 * Copyright (C) 2015-2017 Nick Mainsbridge <mainsbridge@gmail.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 */
25
26 #ifdef WAF_BUILD
27 #include "gtk2ardour-config.h"
28 #endif
29
30 #include "gtkmm2ext/utils.h"
31
32 #include "ardour/profile.h"
33 #include "ardour/rc_configuration.h"
34 #include "ardour/smf_source.h"
35
36 #include "pbd/error.h"
37
38 #include "canvas/canvas.h"
39 #include "canvas/rectangle.h"
40 #include "canvas/pixbuf.h"
41 #include "canvas/scroll_group.h"
42 #include "canvas/text.h"
43 #include "canvas/debug.h"
44
45 #include "ardour_ui.h"
46 #include "automation_time_axis.h"
47 #include "editor.h"
48 #include "editing.h"
49 #include "rgb_macros.h"
50 #include "utils.h"
51 #include "audio_time_axis.h"
52 #include "editor_drag.h"
53 #include "region_view.h"
54 #include "editor_group_tabs.h"
55 #include "editor_summary.h"
56 #include "video_timeline.h"
57 #include "keyboard.h"
58 #include "editor_cursors.h"
59 #include "mouse_cursors.h"
60 #include "note_base.h"
61 #include "region_peak_cursor.h"
62 #include "ui_config.h"
63 #include "verbose_cursor.h"
64
65 #include "pbd/i18n.h"
66
67 using namespace std;
68 using namespace ARDOUR;
69 using namespace ARDOUR_UI_UTILS;
70 using namespace PBD;
71 using namespace Gtk;
72 using namespace Glib;
73 using namespace Gtkmm2ext;
74 using namespace Editing;
75
76 void
initialize_canvas()77 Editor::initialize_canvas ()
78 {
79 _track_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, vertical_adjustment);
80 _track_canvas = _track_canvas_viewport->canvas ();
81
82 _track_canvas->set_background_color (UIConfiguration::instance().color ("arrange base"));
83 _track_canvas->use_nsglview ();
84
85 /* scroll group for items that should not automatically scroll
86 * (e.g verbose cursor). It shares the canvas coordinate space.
87 */
88 no_scroll_group = new ArdourCanvas::Container (_track_canvas->root());
89
90 ArdourCanvas::ScrollGroup* hsg;
91 ArdourCanvas::ScrollGroup* hg;
92 ArdourCanvas::ScrollGroup* cg;
93
94 h_scroll_group = hg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
95 CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll");
96 _track_canvas->add_scroller (*hg);
97
98 hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_track_canvas->root(),
99 ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically|
100 ArdourCanvas::ScrollGroup::ScrollsHorizontally));
101 CANVAS_DEBUG_NAME (hv_scroll_group, "canvas hv scroll");
102 _track_canvas->add_scroller (*hsg);
103
104 cursor_scroll_group = cg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
105 CANVAS_DEBUG_NAME (cursor_scroll_group, "canvas cursor scroll");
106 _track_canvas->add_scroller (*cg);
107
108 _verbose_cursor = new VerboseCursor (this);
109 _region_peak_cursor = new RegionPeakCursor (get_noscroll_group ());
110
111 /*a group to hold global rects like punch/loop indicators */
112 global_rect_group = new ArdourCanvas::Container (hv_scroll_group);
113 CANVAS_DEBUG_NAME (global_rect_group, "global rect group");
114
115 transport_loop_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
116 CANVAS_DEBUG_NAME (transport_loop_range_rect, "loop rect");
117 transport_loop_range_rect->hide();
118
119 transport_punch_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
120 CANVAS_DEBUG_NAME (transport_punch_range_rect, "punch rect");
121 transport_punch_range_rect->hide();
122
123 /*a group to hold time (measure) lines */
124 time_line_group = new ArdourCanvas::Container (h_scroll_group);
125 CANVAS_DEBUG_NAME (time_line_group, "time line group");
126
127 _trackview_group = new ArdourCanvas::Container (hv_scroll_group);
128 CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
129
130 // used as rubberband rect
131 rubberband_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
132 rubberband_rect->hide();
133
134 /* a group to hold stuff while it gets dragged around. Must be the
135 * uppermost (last) group with hv_scroll_group as a parent
136 */
137 _drag_motion_group = new ArdourCanvas::Container (hv_scroll_group);
138 CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion");
139
140 /* TIME BAR CANVAS */
141
142 _time_markers_group = new ArdourCanvas::Container (h_scroll_group);
143 CANVAS_DEBUG_NAME (_time_markers_group, "time bars");
144
145 cd_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, 0.0));
146 CANVAS_DEBUG_NAME (cd_marker_group, "cd marker group");
147 /* the vide is temporarily placed a the same location as the
148 cd_marker_group, but is moved later.
149 */
150 videotl_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0));
151 CANVAS_DEBUG_NAME (videotl_group, "videotl group");
152 marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, timebar_height + 1.0));
153 CANVAS_DEBUG_NAME (marker_group, "marker group");
154 transport_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 2.0) + 1.0));
155 CANVAS_DEBUG_NAME (transport_marker_group, "transport marker group");
156 range_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 3.0) + 1.0));
157 CANVAS_DEBUG_NAME (range_marker_group, "range marker group");
158 tempo_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 4.0) + 1.0));
159 CANVAS_DEBUG_NAME (tempo_group, "tempo group");
160 meter_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0));
161 CANVAS_DEBUG_NAME (meter_group, "meter group");
162
163 meter_bar = new ArdourCanvas::Rectangle (meter_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
164 CANVAS_DEBUG_NAME (meter_bar, "meter Bar");
165 meter_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
166
167 tempo_bar = new ArdourCanvas::Rectangle (tempo_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
168 CANVAS_DEBUG_NAME (tempo_bar, "Tempo Bar");
169 tempo_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
170
171 range_marker_bar = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
172 CANVAS_DEBUG_NAME (range_marker_bar, "Range Marker Bar");
173 range_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
174
175 transport_marker_bar = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
176 CANVAS_DEBUG_NAME (transport_marker_bar, "transport Marker Bar");
177 transport_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
178
179 marker_bar = new ArdourCanvas::Rectangle (marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
180 CANVAS_DEBUG_NAME (marker_bar, "Marker Bar");
181 marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
182
183 cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
184 CANVAS_DEBUG_NAME (cd_marker_bar, "CD Marker Bar");
185 cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
186
187 ARDOUR_UI::instance()->video_timeline = new VideoTimeLine(this, videotl_group, (timebar_height * videotl_bar_height));
188
189 cd_marker_bar_drag_rect = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
190 CANVAS_DEBUG_NAME (cd_marker_bar_drag_rect, "cd marker drag");
191 cd_marker_bar_drag_rect->set_outline (false);
192 cd_marker_bar_drag_rect->hide ();
193
194 range_bar_drag_rect = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
195 CANVAS_DEBUG_NAME (range_bar_drag_rect, "range drag");
196 range_bar_drag_rect->set_outline (false);
197 range_bar_drag_rect->hide ();
198
199 transport_bar_drag_rect = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
200 CANVAS_DEBUG_NAME (transport_bar_drag_rect, "transport drag");
201 transport_bar_drag_rect->set_outline (false);
202 transport_bar_drag_rect->hide ();
203
204 transport_punchin_line = new ArdourCanvas::Line (hv_scroll_group);
205 transport_punchin_line->set_x0 (0);
206 transport_punchin_line->set_y0 (0);
207 transport_punchin_line->set_x1 (0);
208 transport_punchin_line->set_y1 (ArdourCanvas::COORD_MAX);
209 transport_punchin_line->hide ();
210
211 transport_punchout_line = new ArdourCanvas::Line (hv_scroll_group);
212 transport_punchout_line->set_x0 (0);
213 transport_punchout_line->set_y0 (0);
214 transport_punchout_line->set_x1 (0);
215 transport_punchout_line->set_y1 (ArdourCanvas::COORD_MAX);
216 transport_punchout_line->hide();
217
218 tempo_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_tempo_bar_event), tempo_bar));
219 meter_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_meter_bar_event), meter_bar));
220 marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_marker_bar_event), marker_bar));
221 cd_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_cd_marker_bar_event), cd_marker_bar));
222 videotl_group->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_videotl_bar_event), videotl_group));
223 range_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_range_marker_bar_event), range_marker_bar));
224 transport_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_transport_marker_bar_event), transport_marker_bar));
225
226 _playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event);
227
228 _snapped_cursor = new EditorCursor (*this);
229
230 _canvas_drop_zone = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, 0.0));
231 /* this thing is transparent */
232 _canvas_drop_zone->set_fill (false);
233 _canvas_drop_zone->set_outline (false);
234 _canvas_drop_zone->Event.connect (sigc::mem_fun (*this, &Editor::canvas_drop_zone_event));
235
236 /* these signals will initially be delivered to the canvas itself, but if they end up remaining unhandled, they are passed to Editor-level
237 handlers.
238 */
239
240 _track_canvas->signal_scroll_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_scroll_event), true));
241 _track_canvas->signal_motion_notify_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_motion_notify_event));
242 _track_canvas->signal_button_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_press_event));
243 _track_canvas->signal_button_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_release_event));
244 _track_canvas->signal_drag_motion().connect (sigc::mem_fun (*this, &Editor::track_canvas_drag_motion));
245 _track_canvas->signal_key_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_press));
246 _track_canvas->signal_key_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_key_release));
247
248 _track_canvas->set_name ("EditorMainCanvas");
249 _track_canvas->add_events (Gdk::POINTER_MOTION_HINT_MASK | Gdk::SCROLL_MASK | Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
250 _track_canvas->signal_leave_notify_event().connect (sigc::mem_fun(*this, &Editor::left_track_canvas), false);
251 _track_canvas->signal_enter_notify_event().connect (sigc::mem_fun(*this, &Editor::entered_track_canvas), false);
252 _track_canvas->set_flags (CAN_FOCUS);
253
254 _track_canvas->PreRender.connect (sigc::mem_fun(*this, &Editor::pre_render));
255
256 /* set up drag-n-drop */
257
258 vector<TargetEntry> target_table;
259
260 target_table.push_back (TargetEntry ("regions")); // DnD from the region list will generate this target
261 target_table.push_back (TargetEntry ("sources")); // DnD from the source list will generate this target
262 target_table.push_back (TargetEntry ("text/uri-list"));
263 target_table.push_back (TargetEntry ("text/plain"));
264 target_table.push_back (TargetEntry ("application/x-rootwin-drop"));
265
266 _track_canvas->drag_dest_set (target_table);
267 _track_canvas->signal_drag_data_received().connect (sigc::mem_fun(*this, &Editor::track_canvas_drag_data_received));
268
269 _track_canvas_viewport->signal_size_allocate().connect (sigc::mem_fun(*this, &Editor::track_canvas_viewport_allocate));
270
271 initialize_rulers ();
272
273 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &Editor::color_handler));
274 color_handler();
275
276 }
277
278 void
track_canvas_viewport_allocate(Gtk::Allocation alloc)279 Editor::track_canvas_viewport_allocate (Gtk::Allocation alloc)
280 {
281 _canvas_viewport_allocation = alloc;
282 track_canvas_viewport_size_allocated ();
283 }
284
285 void
track_canvas_viewport_size_allocated()286 Editor::track_canvas_viewport_size_allocated ()
287 {
288 bool height_changed = _visible_canvas_height != _canvas_viewport_allocation.get_height();
289
290 _visible_canvas_width = _canvas_viewport_allocation.get_width ();
291 _visible_canvas_height = _canvas_viewport_allocation.get_height ();
292
293 _canvas_drop_zone->set_y1 (_canvas_drop_zone->y0() + (_visible_canvas_height - 20.0));
294
295 // SHOWTRACKS
296
297 if (height_changed) {
298
299 vertical_adjustment.set_page_size (_visible_canvas_height);
300 if ((vertical_adjustment.get_value() + _visible_canvas_height) >= vertical_adjustment.get_upper()) {
301 /*
302 We're increasing the size of the canvas while the bottom is visible.
303 We scroll down to keep in step with the controls layout.
304 */
305 vertical_adjustment.set_value (_full_canvas_height - _visible_canvas_height);
306 }
307
308 set_visible_track_count (_visible_track_count);
309 }
310
311 update_fixed_rulers();
312 update_tempo_based_rulers ();
313 redisplay_grid (false);
314 _summary->set_overlays_dirty ();
315 }
316
317 void
reset_controls_layout_width()318 Editor::reset_controls_layout_width ()
319 {
320 GtkRequisition req = { 0, 0 };
321 gint w;
322
323 edit_controls_vbox.size_request (req);
324 w = req.width;
325
326 if (_group_tabs->is_visible()) {
327 _group_tabs->size_request (req);
328 w += req.width;
329 }
330
331 /* the controls layout has no horizontal scrolling, its visible
332 width is always equal to the total width of its contents.
333 */
334
335 controls_layout.property_width() = w;
336 controls_layout.property_width_request() = w;
337 }
338
339 void
reset_controls_layout_height(int32_t h)340 Editor::reset_controls_layout_height (int32_t h)
341 {
342 /* ensure that the rect that represents the "bottom" of the canvas
343 * (the drag-n-drop zone) is, in fact, at the bottom.
344 */
345
346 _canvas_drop_zone->set_position (ArdourCanvas::Duple (0, h));
347
348 /* track controls layout must span the full height of "h" (all tracks)
349 * plus the bottom rect.
350 */
351
352 h += _canvas_drop_zone->height ();
353
354 /* set the height of the scrollable area (i.e. the sum of all contained widgets)
355 * for the controls layout. The size request is set elsewhere.
356 */
357
358 controls_layout.property_height() = h;
359
360 }
361
362 bool
track_canvas_map_handler(GdkEventAny *)363 Editor::track_canvas_map_handler (GdkEventAny* /*ev*/)
364 {
365 if (!_cursor_stack.empty()) {
366 set_canvas_cursor (get_canvas_cursor());
367 } else {
368 PBD::error << "cursor stack is empty" << endmsg;
369 }
370 return false;
371 }
372
373 /** This is called when something is dropped onto the track canvas */
374 void
track_canvas_drag_data_received(const RefPtr<Gdk::DragContext> & context,int x,int y,const SelectionData & data,guint info,guint time)375 Editor::track_canvas_drag_data_received (const RefPtr<Gdk::DragContext>& context,
376 int x, int y,
377 const SelectionData& data,
378 guint info, guint time)
379 {
380 if (!ARDOUR_UI_UTILS::engine_is_running ()) {
381 return;
382 }
383 if (data.get_target() == X_("regions")) {
384 drop_regions (context, x, y, data, info, time, true);
385 } else if (data.get_target() == X_("sources")) {
386 drop_regions (context, x, y, data, info, time, false);
387 } else {
388 drop_paths (context, x, y, data, info, time);
389 }
390 }
391
392 bool
idle_drop_paths(vector<string> paths,samplepos_t sample,double ypos,bool copy)393 Editor::idle_drop_paths (vector<string> paths, samplepos_t sample, double ypos, bool copy)
394 {
395 drop_paths_part_two (paths, sample, ypos, copy);
396 return false;
397 }
398
399 void
drop_paths_part_two(const vector<string> & paths,samplepos_t sample,double ypos,bool copy)400 Editor::drop_paths_part_two (const vector<string>& paths, samplepos_t sample, double ypos, bool copy)
401 {
402 RouteTimeAxisView* tv;
403
404 /* MIDI files must always be imported, because we consider them
405 * writable. So split paths into two vectors, and follow the import
406 * path on the MIDI part.
407 */
408
409 vector<string> midi_paths;
410 vector<string> audio_paths;
411
412 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
413 if (SMFSource::safe_midi_file_extension (*i)) {
414 midi_paths.push_back (*i);
415 } else {
416 audio_paths.push_back (*i);
417 }
418 }
419
420
421 std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (ypos, false);
422 if (tvp.first == 0) {
423
424 /* drop onto canvas background: create new tracks */
425
426 InstrumentSelector is; // instantiation builds instrument-list and sets default.
427 do_import (midi_paths, Editing::ImportDistinctFiles, ImportAsTrack, SrcBest, SMFTrackName, SMFTempoIgnore, sample, is.selected_instrument(), false);
428
429 if (UIConfiguration::instance().get_only_copy_imported_files() || copy) {
430 do_import (audio_paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack,
431 SrcBest, SMFTrackName, SMFTempoIgnore, sample);
432 } else {
433 do_embed (audio_paths, Editing::ImportDistinctFiles, ImportAsTrack, sample);
434 }
435
436 } else if ((tv = dynamic_cast<RouteTimeAxisView*> (tvp.first)) != 0) {
437
438 /* check that its a track, not a bus */
439
440 if (tv->track()) {
441 /* select the track, then embed/import */
442 selection->set (tv);
443
444 do_import (midi_paths, Editing::ImportSerializeFiles, ImportToTrack,
445 SrcBest, SMFTrackName, SMFTempoIgnore, sample);
446
447 if (UIConfiguration::instance().get_only_copy_imported_files() || copy) {
448 do_import (audio_paths, Editing::ImportSerializeFiles, Editing::ImportToTrack,
449 SrcBest, SMFTrackName, SMFTempoIgnore, sample, boost::shared_ptr<PluginInfo>(), false);
450 } else {
451 do_embed (audio_paths, Editing::ImportSerializeFiles, ImportToTrack, sample);
452 }
453 }
454 }
455 }
456
457 void
drop_paths(const RefPtr<Gdk::DragContext> & context,int x,int y,const SelectionData & data,guint info,guint time)458 Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
459 int x, int y,
460 const SelectionData& data,
461 guint info, guint time)
462 {
463 vector<string> paths;
464 GdkEvent ev;
465 double cy;
466
467 if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
468
469 /* D-n-D coordinates are window-relative, so convert to canvas coordinates
470 */
471
472 ev.type = GDK_BUTTON_RELEASE;
473 ev.button.x = x;
474 ev.button.y = y;
475
476 MusicSample when (window_event_sample (&ev, 0, &cy), 0);
477 snap_to (when);
478
479 bool copy = ((context->get_actions() & (Gdk::ACTION_COPY | Gdk::ACTION_LINK | Gdk::ACTION_MOVE)) == Gdk::ACTION_COPY);
480 #ifdef __APPLE__
481 /* We are not allowed to call recursive main event loops from within
482 the main event loop with GTK/Quartz. Since import/embed wants
483 to push up a progress dialog, defer all this till we go idle.
484 */
485 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun (*this, &Editor::idle_drop_paths), paths, when.sample, cy, copy));
486 #else
487 drop_paths_part_two (paths, when.sample, cy, copy);
488 #endif
489 }
490
491 context->drag_finish (true, false, time);
492 }
493
494 /** @param allow_horiz true to allow horizontal autoscroll, otherwise false.
495 *
496 * @param allow_vert true to allow vertical autoscroll, otherwise false.
497 *
498 */
499 void
maybe_autoscroll(bool allow_horiz,bool allow_vert,bool from_headers)500 Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
501 {
502 Gtk::Window* toplevel = dynamic_cast<Gtk::Window*>(contents().get_toplevel());
503
504 if (!toplevel) {
505 return;
506 }
507
508 if (!UIConfiguration::instance().get_autoscroll_editor () || autoscroll_active ()) {
509 return;
510 }
511
512 /* define a rectangular boundary for scrolling. If the mouse moves
513 * outside of this area and/or continue to be outside of this area,
514 * then we will continuously auto-scroll the canvas in the appropriate
515 * direction(s)
516 *
517 * the boundary is defined in coordinates relative to the toplevel
518 * window since that is what we're going to call ::get_pointer() on
519 * during autoscrolling to determine if we're still outside the
520 * boundary or not.
521 */
522
523 ArdourCanvas::Rect scrolling_boundary;
524 Gtk::Allocation alloc;
525
526 if (from_headers) {
527 alloc = controls_layout.get_allocation ();
528
529 int wx, wy;
530
531 controls_layout.get_parent()->translate_coordinates (*toplevel,
532 alloc.get_x(), alloc.get_y(),
533 wx, wy);
534
535 scrolling_boundary = ArdourCanvas::Rect (wx, wy, wx + alloc.get_width(), wy + alloc.get_height());
536
537
538 } else {
539 alloc = _track_canvas_viewport->get_allocation ();
540
541 /* reduce height by the height of the timebars, which happens
542 to correspond to the position of the hv_scroll_group.
543 */
544
545 alloc.set_height (alloc.get_height() - hv_scroll_group->position().y);
546 alloc.set_y (alloc.get_y() + hv_scroll_group->position().y);
547
548 /* now reduce it again so that we start autoscrolling before we
549 * move off the top or bottom of the canvas
550 */
551
552 alloc.set_height (alloc.get_height() - 20);
553 alloc.set_y (alloc.get_y() + 10);
554
555 /* the effective width of the autoscroll boundary so
556 that we start scrolling before we hit the edge.
557
558 this helps when the window is slammed up against the
559 right edge of the screen, making it hard to scroll
560 effectively.
561 */
562
563 if (alloc.get_width() > 20) {
564 alloc.set_width (alloc.get_width() - 20);
565 alloc.set_x (alloc.get_x() + 10);
566 }
567
568 int wx, wy;
569
570 _track_canvas_viewport->get_parent()->translate_coordinates (*toplevel,
571 alloc.get_x(), alloc.get_y(),
572 wx, wy);
573
574 scrolling_boundary = ArdourCanvas::Rect (wx, wy, wx + alloc.get_width(), wy + alloc.get_height());
575 }
576
577 int x, y;
578 Gdk::ModifierType mask;
579
580 toplevel->get_window()->get_pointer (x, y, mask);
581
582 if ((allow_horiz && ((x < scrolling_boundary.x0 && _leftmost_sample > 0) || x >= scrolling_boundary.x1)) ||
583 (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) {
584 start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary);
585 }
586 }
587
588 bool
drag_active() const589 Editor::drag_active () const
590 {
591 return _drags->active();
592 }
593
594 bool
preview_video_drag_active() const595 Editor::preview_video_drag_active () const
596 {
597 return _drags->preview_video ();
598 }
599
600 bool
autoscroll_active() const601 Editor::autoscroll_active () const
602 {
603 return autoscroll_connection.connected ();
604 }
605
606 std::pair <samplepos_t,samplepos_t>
session_gui_extents(bool use_extra) const607 Editor::session_gui_extents (bool use_extra) const
608 {
609 if (!_session) {
610 return std::pair <samplepos_t,samplepos_t>(max_samplepos,0);
611 }
612
613 samplecnt_t session_extent_start = _session->current_start_sample();
614 samplecnt_t session_extent_end = _session->current_end_sample();
615
616 /* calculate the extents of all regions in every playlist
617 * NOTE: we should listen to playlists, and cache these values so we don't calculate them every time.
618 */
619 {
620 boost::shared_ptr<RouteList> rl = _session->get_routes();
621 for (RouteList::iterator r = rl->begin(); r != rl->end(); ++r) {
622 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*r);
623 if (!tr) {
624 continue;
625 }
626 if (tr->presentation_info ().hidden ()) {
627 continue;
628 }
629 pair<samplepos_t, samplepos_t> e = tr->playlist()->get_extent ();
630 if (e.first == e.second) {
631 /* no regions present */
632 continue;
633 }
634 session_extent_start = std::min (session_extent_start, e.first);
635 session_extent_end = std::max (session_extent_end, e.second);
636 }
637 }
638
639 /* ToDo: also incorporate automation regions (in case the session has no audio/midi but is just used for automating plugins or the like) */
640
641 /* add additional time to the ui extents (user-defined in config) */
642 if (use_extra) {
643 samplecnt_t const extra = UIConfiguration::instance().get_extra_ui_extents_time() * 60 * _session->nominal_sample_rate();
644 session_extent_end += extra;
645 session_extent_start -= extra;
646 }
647
648 /* range-check */
649 if (session_extent_end > max_samplepos) {
650 session_extent_end = max_samplepos;
651 }
652 if (session_extent_start < 0) {
653 session_extent_start = 0;
654 }
655
656 std::pair <samplepos_t,samplepos_t> ret (session_extent_start, session_extent_end);
657 return ret;
658 }
659
660 bool
autoscroll_canvas()661 Editor::autoscroll_canvas ()
662 {
663 int x, y;
664 Gdk::ModifierType mask;
665 sampleoffset_t dx = 0;
666 bool no_stop = false;
667 Gtk::Window* toplevel = dynamic_cast<Gtk::Window*>(contents().get_toplevel());
668
669 if (!toplevel) {
670 return false;
671 }
672
673 toplevel->get_window()->get_pointer (x, y, mask);
674
675 VisualChange vc;
676 bool vertical_motion = false;
677
678 if (autoscroll_horizontal_allowed) {
679
680 samplepos_t new_sample = _leftmost_sample;
681
682 /* horizontal */
683
684 if (x > autoscroll_boundary.x1) {
685
686 /* bring it back into view */
687 dx = x - autoscroll_boundary.x1;
688 dx += 10 + (2 * (autoscroll_cnt/2));
689
690 dx = pixel_to_sample (dx);
691
692 dx *= UIConfiguration::instance().get_draggable_playhead_speed();
693
694 if (_leftmost_sample < max_samplepos - dx) {
695 new_sample = _leftmost_sample + dx;
696 } else {
697 new_sample = max_samplepos;
698 }
699
700 no_stop = true;
701
702 } else if (x < autoscroll_boundary.x0) {
703
704 dx = autoscroll_boundary.x0 - x;
705 dx += 10 + (2 * (autoscroll_cnt/2));
706
707 dx = pixel_to_sample (dx);
708
709 dx *= UIConfiguration::instance().get_draggable_playhead_speed();
710
711 if (_leftmost_sample >= dx) {
712 new_sample = _leftmost_sample - dx;
713 } else {
714 new_sample = 0;
715 }
716
717 no_stop = true;
718 }
719
720 if (new_sample != _leftmost_sample) {
721 vc.time_origin = new_sample;
722 vc.add (VisualChange::TimeOrigin);
723 }
724 }
725
726 if (autoscroll_vertical_allowed) {
727
728 // const double vertical_pos = vertical_adjustment.get_value();
729 const int speed_factor = 10;
730
731 /* vertical */
732
733 if (y < autoscroll_boundary.y0) {
734
735 /* scroll to make higher tracks visible */
736
737 if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
738 scroll_up_one_track ();
739 vertical_motion = true;
740 }
741 no_stop = true;
742
743 } else if (y > autoscroll_boundary.y1) {
744
745 if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
746 scroll_down_one_track ();
747 vertical_motion = true;
748 }
749 no_stop = true;
750 }
751
752 }
753
754 if (vc.pending || vertical_motion) {
755
756 /* change horizontal first */
757
758 if (vc.pending) {
759 visual_changer (vc);
760 }
761
762 /* now send a motion event to notify anyone who cares
763 that we have moved to a new location (because we scrolled)
764 */
765
766 GdkEventMotion ev;
767
768 ev.type = GDK_MOTION_NOTIFY;
769 ev.state = Gdk::BUTTON1_MASK;
770
771 /* the motion handler expects events in canvas coordinate space */
772
773 /* we asked for the mouse position above (::get_pointer()) via
774 * our own top level window (we being the Editor). Convert into
775 * coordinates within the canvas window.
776 */
777
778 int cx;
779 int cy;
780
781 toplevel->translate_coordinates (*_track_canvas, x, y, cx, cy);
782
783 /* clamp x and y to remain within the autoscroll boundary,
784 * which is defined in window coordinates
785 */
786
787 x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1);
788 y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1);
789
790 /* now convert from Editor window coordinates to canvas
791 * window coordinates
792 */
793
794 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
795 ev.x = d.x;
796 ev.y = d.y;
797 ev.state = mask;
798
799 motion_handler (0, (GdkEvent*) &ev, true);
800
801 } else if (no_stop) {
802
803 /* not changing visual state but pointer is outside the scrolling boundary
804 * so we still need to deliver a fake motion event
805 */
806
807 GdkEventMotion ev;
808
809 ev.type = GDK_MOTION_NOTIFY;
810 ev.state = Gdk::BUTTON1_MASK;
811
812 /* the motion handler expects events in canvas coordinate space */
813
814 /* first convert from Editor window coordinates to canvas
815 * window coordinates
816 */
817
818 int cx;
819 int cy;
820
821 /* clamp x and y to remain within the visible area. except
822 * .. if horizontal scrolling is allowed, always allow us to
823 * move back to zero
824 */
825
826 if (autoscroll_horizontal_allowed) {
827 x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1);
828 } else {
829 x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
830 }
831 y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
832
833 toplevel->translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
834
835 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
836 ev.x = d.x;
837 ev.y = d.y;
838 ev.state = mask;
839
840 motion_handler (0, (GdkEvent*) &ev, true);
841
842 } else {
843 stop_canvas_autoscroll ();
844 return false;
845 }
846
847 autoscroll_cnt++;
848
849 return true; /* call me again */
850 }
851
852 void
start_canvas_autoscroll(bool allow_horiz,bool allow_vert,const ArdourCanvas::Rect & boundary)853 Editor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary)
854 {
855 if (!_session) {
856 return;
857 }
858
859 stop_canvas_autoscroll ();
860
861 autoscroll_horizontal_allowed = allow_horiz;
862 autoscroll_vertical_allowed = allow_vert;
863 autoscroll_boundary = boundary;
864
865 /* do the first scroll right now
866 */
867
868 autoscroll_canvas ();
869
870 /* scroll again at very very roughly 30FPS */
871
872 autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::autoscroll_canvas), 30);
873 }
874
875 void
stop_canvas_autoscroll()876 Editor::stop_canvas_autoscroll ()
877 {
878 autoscroll_connection.disconnect ();
879 autoscroll_cnt = 0;
880 }
881
882 Editor::EnterContext*
get_enter_context(ItemType type)883 Editor::get_enter_context(ItemType type)
884 {
885 for (ssize_t i = _enter_stack.size() - 1; i >= 0; --i) {
886 if (_enter_stack[i].item_type == type) {
887 return &_enter_stack[i];
888 }
889 }
890 return NULL;
891 }
892
893 bool
left_track_canvas(GdkEventCrossing * ev)894 Editor::left_track_canvas (GdkEventCrossing* ev)
895 {
896 const bool was_within = within_track_canvas;
897 DropDownKeys ();
898 within_track_canvas = false;
899 set_entered_track (0);
900 set_entered_regionview (0);
901 reset_canvas_action_sensitivity (false);
902
903 if (was_within) {
904 if (ev->detail == GDK_NOTIFY_NONLINEAR ||
905 ev->detail == GDK_NOTIFY_NONLINEAR_VIRTUAL) {
906 /* context menu or something similar */
907 sensitize_the_right_region_actions (false);
908 } else {
909 sensitize_the_right_region_actions (true);
910 }
911 }
912
913 return false;
914 }
915
916 bool
entered_track_canvas(GdkEventCrossing * ev)917 Editor::entered_track_canvas (GdkEventCrossing* ev)
918 {
919 const bool was_within = within_track_canvas;
920 within_track_canvas = true;
921 reset_canvas_action_sensitivity (true);
922
923 if (!was_within) {
924
925 if (internal_editing()) {
926 /* ensure that key events go here because there are
927 internal editing bindings associated only with the
928 canvas. if the focus is elsewhere, we cannot find them.
929 */
930 _track_canvas->grab_focus ();
931 }
932
933 if (ev->detail == GDK_NOTIFY_NONLINEAR ||
934 ev->detail == GDK_NOTIFY_NONLINEAR_VIRTUAL) {
935 /* context menu or something similar */
936 sensitize_the_right_region_actions (false);
937 } else {
938 sensitize_the_right_region_actions (true);
939 }
940 }
941
942 return false;
943 }
944
945 void
ensure_time_axis_view_is_visible(TimeAxisView const & track,bool at_top)946 Editor::ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top)
947 {
948 if (track.hidden()) {
949 return;
950 }
951
952 /* compute visible area of trackview group, as offsets from top of
953 * trackview group.
954 */
955
956 double const current_view_min_y = vertical_adjustment.get_value();
957 double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size();
958
959 double const track_min_y = track.y_position ();
960 double const track_max_y = track.y_position () + track.effective_height ();
961
962 if (!at_top &&
963 (track_min_y >= current_view_min_y &&
964 track_max_y < current_view_max_y)) {
965 /* already visible, and caller did not ask to place it at the
966 * top of the track canvas
967 */
968 return;
969 }
970
971 double new_value;
972
973 if (at_top) {
974 new_value = track_min_y;
975 } else {
976 if (track_min_y < current_view_min_y) {
977 // Track is above the current view
978 new_value = track_min_y;
979 } else if (track_max_y > current_view_max_y) {
980 // Track is below the current view
981 new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size();
982 } else {
983 new_value = track_min_y;
984 }
985 }
986
987 vertical_adjustment.set_value(new_value);
988 }
989
990 /** Called when the main vertical_adjustment has changed */
991 void
tie_vertical_scrolling()992 Editor::tie_vertical_scrolling ()
993 {
994 if (pending_visual_change.idle_handler_id < 0) {
995 _region_peak_cursor->hide ();
996 _summary->set_overlays_dirty ();
997 }
998 }
999
1000 void
set_horizontal_position(double p)1001 Editor::set_horizontal_position (double p)
1002 {
1003 horizontal_adjustment.set_value (p);
1004
1005 _leftmost_sample = (samplepos_t) floor (p * samples_per_pixel);
1006 }
1007
1008 void
color_handler()1009 Editor::color_handler()
1010 {
1011 Gtkmm2ext::Color base = UIConfiguration::instance().color ("ruler base");
1012 Gtkmm2ext::Color text = UIConfiguration::instance().color ("ruler text");
1013 timecode_ruler->set_fill_color (base);
1014 timecode_ruler->set_outline_color (text);
1015 minsec_ruler->set_fill_color (base);
1016 minsec_ruler->set_outline_color (text);
1017 samples_ruler->set_fill_color (base);
1018 samples_ruler->set_outline_color (text);
1019 bbt_ruler->set_fill_color (base);
1020 bbt_ruler->set_outline_color (text);
1021
1022 _playhead_cursor->set_color (UIConfiguration::instance().color ("play head"));
1023
1024 meter_bar->set_fill_color (UIConfiguration::instance().color_mod ("meter bar", "marker bar"));
1025 meter_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
1026
1027 tempo_bar->set_fill_color (UIConfiguration::instance().color_mod ("tempo bar", "marker bar"));
1028 tempo_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
1029
1030 marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("marker bar", "marker bar"));
1031 marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
1032
1033 cd_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("cd marker bar", "marker bar"));
1034 cd_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
1035
1036 range_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("range marker bar", "marker bar"));
1037 range_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
1038
1039 transport_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("transport marker bar", "marker bar"));
1040 transport_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator"));
1041
1042 cd_marker_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect"));
1043 cd_marker_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect"));
1044
1045 range_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect"));
1046 range_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect"));
1047
1048 transport_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("transport drag rect"));
1049 transport_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("transport drag rect"));
1050
1051 transport_loop_range_rect->set_fill_color (UIConfiguration::instance().color_mod ("transport loop rect", "loop rectangle"));
1052 transport_loop_range_rect->set_outline_color (UIConfiguration::instance().color ("transport loop rect"));
1053
1054 transport_punch_range_rect->set_fill_color (UIConfiguration::instance().color ("transport punch rect"));
1055 transport_punch_range_rect->set_outline_color (UIConfiguration::instance().color ("transport punch rect"));
1056
1057 transport_punchin_line->set_outline_color (UIConfiguration::instance().color ("punch line"));
1058 transport_punchout_line->set_outline_color (UIConfiguration::instance().color ("punch line"));
1059
1060 rubberband_rect->set_outline_color (UIConfiguration::instance().color ("rubber band rect"));
1061 rubberband_rect->set_fill_color (UIConfiguration::instance().color_mod ("rubber band rect", "selection rect"));
1062
1063 location_marker_color = UIConfiguration::instance().color ("location marker");
1064 location_range_color = UIConfiguration::instance().color ("location range");
1065 location_cd_marker_color = UIConfiguration::instance().color ("location cd marker");
1066 location_loop_color = UIConfiguration::instance().color ("location loop");
1067 location_punch_color = UIConfiguration::instance().color ("location punch");
1068
1069 refresh_location_display ();
1070
1071 NoteBase::set_colors ();
1072
1073 /* redraw the whole thing */
1074 _track_canvas->set_background_color (UIConfiguration::instance().color ("arrange base"));
1075 _track_canvas->queue_draw ();
1076
1077 /*
1078 redisplay_grid (true);
1079
1080 if (_session)
1081 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
1082 */
1083 }
1084
1085 double
horizontal_position() const1086 Editor::horizontal_position () const
1087 {
1088 return sample_to_pixel (_leftmost_sample);
1089 }
1090
1091 bool
track_canvas_key_press(GdkEventKey *)1092 Editor::track_canvas_key_press (GdkEventKey*)
1093 {
1094 return false;
1095 }
1096
1097 bool
track_canvas_key_release(GdkEventKey *)1098 Editor::track_canvas_key_release (GdkEventKey*)
1099 {
1100 return false;
1101 }
1102
1103 double
clamp_verbose_cursor_x(double x)1104 Editor::clamp_verbose_cursor_x (double x)
1105 {
1106 if (x < 0) {
1107 x = 0;
1108 } else {
1109 x = min (_visible_canvas_width - 200.0, x);
1110 }
1111 return x;
1112 }
1113
1114 double
clamp_verbose_cursor_y(double y)1115 Editor::clamp_verbose_cursor_y (double y)
1116 {
1117 y = max (0.0, y);
1118 y = min (_visible_canvas_height - 50, y);
1119 return y;
1120 }
1121
1122 ArdourCanvas::GtkCanvasViewport*
get_track_canvas() const1123 Editor::get_track_canvas() const
1124 {
1125 return _track_canvas_viewport;
1126 }
1127
1128 Gdk::Cursor*
get_canvas_cursor() const1129 Editor::get_canvas_cursor () const
1130 {
1131 /* The top of the cursor stack is always the currently visible cursor. */
1132 return _cursor_stack.back();
1133 }
1134
1135 void
set_canvas_cursor(Gdk::Cursor * cursor)1136 Editor::set_canvas_cursor (Gdk::Cursor* cursor)
1137 {
1138 Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
1139
1140 if (win && !_cursors->is_invalid (cursor)) {
1141 /* glibmm 2.4 doesn't allow null cursor pointer because it uses
1142 a Gdk::Cursor& as the argument to Gdk::Window::set_cursor().
1143 But a null pointer just means "use parent window cursor",
1144 and so should be allowed. Gtkmm 3.x has fixed this API.
1145
1146 For now, drop down and use C API
1147 */
1148 gdk_window_set_cursor (win->gobj(), cursor ? cursor->gobj() : 0);
1149 }
1150 }
1151
1152 size_t
push_canvas_cursor(Gdk::Cursor * cursor)1153 Editor::push_canvas_cursor (Gdk::Cursor* cursor)
1154 {
1155 if (!_cursors->is_invalid (cursor)) {
1156 _cursor_stack.push_back (cursor);
1157 set_canvas_cursor (cursor);
1158 }
1159 return _cursor_stack.size() - 1;
1160 }
1161
1162 void
pop_canvas_cursor()1163 Editor::pop_canvas_cursor ()
1164 {
1165 while (true) {
1166 if (_cursor_stack.size() <= 1) {
1167 PBD::error << "attempt to pop default cursor" << endmsg;
1168 return;
1169 }
1170
1171 _cursor_stack.pop_back();
1172 if (_cursor_stack.back()) {
1173 /* Popped to an existing cursor, we're done. Otherwise, the
1174 context that created this cursor has been destroyed, so we need
1175 to skip to the next down the stack. */
1176 set_canvas_cursor (_cursor_stack.back());
1177 return;
1178 }
1179 }
1180 }
1181
1182 Gdk::Cursor*
which_trim_cursor(bool left) const1183 Editor::which_trim_cursor (bool left) const
1184 {
1185 if (!entered_regionview) {
1186 return 0;
1187 }
1188
1189 Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
1190
1191 if (left) {
1192 if (ct & Trimmable::FrontTrimEarlier) {
1193 return _cursors->left_side_trim;
1194 } else {
1195 return _cursors->left_side_trim_right_only;
1196 }
1197 } else {
1198 if (ct & Trimmable::EndTrimLater) {
1199 return _cursors->right_side_trim;
1200 } else {
1201 return _cursors->right_side_trim_left_only;
1202 }
1203 }
1204 }
1205
1206 Gdk::Cursor*
which_mode_cursor() const1207 Editor::which_mode_cursor () const
1208 {
1209 Gdk::Cursor* mode_cursor = MouseCursors::invalid_cursor ();
1210
1211 switch (mouse_mode) {
1212 case MouseRange:
1213 mode_cursor = _cursors->selector;
1214 break;
1215
1216 case MouseCut:
1217 mode_cursor = _cursors->scissors;
1218 break;
1219
1220 case MouseObject:
1221 case MouseContent:
1222 /* don't use mode cursor, pick a grabber cursor based on the item */
1223 break;
1224
1225 case MouseDraw:
1226 mode_cursor = _cursors->midi_pencil;
1227 break;
1228
1229 case MouseTimeFX:
1230 mode_cursor = _cursors->time_fx; // just use playhead
1231 break;
1232
1233 case MouseAudition:
1234 mode_cursor = _cursors->speaker;
1235 break;
1236 }
1237
1238 /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
1239 if (get_smart_mode()) {
1240
1241 double x, y;
1242 get_pointer_position (x, y);
1243
1244 if (x >= 0 && y >= 0) {
1245
1246 vector<ArdourCanvas::Item const *> items;
1247
1248 /* Note how we choose a specific scroll group to get
1249 * items from. This could be problematic.
1250 */
1251
1252 hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
1253
1254 // first item will be the upper most
1255
1256 if (!items.empty()) {
1257 const ArdourCanvas::Item* i = items.front();
1258
1259 if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
1260 pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
1261 if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
1262 mode_cursor = _cursors->up_down;
1263 }
1264 }
1265 }
1266 }
1267 }
1268
1269 return mode_cursor;
1270 }
1271
1272 Gdk::Cursor*
which_track_cursor() const1273 Editor::which_track_cursor () const
1274 {
1275 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
1276
1277 switch (_join_object_range_state) {
1278 case JOIN_OBJECT_RANGE_NONE:
1279 case JOIN_OBJECT_RANGE_OBJECT:
1280 cursor = _cursors->grabber;
1281 break;
1282 case JOIN_OBJECT_RANGE_RANGE:
1283 cursor = _cursors->selector;
1284 break;
1285 }
1286
1287 return cursor;
1288 }
1289
1290 Gdk::Cursor*
which_canvas_cursor(ItemType type) const1291 Editor::which_canvas_cursor(ItemType type) const
1292 {
1293 Gdk::Cursor* cursor = which_mode_cursor ();
1294
1295 if (mouse_mode == MouseRange) {
1296 switch (type) {
1297 case StartSelectionTrimItem:
1298 cursor = _cursors->left_side_trim;
1299 break;
1300 case EndSelectionTrimItem:
1301 cursor = _cursors->right_side_trim;
1302 break;
1303 default:
1304 break;
1305 }
1306 }
1307
1308 if ((mouse_mode == MouseObject || get_smart_mode ()) ||
1309 mouse_mode == MouseContent) {
1310
1311 /* find correct cursor to use in object/smart mode */
1312 switch (type) {
1313 case RegionItem:
1314 /* We don't choose a cursor for these items on top of a region view,
1315 because this would push a new context on the enter stack which
1316 means switching the region context for things like smart mode
1317 won't actualy change the cursor. */
1318 // case WaveItem:
1319 case StreamItem:
1320 case AutomationTrackItem:
1321 cursor = which_track_cursor ();
1322 break;
1323 case PlayheadCursorItem:
1324 cursor = _cursors->grabber;
1325 break;
1326 case SelectionItem:
1327 cursor = _cursors->selector;
1328 break;
1329 case ControlPointItem:
1330 cursor = _cursors->fader;
1331 break;
1332 case GainLineItem:
1333 cursor = _cursors->cross_hair;
1334 break;
1335 case AutomationLineItem:
1336 cursor = _cursors->cross_hair;
1337 break;
1338 case StartSelectionTrimItem:
1339 cursor = _cursors->left_side_trim;
1340 break;
1341 case EndSelectionTrimItem:
1342 cursor = _cursors->right_side_trim;
1343 break;
1344 case FadeInItem:
1345 cursor = _cursors->fade_in;
1346 break;
1347 case FadeInHandleItem:
1348 cursor = _cursors->fade_in;
1349 break;
1350 case FadeInTrimHandleItem:
1351 cursor = _cursors->fade_in;
1352 break;
1353 case FadeOutItem:
1354 cursor = _cursors->fade_out;
1355 break;
1356 case FadeOutHandleItem:
1357 cursor = _cursors->fade_out;
1358 break;
1359 case FadeOutTrimHandleItem:
1360 cursor = _cursors->fade_out;
1361 break;
1362 case FeatureLineItem:
1363 cursor = _cursors->cross_hair;
1364 break;
1365 case LeftFrameHandle:
1366 if (effective_mouse_mode() == MouseObject) // (smart mode): if the user is in the btm half, show the trim cursor
1367 cursor = which_trim_cursor (true);
1368 else
1369 cursor = _cursors->selector; // (smart mode): in the top half, just show the selection (range) cursor
1370 break;
1371 case RightFrameHandle:
1372 if (effective_mouse_mode() == MouseObject) // see above
1373 cursor = which_trim_cursor (false);
1374 else
1375 cursor = _cursors->selector;
1376 break;
1377 case RegionViewName:
1378 case RegionViewNameHighlight:
1379 /* the trim bar is used for trimming, but we have to determine if we are on the left or right side of the region */
1380 cursor = MouseCursors::invalid_cursor ();
1381 if (entered_regionview) {
1382 samplepos_t where;
1383 bool in_track_canvas;
1384 if (mouse_sample (where, in_track_canvas)) {
1385 samplepos_t start = entered_regionview->region()->first_sample();
1386 samplepos_t end = entered_regionview->region()->last_sample();
1387 cursor = which_trim_cursor ((where - start) < (end - where));
1388 }
1389 }
1390 break;
1391 case StartCrossFadeItem:
1392 cursor = _cursors->fade_in;
1393 break;
1394 case EndCrossFadeItem:
1395 cursor = _cursors->fade_out;
1396 break;
1397 case CrossfadeViewItem:
1398 cursor = _cursors->cross_hair;
1399 break;
1400 case NoteItem:
1401 cursor = _cursors->grabber_note;
1402 default:
1403 break;
1404 }
1405
1406 } else if (mouse_mode == MouseDraw) {
1407
1408 /* ControlPointItem is not really specific to region gain mode
1409 but it is the same cursor so don't worry about this for now.
1410 The result is that we'll see the fader cursor if we enter
1411 non-region-gain-line control points while in MouseDraw
1412 mode, even though we can't edit them in this mode.
1413 */
1414
1415 switch (type) {
1416 case GainLineItem:
1417 case ControlPointItem:
1418 cursor = _cursors->fader;
1419 break;
1420 case NoteItem:
1421 cursor = _cursors->grabber_note;
1422 default:
1423 break;
1424 }
1425 }
1426
1427 switch (type) {
1428 /* These items use the timebar cursor at all times */
1429 case TimecodeRulerItem:
1430 case MinsecRulerItem:
1431 case BBTRulerItem:
1432 case SamplesRulerItem:
1433 cursor = _cursors->timebar;
1434 break;
1435
1436 /* These items use the grabber cursor at all times */
1437 case MeterMarkerItem:
1438 case TempoMarkerItem:
1439 case MeterBarItem:
1440 case TempoBarItem:
1441 case MarkerItem:
1442 case MarkerBarItem:
1443 case RangeMarkerBarItem:
1444 case CdMarkerBarItem:
1445 case VideoBarItem:
1446 case TransportMarkerBarItem:
1447 case DropZoneItem:
1448 cursor = _cursors->grabber;
1449 break;
1450
1451 default:
1452 break;
1453 }
1454
1455 return cursor;
1456 }
1457
1458 void
choose_canvas_cursor_on_entry(ItemType type)1459 Editor::choose_canvas_cursor_on_entry (ItemType type)
1460 {
1461 if (_drags->active()) {
1462 return;
1463 }
1464
1465 Gdk::Cursor* cursor = which_canvas_cursor(type);
1466
1467 if (!_cursors->is_invalid (cursor)) {
1468 // Push a new enter context
1469 const EnterContext ctx = { type, CursorContext::create(*this, cursor) };
1470 _enter_stack.push_back(ctx);
1471 }
1472 }
1473
1474 void
update_all_enter_cursors()1475 Editor::update_all_enter_cursors ()
1476 {
1477 for (std::vector<EnterContext>::iterator i = _enter_stack.begin(); i != _enter_stack.end(); ++i) {
1478 i->cursor_ctx->change(which_canvas_cursor(i->item_type));
1479 }
1480 }
1481
1482 double
trackviews_height() const1483 Editor::trackviews_height() const
1484 {
1485 if (!_trackview_group) {
1486 return 0;
1487 }
1488
1489 return _visible_canvas_height - _trackview_group->canvas_origin().y;
1490 }
1491