1 /*
2 * Copyright (C) 2005-2007 Doug McLain <doug@nostar.net>
3 * Copyright (C) 2005-2009 Taybin Rutkin <taybin@taybin.com>
4 * Copyright (C) 2005-2019 Paul Davis <paul@linuxaudiosystems.com>
5 * Copyright (C) 2005 Karsten Wiese <fzuuzf@googlemail.com>
6 * Copyright (C) 2006-2009 Sampo Savolainen <v2@iki.fi>
7 * Copyright (C) 2006-2015 David Robillard <d@drobilla.net>
8 * Copyright (C) 2006-2017 Tim Mayberry <mojofunk@gmail.com>
9 * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
10 * Copyright (C) 2008 Hans Baier <hansfbaier@googlemail.com>
11 * Copyright (C) 2012-2019 Robin Gareus <robin@gareus.org>
12 * Copyright (C) 2013-2015 Colin Fletcher <colin.m.fletcher@googlemail.com>
13 * Copyright (C) 2014-2017 Nick Mainsbridge <mainsbridge@gmail.com>
14 * Copyright (C) 2014-2019 Ben Loftis <ben@harrisonconsoles.com>
15 * Copyright (C) 2015 André Nusser <andre.nusser@googlemail.com>
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License along
28 * with this program; if not, write to the Free Software Foundation, Inc.,
29 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30 */
31
32 /* Note: public Editor methods are documented in public_editor.h */
33
34 #include <stdint.h>
35 #include <unistd.h>
36 #include <cstdlib>
37 #include <cmath>
38 #include <string>
39 #include <algorithm>
40 #include <map>
41
42 #include "ardour_ui.h"
43 /*
44 * ardour_ui.h include was moved to the top of the list
45 * due to a conflicting definition of 'Style' between
46 * Apple's MacTypes.h and BarController.
47 */
48
49 #include <boost/none.hpp>
50
51 #include <sigc++/bind.h>
52
53 #include "pbd/convert.h"
54 #include "pbd/error.h"
55 #include "pbd/enumwriter.h"
56 #include "pbd/memento_command.h"
57 #include "pbd/unknown_type.h"
58 #include "pbd/unwind.h"
59 #include "pbd/timersub.h"
60
61 #include <glibmm/datetime.h> /*for playlist group_id */
62 #include <glibmm/miscutils.h>
63 #include <glibmm/uriutils.h>
64 #include <gtkmm/image.h>
65 #include <gdkmm/color.h>
66 #include <gdkmm/bitmap.h>
67
68 #include <gtkmm/menu.h>
69 #include <gtkmm/menuitem.h>
70
71 #include "gtkmm2ext/bindings.h"
72 #include "gtkmm2ext/gtk_ui.h"
73 #include "gtkmm2ext/keyboard.h"
74 #include "gtkmm2ext/utils.h"
75 #include "gtkmm2ext/window_title.h"
76 #include "gtkmm2ext/cell_renderer_pixbuf_toggle.h"
77
78 #include "ardour/analysis_graph.h"
79 #include "ardour/audio_track.h"
80 #include "ardour/audioengine.h"
81 #include "ardour/audioregion.h"
82 #include "ardour/lmath.h"
83 #include "ardour/location.h"
84 #include "ardour/profile.h"
85 #include "ardour/route.h"
86 #include "ardour/route_group.h"
87 #include "ardour/session_playlists.h"
88 #include "ardour/tempo.h"
89 #include "ardour/utils.h"
90 #include "ardour/vca_manager.h"
91 #include "ardour/vca.h"
92
93 #include "canvas/debug.h"
94 #include "canvas/note.h"
95 #include "canvas/text.h"
96
97 #include "widgets/ardour_spacer.h"
98 #include "widgets/eventboxext.h"
99 #include "widgets/tooltips.h"
100 #include "widgets/prompter.h"
101
102 #include "control_protocol/control_protocol.h"
103
104 #include "actions.h"
105 #include "analysis_window.h"
106 #include "ardour_message.h"
107 #include "audio_clock.h"
108 #include "audio_region_view.h"
109 #include "audio_streamview.h"
110 #include "audio_time_axis.h"
111 #include "automation_time_axis.h"
112 #include "bundle_manager.h"
113 #include "crossfade_edit.h"
114 #include "debug.h"
115 #include "editing.h"
116 #include "editing_convert.h"
117 #include "editor.h"
118 #include "editor_cursors.h"
119 #include "editor_drag.h"
120 #include "editor_group_tabs.h"
121 #include "editor_locations.h"
122 #include "editor_regions.h"
123 #include "editor_route_groups.h"
124 #include "editor_routes.h"
125 #include "editor_snapshots.h"
126 #include "editor_sources.h"
127 #include "editor_summary.h"
128 #include "enums_convert.h"
129 #include "export_report.h"
130 #include "global_port_matrix.h"
131 #include "gui_object.h"
132 #include "gui_thread.h"
133 #include "keyboard.h"
134 #include "luainstance.h"
135 #include "marker.h"
136 #include "midi_region_view.h"
137 #include "midi_time_axis.h"
138 #include "mixer_strip.h"
139 #include "mixer_ui.h"
140 #include "mouse_cursors.h"
141 #include "note_base.h"
142 #include "public_editor.h"
143 #include "quantize_dialog.h"
144 #include "region_peak_cursor.h"
145 #include "region_layering_order_editor.h"
146 #include "rgb_macros.h"
147 #include "rhythm_ferret.h"
148 #include "route_sorter.h"
149 #include "selection.h"
150 #include "simple_progress_dialog.h"
151 #include "sfdb_ui.h"
152 #include "grid_lines.h"
153 #include "time_axis_view.h"
154 #include "time_info_box.h"
155 #include "timers.h"
156 #include "ui_config.h"
157 #include "utils.h"
158 #include "vca_time_axis.h"
159 #include "verbose_cursor.h"
160
161 #include "pbd/i18n.h"
162
163 using namespace std;
164 using namespace ARDOUR;
165 using namespace ArdourWidgets;
166 using namespace ARDOUR_UI_UTILS;
167 using namespace PBD;
168 using namespace Gtk;
169 using namespace Glib;
170 using namespace Gtkmm2ext;
171 using namespace Editing;
172
173 using PBD::internationalize;
174 using PBD::atoi;
175 using Gtkmm2ext::Keyboard;
176
177 double Editor::timebar_height = 15.0;
178
179 static const gchar *_grid_type_strings[] = {
180 N_("No Grid"),
181 N_("Bar"),
182 N_("1/4 Note"),
183 N_("1/8 Note"),
184 N_("1/16 Note"),
185 N_("1/32 Note"),
186 N_("1/64 Note"),
187 N_("1/128 Note"),
188 N_("1/3 (8th triplet)"), // or "1/12" ?
189 N_("1/6 (16th triplet)"),
190 N_("1/12 (32nd triplet)"),
191 N_("1/24 (64th triplet)"),
192 N_("1/5 (8th quintuplet)"),
193 N_("1/10 (16th quintuplet)"),
194 N_("1/20 (32nd quintuplet)"),
195 N_("1/7 (8th septuplet)"),
196 N_("1/14 (16th septuplet)"),
197 N_("1/28 (32nd septuplet)"),
198 N_("Timecode"),
199 N_("MinSec"),
200 N_("CD Frames"),
201 0
202 };
203
204 static const gchar *_edit_point_strings[] = {
205 N_("Playhead"),
206 N_("Marker"),
207 N_("Mouse"),
208 0
209 };
210
211 static const gchar *_edit_mode_strings[] = {
212 N_("Slide"),
213 N_("Splice"),
214 N_("Ripple"),
215 N_("Lock"),
216 0
217 };
218
219 static const gchar *_zoom_focus_strings[] = {
220 N_("Left"),
221 N_("Right"),
222 N_("Center"),
223 N_("Playhead"),
224 N_("Mouse"),
225 N_("Edit point"),
226 0
227 };
228
229 #ifdef USE_RUBBERBAND
230 static const gchar *_rb_opt_strings[] = {
231 N_("Mushy"),
232 N_("Smooth"),
233 N_("Balanced multitimbral mixture"),
234 N_("Unpitched percussion with stable notes"),
235 N_("Crisp monophonic instrumental"),
236 N_("Unpitched solo percussion"),
237 N_("Resample without preserving pitch"),
238 #ifdef HAVE_SOUNDTOUCH
239 N_("Vocal"),
240 #endif
241 0
242 };
243 #endif
244
245 /* Robin says: this should be odd to accomodate cairo drawing offset (width/2 rounds up to pixel boundary) */
246 #ifdef __APPLE__
247 #define COMBO_TRIANGLE_WIDTH 19 // ArdourButton _diameter (11) + 2 * arrow-padding (2*2) + 2 * text-padding (2*5)
248 #else
249 #define COMBO_TRIANGLE_WIDTH 11 // as-measured for win/linux.
250 #endif
251
Editor()252 Editor::Editor ()
253 : PublicEditor (global_hpacker)
254 , editor_mixer_strip_width (Wide)
255 , constructed (false)
256 , _time_info_box (0)
257 , no_save_visual (false)
258 , _leftmost_sample (0)
259 , samples_per_pixel (2048)
260 , zoom_focus (ZoomFocusPlayhead)
261 , mouse_mode (MouseObject)
262 , pre_internal_grid_type (GridTypeBeat)
263 , pre_internal_snap_mode (SnapOff)
264 , internal_grid_type (GridTypeBeat)
265 , internal_snap_mode (SnapOff)
266 , _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
267 , _notebook_shrunk (false)
268 , location_marker_color (0)
269 , location_range_color (0)
270 , location_loop_color (0)
271 , location_punch_color (0)
272 , location_cd_marker_color (0)
273 , entered_marker (0)
274 , _show_marker_lines (false)
275 , clicked_axisview (0)
276 , clicked_routeview (0)
277 , clicked_regionview (0)
278 , clicked_selection (0)
279 , clicked_control_point (0)
280 , button_release_can_deselect (true)
281 , _mouse_changed_selection (false)
282 , _popup_region_menu_item (0)
283 , _track_canvas (0)
284 , _track_canvas_viewport (0)
285 , within_track_canvas (false)
286 , _verbose_cursor (0)
287 , _region_peak_cursor (0)
288 , tempo_group (0)
289 , meter_group (0)
290 , marker_group (0)
291 , range_marker_group (0)
292 , transport_marker_group (0)
293 , cd_marker_group (0)
294 , _time_markers_group (0)
295 , hv_scroll_group (0)
296 , h_scroll_group (0)
297 , cursor_scroll_group (0)
298 , no_scroll_group (0)
299 , _trackview_group (0)
300 , _drag_motion_group (0)
301 , _canvas_drop_zone (0)
302 , no_ruler_shown_update (false)
303 , ruler_grabbed_widget (0)
304 , ruler_dialog (0)
305 , minsec_mark_interval (0)
306 , minsec_mark_modulo (0)
307 , minsec_nmarks (0)
308 , timecode_ruler_scale (timecode_show_many_hours)
309 , timecode_mark_modulo (0)
310 , timecode_nmarks (0)
311 , _samples_ruler_interval (0)
312 , bbt_ruler_scale (bbt_show_many)
313 , bbt_bars (0)
314 , bbt_nmarks (0)
315 , bbt_bar_helper_on (0)
316 , timecode_ruler (0)
317 , bbt_ruler (0)
318 , samples_ruler (0)
319 , minsec_ruler (0)
320 , visible_timebars (0)
321 , editor_ruler_menu (0)
322 , tempo_bar (0)
323 , meter_bar (0)
324 , marker_bar (0)
325 , range_marker_bar (0)
326 , transport_marker_bar (0)
327 , cd_marker_bar (0)
328 , minsec_label (_("Mins:Secs"))
329 , bbt_label (_("Bars:Beats"))
330 , timecode_label (_("Timecode"))
331 , samples_label (_("Samples"))
332 , tempo_label (_("Tempo"))
333 , meter_label (_("Meter"))
334 , mark_label (_("Location Markers"))
335 , range_mark_label (_("Range Markers"))
336 , transport_mark_label (_("Loop/Punch Ranges"))
337 , cd_mark_label (_("CD Markers"))
338 , videotl_label (_("Video Timeline"))
339 , videotl_group (0)
340 , _region_boundary_cache_dirty (true)
341 , edit_packer (4, 4, true)
342 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
343 , horizontal_adjustment (0.0, 0.0, 1e16)
344 , unused_adjustment (0.0, 0.0, 10.0, 400.0)
345 , controls_layout (unused_adjustment, vertical_adjustment)
346 , _scroll_callbacks (0)
347 , _visible_canvas_width (0)
348 , _visible_canvas_height (0)
349 , _full_canvas_height (0)
350 , edit_controls_left_menu (0)
351 , edit_controls_right_menu (0)
352 , visual_change_queued(false)
353 , _last_update_time (0)
354 , _err_screen_engine (0)
355 , cut_buffer_start (0)
356 , cut_buffer_length (0)
357 , button_bindings (0)
358 , last_paste_pos (-1)
359 , paste_count (0)
360 , sfbrowser (0)
361 , current_interthread_info (0)
362 , analysis_window (0)
363 , select_new_marker (false)
364 , last_scrub_x (0)
365 , scrubbing_direction (0)
366 , scrub_reversals (0)
367 , scrub_reverse_distance (0)
368 , have_pending_keyboard_selection (false)
369 , pending_keyboard_selection_start (0)
370 , _grid_type (GridTypeBeat)
371 , _snap_mode (SnapOff)
372 , ignore_gui_changes (false)
373 , _drags (new DragManager (this))
374 , lock_dialog (0)
375 /* , last_event_time { 0, 0 } */ /* this initialization style requires C++11 */
376 , _dragging_playhead (false)
377 , _follow_playhead (true)
378 , _stationary_playhead (false)
379 , _maximised (false)
380 , grid_lines (0)
381 , global_rect_group (0)
382 , time_line_group (0)
383 , tempo_marker_menu (0)
384 , meter_marker_menu (0)
385 , marker_menu (0)
386 , range_marker_menu (0)
387 , new_transport_marker_menu (0)
388 , marker_menu_item (0)
389 , _visible_track_count (-1)
390 , toolbar_selection_clock_table (2,3)
391 , automation_mode_button (_("mode"))
392 , selection (new Selection (this, true))
393 , cut_buffer (new Selection (this, false))
394 , _selection_memento (new SelectionMemento())
395 , _all_region_actions_sensitized (false)
396 , _ignore_region_action (false)
397 , _last_region_menu_was_main (false)
398 , _track_selection_change_without_scroll (false)
399 , _editor_track_selection_change_without_scroll (false)
400 , _playhead_cursor (0)
401 , _snapped_cursor (0)
402 , cd_marker_bar_drag_rect (0)
403 , range_bar_drag_rect (0)
404 , transport_bar_drag_rect (0)
405 , transport_bar_range_rect (0)
406 , transport_bar_preroll_rect (0)
407 , transport_bar_postroll_rect (0)
408 , transport_loop_range_rect (0)
409 , transport_punch_range_rect (0)
410 , transport_punchin_line (0)
411 , transport_punchout_line (0)
412 , transport_preroll_rect (0)
413 , transport_postroll_rect (0)
414 , temp_location (0)
415 , rubberband_rect (0)
416 , _route_groups (0)
417 , _routes (0)
418 , _regions (0)
419 , _snapshots (0)
420 , _locations (0)
421 , autoscroll_horizontal_allowed (false)
422 , autoscroll_vertical_allowed (false)
423 , autoscroll_cnt (0)
424 , autoscroll_widget (0)
425 , show_gain_after_trim (false)
426 , selection_op_cmd_depth (0)
427 , selection_op_history_it (0)
428 , no_save_instant (false)
429 , current_timefx (0)
430 , current_mixer_strip (0)
431 , show_editor_mixer_when_tracks_arrive (false)
432 , nudge_clock (new AudioClock (X_("nudge"), false, X_("nudge"), true, false, true))
433 , current_stepping_trackview (0)
434 , last_track_height_step_timestamp (0)
435 , entered_track (0)
436 , entered_regionview (0)
437 , clear_entered_track (false)
438 , _edit_point (EditAtMouse)
439 , meters_running (false)
440 , rhythm_ferret (0)
441 , _have_idled (false)
442 , resize_idle_id (-1)
443 , _pending_resize_amount (0)
444 , _pending_resize_view (0)
445 , _pending_locate_request (false)
446 , _pending_initial_locate (false)
447 , _summary (0)
448 , _group_tabs (0)
449 , _last_motion_y (0)
450 , layering_order_editor (0)
451 , _last_cut_copy_source_track (0)
452 , _region_selection_change_updates_region_list (true)
453 , _cursors (0)
454 , _following_mixer_selection (false)
455 , _show_touched_automation (false)
456 , _control_point_toggled_on_press (false)
457 , _stepping_axis_view (0)
458 , quantize_dialog (0)
459 , _main_menu_disabler (0)
460 {
461 /* we are a singleton */
462
463 PublicEditor::_instance = this;
464
465 _have_idled = false;
466
467 last_event_time.tv_sec = 0;
468 last_event_time.tv_usec = 0;
469
470 selection_op_history.clear();
471 before.clear();
472
473 grid_type_strings = I18N (_grid_type_strings);
474 zoom_focus_strings = I18N (_zoom_focus_strings);
475 edit_mode_strings = I18N (_edit_mode_strings);
476 edit_point_strings = I18N (_edit_point_strings);
477 #ifdef USE_RUBBERBAND
478 rb_opt_strings = I18N (_rb_opt_strings);
479 rb_current_opt = 4;
480 #endif
481
482 build_edit_mode_menu();
483 build_zoom_focus_menu();
484 build_track_count_menu();
485 build_grid_type_menu();
486 build_edit_point_menu();
487
488 location_marker_color = UIConfiguration::instance().color ("location marker");
489 location_range_color = UIConfiguration::instance().color ("location range");
490 location_cd_marker_color = UIConfiguration::instance().color ("location cd marker");
491 location_loop_color = UIConfiguration::instance().color ("location loop");
492 location_punch_color = UIConfiguration::instance().color ("location punch");
493
494 timebar_height = std::max (12., ceil (15. * UIConfiguration::instance().get_ui_scale()));
495
496 TimeAxisView::setup_sizes ();
497 ArdourMarker::setup_sizes (timebar_height);
498 TempoCurve::setup_sizes (timebar_height);
499
500 bbt_label.set_name ("EditorRulerLabel");
501 bbt_label.set_size_request (-1, (int)timebar_height);
502 bbt_label.set_alignment (1.0, 0.5);
503 bbt_label.set_padding (5,0);
504 bbt_label.hide ();
505 bbt_label.set_no_show_all();
506 minsec_label.set_name ("EditorRulerLabel");
507 minsec_label.set_size_request (-1, (int)timebar_height);
508 minsec_label.set_alignment (1.0, 0.5);
509 minsec_label.set_padding (5,0);
510 minsec_label.hide ();
511 minsec_label.set_no_show_all();
512 timecode_label.set_name ("EditorRulerLabel");
513 timecode_label.set_size_request (-1, (int)timebar_height);
514 timecode_label.set_alignment (1.0, 0.5);
515 timecode_label.set_padding (5,0);
516 timecode_label.hide ();
517 timecode_label.set_no_show_all();
518 samples_label.set_name ("EditorRulerLabel");
519 samples_label.set_size_request (-1, (int)timebar_height);
520 samples_label.set_alignment (1.0, 0.5);
521 samples_label.set_padding (5,0);
522 samples_label.hide ();
523 samples_label.set_no_show_all();
524
525 tempo_label.set_name ("EditorRulerLabel");
526 tempo_label.set_size_request (-1, (int)timebar_height);
527 tempo_label.set_alignment (1.0, 0.5);
528 tempo_label.set_padding (5,0);
529 tempo_label.hide();
530 tempo_label.set_no_show_all();
531
532 meter_label.set_name ("EditorRulerLabel");
533 meter_label.set_size_request (-1, (int)timebar_height);
534 meter_label.set_alignment (1.0, 0.5);
535 meter_label.set_padding (5,0);
536 meter_label.hide();
537 meter_label.set_no_show_all();
538
539 mark_label.set_name ("EditorRulerLabel");
540 mark_label.set_size_request (-1, (int)timebar_height);
541 mark_label.set_alignment (1.0, 0.5);
542 mark_label.set_padding (5,0);
543 mark_label.hide();
544 mark_label.set_no_show_all();
545
546 cd_mark_label.set_name ("EditorRulerLabel");
547 cd_mark_label.set_size_request (-1, (int)timebar_height);
548 cd_mark_label.set_alignment (1.0, 0.5);
549 cd_mark_label.set_padding (5,0);
550 cd_mark_label.hide();
551 cd_mark_label.set_no_show_all();
552
553 videotl_bar_height = 4;
554 videotl_label.set_name ("EditorRulerLabel");
555 videotl_label.set_size_request (-1, (int)timebar_height * videotl_bar_height);
556 videotl_label.set_alignment (1.0, 0.5);
557 videotl_label.set_padding (5,0);
558 videotl_label.hide();
559 videotl_label.set_no_show_all();
560
561 range_mark_label.set_name ("EditorRulerLabel");
562 range_mark_label.set_size_request (-1, (int)timebar_height);
563 range_mark_label.set_alignment (1.0, 0.5);
564 range_mark_label.set_padding (5,0);
565 range_mark_label.hide();
566 range_mark_label.set_no_show_all();
567
568 transport_mark_label.set_name ("EditorRulerLabel");
569 transport_mark_label.set_size_request (-1, (int)timebar_height);
570 transport_mark_label.set_alignment (1.0, 0.5);
571 transport_mark_label.set_padding (5,0);
572 transport_mark_label.hide();
573 transport_mark_label.set_no_show_all();
574
575 initialize_canvas ();
576
577 CairoWidget::set_focus_handler (sigc::mem_fun (ARDOUR_UI::instance(), &ARDOUR_UI::reset_focus));
578
579 _summary = new EditorSummary (this);
580
581 selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
582 selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
583
584 editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
585
586 selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
587 selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
588
589 edit_controls_vbox.set_spacing (0);
590 vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
591 _track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
592
593 HBox* h = manage (new HBox);
594 _group_tabs = new EditorGroupTabs (this);
595 h->pack_start (*_group_tabs, PACK_SHRINK);
596 h->pack_start (edit_controls_vbox);
597 controls_layout.add (*h);
598
599 controls_layout.set_name ("EditControlsBase");
600 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK|Gdk::SCROLL_MASK);
601 controls_layout.signal_button_press_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_event));
602 controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_event));
603 controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
604
605 _cursors = new MouseCursors;
606 _cursors->set_cursor_set (UIConfiguration::instance().get_icon_set());
607 cerr << "Set cursor set to " << UIConfiguration::instance().get_icon_set() << endl;
608
609 /* Push default cursor to ever-present bottom of cursor stack. */
610 push_canvas_cursor(_cursors->grabber);
611
612 ArdourCanvas::GtkCanvas* time_pad = manage (new ArdourCanvas::GtkCanvas ());
613
614 ArdourCanvas::Line* pad_line_1 = new ArdourCanvas::Line (time_pad->root());
615 pad_line_1->set (ArdourCanvas::Duple (0.0, 1.0), ArdourCanvas::Duple (100.0, 1.0));
616 pad_line_1->set_outline_color (0xFF0000FF);
617 pad_line_1->show();
618
619 /* CAIROCANVAS */
620 time_pad->show();
621
622 edit_packer.set_col_spacings (0);
623 edit_packer.set_row_spacings (0);
624 edit_packer.set_homogeneous (false);
625 edit_packer.set_border_width (0);
626 edit_packer.set_name ("EditorWindow");
627
628 time_bars_event_box.add (time_bars_vbox);
629 time_bars_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
630 time_bars_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
631
632 ArdourWidgets::ArdourDropShadow *axis_view_shadow = manage (new (ArdourWidgets::ArdourDropShadow));
633 axis_view_shadow->set_size_request (4, -1);
634 axis_view_shadow->set_name("EditorWindow");
635 axis_view_shadow->show();
636
637 edit_packer.attach (*axis_view_shadow, 0, 1, 0, 2, FILL, FILL|EXPAND, 0, 0);
638
639 /* labels for the time bars */
640 edit_packer.attach (time_bars_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
641 /* track controls */
642 edit_packer.attach (controls_layout, 1, 2, 1, 2, FILL, FILL|EXPAND, 0, 0);
643 /* canvas */
644 edit_packer.attach (*_track_canvas_viewport, 2, 3, 0, 2, FILL|EXPAND, FILL|EXPAND, 0, 0);
645
646 bottom_hbox.set_border_width (2);
647 bottom_hbox.set_spacing (3);
648
649 PresentationInfo::Change.connect (*this, MISSING_INVALIDATOR, boost::bind (&Editor::presentation_info_changed, this, _1), gui_context());
650
651 _route_groups = new EditorRouteGroups (this);
652 _routes = new EditorRoutes (this);
653 _regions = new EditorRegions (this);
654 _sources = new EditorSources (this);
655 _snapshots = new EditorSnapshots (this);
656 _locations = new EditorLocations (this);
657 _time_info_box = new TimeInfoBox ("EditorTimeInfo", true);
658
659 /* these are static location signals */
660
661 Location::start_changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context());
662 Location::end_changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context());
663 Location::changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context());
664
665 add_notebook_page (_("Tracks & Busses"), _routes->widget ());
666 add_notebook_page (_("Sources"), _sources->widget ());
667 add_notebook_page (_("Regions"), _regions->widget ());
668 add_notebook_page (_("Snapshots"), _snapshots->widget ());
669 add_notebook_page (_("Track & Bus Groups"), _route_groups->widget ());
670 add_notebook_page (_("Ranges & Marks"), _locations->widget ());
671
672 _the_notebook.set_show_tabs (true);
673 _the_notebook.set_scrollable (true);
674 _the_notebook.popup_disable ();
675 _the_notebook.set_tab_pos (Gtk::POS_RIGHT);
676 _the_notebook.show_all ();
677
678 _notebook_shrunk = false;
679
680
681 /* Pick up some settings we need to cache, early */
682
683 XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
684
685 if (settings) {
686 settings->get_property ("notebook-shrunk", _notebook_shrunk);
687 }
688
689 editor_summary_pane.set_check_divider_position (true);
690 editor_summary_pane.add (edit_packer);
691
692 Button* summary_arrow_left = manage (new Button);
693 summary_arrow_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
694 summary_arrow_left->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), LEFT)));
695 summary_arrow_left->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
696
697 Button* summary_arrow_right = manage (new Button);
698 summary_arrow_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
699 summary_arrow_right->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), RIGHT)));
700 summary_arrow_right->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
701
702 VBox* summary_arrows_left = manage (new VBox);
703 summary_arrows_left->pack_start (*summary_arrow_left);
704
705 VBox* summary_arrows_right = manage (new VBox);
706 summary_arrows_right->pack_start (*summary_arrow_right);
707
708 Frame* summary_frame = manage (new Frame);
709 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
710
711 summary_frame->add (*_summary);
712 summary_frame->show ();
713
714 _summary_hbox.pack_start (*summary_arrows_left, false, false);
715 _summary_hbox.pack_start (*summary_frame, true, true);
716 _summary_hbox.pack_start (*summary_arrows_right, false, false);
717
718 editor_summary_pane.add (_summary_hbox);
719 edit_pane.set_check_divider_position (true);
720 edit_pane.add (editor_summary_pane);
721 _editor_list_vbox.pack_start (*_time_info_box, false, false, 0);
722 _editor_list_vbox.pack_start (_the_notebook);
723 edit_pane.add (_editor_list_vbox);
724 edit_pane.set_child_minsize (_editor_list_vbox, 30); /* rough guess at width of notebook tabs */
725
726 edit_pane.set_drag_cursor (*_cursors->expand_left_right);
727 editor_summary_pane.set_drag_cursor (*_cursors->expand_up_down);
728
729 float fract;
730 if (!settings || !settings->get_property ("edit-horizontal-pane-pos", fract) || fract > 1.0) {
731 /* initial allocation is 90% to canvas, 10% to notebook */
732 fract = 0.90;
733 }
734 edit_pane.set_divider (0, fract);
735
736 if (!settings || !settings->get_property ("edit-vertical-pane-pos", fract) || fract > 1.0) {
737 /* initial allocation is 90% to canvas, 10% to summary */
738 fract = 0.90;
739 }
740 editor_summary_pane.set_divider (0, fract);
741
742 global_vpacker.set_spacing (0);
743 global_vpacker.set_border_width (0);
744
745 /* the next three EventBoxes provide the ability for their child widgets to have a background color. That is all. */
746
747 Gtk::EventBox* ebox = manage (new Gtk::EventBox); // a themeable box
748 ebox->set_name("EditorWindow");
749 ebox->add (ebox_hpacker);
750
751 Gtk::EventBox* epane_box = manage (new EventBoxExt); // a themeable box
752 epane_box->set_name("EditorWindow");
753 epane_box->add (edit_pane);
754
755 Gtk::EventBox* epane_box2 = manage (new EventBoxExt); // a themeable box
756 epane_box2->set_name("EditorWindow");
757 epane_box2->add (global_vpacker);
758
759 ArdourWidgets::ArdourDropShadow *toolbar_shadow = manage (new (ArdourWidgets::ArdourDropShadow));
760 toolbar_shadow->set_size_request (-1, 4);
761 toolbar_shadow->set_mode(ArdourWidgets::ArdourDropShadow::DropShadowBoth);
762 toolbar_shadow->set_name("EditorWindow");
763 toolbar_shadow->show();
764
765 global_vpacker.pack_start (*toolbar_shadow, false, false);
766 global_vpacker.pack_start (*ebox, false, false);
767 global_vpacker.pack_start (*epane_box, true, true);
768 global_hpacker.pack_start (*epane_box2, true, true);
769
770 /* need to show the "contents" widget so that notebook will show if tab is switched to
771 */
772
773 global_hpacker.show ();
774 ebox_hpacker.show();
775 ebox->show();
776
777 /* register actions now so that set_state() can find them and set toggles/checks etc */
778
779 load_bindings ();
780 register_actions ();
781
782 setup_toolbar ();
783
784 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), boost::bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
785
786 /* nudge stuff */
787
788 nudge_forward_button.set_name ("nudge button");
789 nudge_forward_button.set_icon(ArdourIcon::NudgeRight);
790
791 nudge_backward_button.set_name ("nudge button");
792 nudge_backward_button.set_icon(ArdourIcon::NudgeLeft);
793
794 fade_context_menu.set_name ("ArdourContextMenu");
795
796 Gtkmm2ext::Keyboard::the_keyboard().ZoomVerticalModifierReleased.connect (sigc::mem_fun (*this, &Editor::zoom_vertical_modifier_released));
797
798 /* allow external control surfaces/protocols to do various things */
799
800 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
801 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
802 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
803 ControlProtocol::Undo.connect (*this, invalidator (*this), boost::bind (&Editor::undo, this, true), gui_context());
804 ControlProtocol::Redo.connect (*this, invalidator (*this), boost::bind (&Editor::redo, this, true), gui_context());
805 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), boost::bind (&Editor::control_scroll, this, _1), gui_context());
806 ControlProtocol::StepTracksUp.connect (*this, invalidator (*this), boost::bind (&Editor::control_step_tracks_up, this), gui_context());
807 ControlProtocol::StepTracksDown.connect (*this, invalidator (*this), boost::bind (&Editor::control_step_tracks_down, this), gui_context());
808 ControlProtocol::GotoView.connect (*this, invalidator (*this), boost::bind (&Editor::control_view, this, _1), gui_context());
809 ControlProtocol::CloseDialog.connect (*this, invalidator (*this), Keyboard::close_current_dialog, gui_context());
810 ControlProtocol::VerticalZoomInAll.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_in_all, this), gui_context());
811 ControlProtocol::VerticalZoomOutAll.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_out_all, this), gui_context());
812 ControlProtocol::VerticalZoomInSelected.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_in_selected, this), gui_context());
813 ControlProtocol::VerticalZoomOutSelected.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_out_selected, this), gui_context());
814
815 BasicUI::AccessAction.connect (*this, invalidator (*this), boost::bind (&Editor::access_action, this, _1, _2), gui_context());
816
817 /* handle escape */
818
819 ARDOUR_UI::instance()->Escape.connect (*this, invalidator (*this), boost::bind (&Editor::escape, this), gui_context());
820
821 /* problematic: has to return a value and thus cannot be x-thread */
822
823 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
824
825 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&Editor::parameter_changed, this, _1), gui_context());
826 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &Editor::ui_parameter_changed));
827
828 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
829
830 _ignore_region_action = false;
831 _last_region_menu_was_main = false;
832
833 _show_marker_lines = false;
834
835 /* Button bindings */
836
837 button_bindings = new Bindings ("editor-mouse");
838
839 XMLNode* node = button_settings();
840 if (node) {
841 for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
842 button_bindings->load_operation (**i);
843 }
844 }
845
846 constructed = true;
847
848 /* grab current parameter state */
849 boost::function<void (string)> pc (boost::bind (&Editor::ui_parameter_changed, this, _1));
850 UIConfiguration::instance().map_parameters (pc);
851
852 setup_fade_images ();
853
854 set_grid_to (GridTypeNone);
855 }
856
~Editor()857 Editor::~Editor()
858 {
859 delete tempo_marker_menu;
860 delete meter_marker_menu;
861 delete marker_menu;
862 delete range_marker_menu;
863 delete new_transport_marker_menu;
864 delete editor_ruler_menu;
865 delete _popup_region_menu_item;
866
867 delete button_bindings;
868 delete _routes;
869 delete _route_groups;
870 delete _track_canvas_viewport;
871 delete _drags;
872 delete nudge_clock;
873 delete _verbose_cursor;
874 delete _region_peak_cursor;
875 delete quantize_dialog;
876 delete _summary;
877 delete _group_tabs;
878 delete _regions;
879 delete _snapshots;
880 delete _locations;
881 delete _time_info_box;
882 delete selection;
883 delete cut_buffer;
884 delete _cursors;
885
886 LuaInstance::destroy_instance ();
887
888 for (list<XMLNode *>::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) {
889 delete *i;
890 }
891 for (std::map<ARDOUR::FadeShape, Gtk::Image*>::const_iterator i = _xfade_in_images.begin(); i != _xfade_in_images.end (); ++i) {
892 delete i->second;
893 }
894 for (std::map<ARDOUR::FadeShape, Gtk::Image*>::const_iterator i = _xfade_out_images.begin(); i != _xfade_out_images.end (); ++i) {
895 delete i->second;
896 }
897 }
898
899 XMLNode*
button_settings() const900 Editor::button_settings () const
901 {
902 XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
903 XMLNode* node = find_named_node (*settings, X_("Buttons"));
904
905 if (!node) {
906 node = new XMLNode (X_("Buttons"));
907 }
908
909 return node;
910 }
911
912 bool
get_smart_mode() const913 Editor::get_smart_mode () const
914 {
915 return ((current_mouse_mode() == MouseObject) && smart_mode_action->get_active());
916 }
917
918 void
catch_vanishing_regionview(RegionView * rv)919 Editor::catch_vanishing_regionview (RegionView *rv)
920 {
921 /* note: the selection will take care of the vanishing
922 audioregionview by itself.
923 */
924
925 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
926 _drags->abort ();
927 }
928
929 if (clicked_regionview == rv) {
930 clicked_regionview = 0;
931 }
932
933 if (entered_regionview == rv) {
934 set_entered_regionview (0);
935 }
936
937 if (!_all_region_actions_sensitized) {
938 sensitize_all_region_actions (true);
939 }
940 }
941
942 void
set_entered_regionview(RegionView * rv)943 Editor::set_entered_regionview (RegionView* rv)
944 {
945 if (rv == entered_regionview) {
946 return;
947 }
948
949 if (entered_regionview) {
950 entered_regionview->exited ();
951 }
952
953 entered_regionview = rv;
954
955 if (entered_regionview != 0) {
956 entered_regionview->entered ();
957 }
958
959 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
960 /* This RegionView entry might have changed what region actions
961 are allowed, so sensitize them all in case a key is pressed.
962 */
963 sensitize_all_region_actions (true);
964 }
965 }
966
967 void
set_entered_track(TimeAxisView * tav)968 Editor::set_entered_track (TimeAxisView* tav)
969 {
970 if (entered_track) {
971 entered_track->exited ();
972 }
973
974 entered_track = tav;
975
976 if (entered_track) {
977 entered_track->entered ();
978 }
979 }
980
981 void
instant_save()982 Editor::instant_save ()
983 {
984 if (!constructed || !_session || no_save_instant) {
985 return;
986 }
987
988 _session->add_instant_xml(get_state());
989 }
990
991 void
control_vertical_zoom_in_all()992 Editor::control_vertical_zoom_in_all ()
993 {
994 tav_zoom_smooth (false, true);
995 }
996
997 void
control_vertical_zoom_out_all()998 Editor::control_vertical_zoom_out_all ()
999 {
1000 tav_zoom_smooth (true, true);
1001 }
1002
1003 void
control_vertical_zoom_in_selected()1004 Editor::control_vertical_zoom_in_selected ()
1005 {
1006 tav_zoom_smooth (false, false);
1007 }
1008
1009 void
control_vertical_zoom_out_selected()1010 Editor::control_vertical_zoom_out_selected ()
1011 {
1012 tav_zoom_smooth (true, false);
1013 }
1014
1015 void
control_view(uint32_t view)1016 Editor::control_view (uint32_t view)
1017 {
1018 goto_visual_state (view);
1019 }
1020
1021 void
control_step_tracks_up()1022 Editor::control_step_tracks_up ()
1023 {
1024 scroll_tracks_up_line ();
1025 }
1026
1027 void
control_step_tracks_down()1028 Editor::control_step_tracks_down ()
1029 {
1030 scroll_tracks_down_line ();
1031 }
1032
1033 void
control_scroll(float fraction)1034 Editor::control_scroll (float fraction)
1035 {
1036 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
1037
1038 if (!_session) {
1039 return;
1040 }
1041
1042 double step = fraction * current_page_samples();
1043
1044 /*
1045 _control_scroll_target is an optional<T>
1046
1047 it acts like a pointer to an samplepos_t, with
1048 a operator conversion to boolean to check
1049 that it has a value could possibly use
1050 _playhead_cursor->current_sample to store the
1051 value and a boolean in the class to know
1052 when it's out of date
1053 */
1054
1055 if (!_control_scroll_target) {
1056 _control_scroll_target = _session->transport_sample();
1057 _dragging_playhead = true;
1058 }
1059
1060 if ((fraction < 0.0f) && (*_control_scroll_target <= (samplepos_t) fabs(step))) {
1061 *_control_scroll_target = 0;
1062 } else if ((fraction > 0.0f) && (max_samplepos - *_control_scroll_target < step)) {
1063 *_control_scroll_target = max_samplepos - (current_page_samples()*2); // allow room for slop in where the PH is on the screen
1064 } else {
1065 *_control_scroll_target += (samplepos_t) trunc (step);
1066 }
1067
1068 /* move visuals, we'll catch up with it later */
1069
1070 _playhead_cursor->set_position (*_control_scroll_target);
1071 UpdateAllTransportClocks (*_control_scroll_target);
1072
1073 if (*_control_scroll_target > (current_page_samples() / 2)) {
1074 /* try to center PH in window */
1075 reset_x_origin (*_control_scroll_target - (current_page_samples()/2));
1076 } else {
1077 reset_x_origin (0);
1078 }
1079
1080 /*
1081 Now we do a timeout to actually bring the session to the right place
1082 according to the playhead. This is to avoid reading disk buffers on every
1083 call to control_scroll, which is driven by ScrollTimeline and therefore
1084 probably by a control surface wheel which can generate lots of events.
1085 */
1086 /* cancel the existing timeout */
1087
1088 control_scroll_connection.disconnect ();
1089
1090 /* add the next timeout */
1091
1092 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
1093 }
1094
1095 bool
deferred_control_scroll(samplepos_t)1096 Editor::deferred_control_scroll (samplepos_t /*target*/)
1097 {
1098 _session->request_locate (*_control_scroll_target);
1099 /* reset for next stream */
1100 _control_scroll_target = boost::none;
1101 _dragging_playhead = false;
1102 return false;
1103 }
1104
1105 void
access_action(const std::string & action_group,const std::string & action_item)1106 Editor::access_action (const std::string& action_group, const std::string& action_item)
1107 {
1108 if (!_session) {
1109 return;
1110 }
1111
1112 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
1113
1114 RefPtr<Action> act;
1115 try {
1116 act = ActionManager::get_action (action_group.c_str(), action_item.c_str());
1117 if (act) {
1118 act->activate();
1119 }
1120 } catch ( ActionManager::MissingActionException const& e) {
1121 cerr << "MissingActionException:" << e.what () << endl;
1122 }
1123 }
1124
1125 void
set_toggleaction(const std::string & action_group,const std::string & action_item,bool s)1126 Editor::set_toggleaction (const std::string& action_group, const std::string& action_item, bool s)
1127 {
1128 ActionManager::set_toggleaction_state (action_group.c_str(), action_item.c_str(), s);
1129 }
1130
1131 void
on_realize()1132 Editor::on_realize ()
1133 {
1134 Realized ();
1135
1136 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
1137 start_lock_event_timing ();
1138 }
1139 }
1140
1141 void
start_lock_event_timing()1142 Editor::start_lock_event_timing ()
1143 {
1144 /* check if we should lock the GUI every 30 seconds */
1145
1146 Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::lock_timeout_callback), 30 * 1000);
1147 }
1148
1149 bool
generic_event_handler(GdkEvent * ev)1150 Editor::generic_event_handler (GdkEvent* ev)
1151 {
1152 switch (ev->type) {
1153 case GDK_BUTTON_PRESS:
1154 case GDK_BUTTON_RELEASE:
1155 case GDK_MOTION_NOTIFY:
1156 case GDK_KEY_PRESS:
1157 case GDK_KEY_RELEASE:
1158 if (contents().is_mapped()) {
1159 gettimeofday (&last_event_time, 0);
1160 }
1161 break;
1162
1163 case GDK_LEAVE_NOTIFY:
1164 switch (ev->crossing.detail) {
1165 case GDK_NOTIFY_UNKNOWN:
1166 case GDK_NOTIFY_INFERIOR:
1167 case GDK_NOTIFY_ANCESTOR:
1168 break;
1169 case GDK_NOTIFY_VIRTUAL:
1170 case GDK_NOTIFY_NONLINEAR:
1171 case GDK_NOTIFY_NONLINEAR_VIRTUAL:
1172 /* leaving window, so reset focus, thus ending any and
1173 all text entry operations.
1174 */
1175 ARDOUR_UI::instance()->reset_focus (&contents());
1176 break;
1177 }
1178 break;
1179
1180 default:
1181 break;
1182 }
1183
1184 return false;
1185 }
1186
1187 bool
lock_timeout_callback()1188 Editor::lock_timeout_callback ()
1189 {
1190 struct timeval now, delta;
1191
1192 gettimeofday (&now, 0);
1193
1194 timersub (&now, &last_event_time, &delta);
1195
1196 if (delta.tv_sec > (time_t) UIConfiguration::instance().get_lock_gui_after_seconds()) {
1197 lock ();
1198 /* don't call again. Returning false will effectively
1199 disconnect us from the timer callback.
1200
1201 unlock() will call start_lock_event_timing() to get things
1202 started again.
1203 */
1204 return false;
1205 }
1206
1207 return true;
1208 }
1209
1210 void
map_position_change(samplepos_t sample)1211 Editor::map_position_change (samplepos_t sample)
1212 {
1213 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, sample)
1214
1215 if (_session == 0) {
1216 return;
1217 }
1218
1219 if (_follow_playhead) {
1220 center_screen (sample);
1221 }
1222
1223 if (!_session->locate_initiated()) {
1224 _playhead_cursor->set_position (sample);
1225 }
1226 }
1227
1228 void
center_screen(samplepos_t sample)1229 Editor::center_screen (samplepos_t sample)
1230 {
1231 samplecnt_t const page = _visible_canvas_width * samples_per_pixel;
1232
1233 /* if we're off the page, then scroll.
1234 */
1235
1236 if (sample < _leftmost_sample || sample >= _leftmost_sample + page) {
1237 center_screen_internal (sample, page);
1238 }
1239 }
1240
1241 void
center_screen_internal(samplepos_t sample,float page)1242 Editor::center_screen_internal (samplepos_t sample, float page)
1243 {
1244 page /= 2;
1245
1246 if (sample > page) {
1247 sample -= (samplepos_t) page;
1248 } else {
1249 sample = 0;
1250 }
1251
1252 reset_x_origin (sample);
1253 }
1254
1255
1256 void
update_title()1257 Editor::update_title ()
1258 {
1259 ENSURE_GUI_THREAD (*this, &Editor::update_title);
1260
1261 if (!own_window()) {
1262 return;
1263 }
1264
1265 if (_session) {
1266 bool dirty = _session->dirty();
1267
1268 string session_name;
1269
1270 if (_session->snap_name() != _session->name()) {
1271 session_name = _session->snap_name();
1272 } else {
1273 session_name = _session->name();
1274 }
1275
1276 if (dirty) {
1277 session_name = "*" + session_name;
1278 }
1279
1280 WindowTitle title(session_name);
1281 title += S_("Window|Editor");
1282 title += Glib::get_application_name();
1283 own_window()->set_title (title.get_string());
1284 } else {
1285 /* ::session_going_away() will have taken care of it */
1286 }
1287 }
1288
1289 void
set_session(Session * t)1290 Editor::set_session (Session *t)
1291 {
1292 SessionHandlePtr::set_session (t);
1293
1294 if (!_session) {
1295 return;
1296 }
1297
1298 /* initialize _leftmost_sample to the extents of the session
1299 * this prevents a bogus setting of leftmost = "0" if the summary view asks for the leftmost sample
1300 * before the visible state has been loaded from instant.xml */
1301 _leftmost_sample = session_gui_extents().first;
1302
1303 nudge_clock->set_session (_session);
1304 _summary->set_session (_session);
1305 _group_tabs->set_session (_session);
1306 _route_groups->set_session (_session);
1307 _regions->set_session (_session);
1308 _sources->set_session (_session);
1309 _snapshots->set_session (_session);
1310 _routes->set_session (_session);
1311 _locations->set_session (_session);
1312 _time_info_box->set_session (_session);
1313
1314 if (rhythm_ferret) {
1315 rhythm_ferret->set_session (_session);
1316 }
1317
1318 if (analysis_window) {
1319 analysis_window->set_session (_session);
1320 }
1321
1322 if (sfbrowser) {
1323 sfbrowser->set_session (_session);
1324 }
1325
1326 compute_fixed_ruler_scale ();
1327
1328 /* Make sure we have auto loop and auto punch ranges */
1329
1330 Location* loc = _session->locations()->auto_loop_location();
1331 if (loc != 0) {
1332 loc->set_name (_("Loop"));
1333 }
1334
1335 loc = _session->locations()->auto_punch_location();
1336 if (loc != 0) {
1337 /* force name */
1338 loc->set_name (_("Punch"));
1339 }
1340
1341 refresh_location_display ();
1342
1343 /* This must happen after refresh_location_display(), as (amongst other things) we restore
1344 * the selected Marker; this needs the LocationMarker list to be available.
1345 */
1346 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1347 set_state (*node, Stateful::loading_state_version);
1348
1349 /* catch up on selection state, etc. */
1350
1351 PropertyChange sc;
1352 sc.add (Properties::selected);
1353 presentation_info_changed (sc);
1354
1355 /* catch up with the playhead */
1356
1357 _session->request_locate (_playhead_cursor->current_sample (), MustStop);
1358 _pending_initial_locate = true;
1359
1360 update_title ();
1361
1362 /* These signals can all be emitted by a non-GUI thread. Therefore the
1363 handlers for them must not attempt to directly interact with the GUI,
1364 but use PBD::Signal<T>::connect() which accepts an event loop
1365 ("context") where the handler will be asked to run.
1366 */
1367
1368 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::step_edit_status_change, this, _1), gui_context());
1369 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1370 _session->TransportLooped.connect (_session_connections, invalidator (*this), boost::bind (&Editor::transport_looped, this), gui_context());
1371 _session->PositionChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_position_change, this, _1), gui_context());
1372 _session->vca_manager().VCAAdded.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_vcas, this, _1), gui_context());
1373 _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_routes, this, _1), gui_context());
1374 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1375 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::tempo_map_changed, this, _1), gui_context());
1376 _session->tempo_map().MetricPositionChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::tempometric_position_changed, this, _1), gui_context());
1377 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1378 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::parameter_changed, this, _1), gui_context());
1379 _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Editor::session_state_saved, this, _1), gui_context());
1380 _session->locations()->added.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_new_location, this, _1), gui_context());
1381 _session->locations()->removed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::location_gone, this, _1), gui_context());
1382 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1383 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1384
1385 _playhead_cursor->track_canvas_item().reparent ((ArdourCanvas::Item*) get_cursor_scroll_group());
1386 _playhead_cursor->show ();
1387
1388 _snapped_cursor->track_canvas_item().reparent ((ArdourCanvas::Item*) get_cursor_scroll_group());
1389 _snapped_cursor->set_color (UIConfiguration::instance().color ("edit point"));
1390 _snapped_cursor->show ();
1391
1392 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1393 Config->map_parameters (pc);
1394 _session->config.map_parameters (pc);
1395
1396 restore_ruler_visibility ();
1397 //tempo_map_changed (PropertyChange (0));
1398 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1399
1400 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1401 (static_cast<TimeAxisView*>(*i))->set_samples_per_pixel (samples_per_pixel);
1402 }
1403
1404 super_rapid_screen_update_connection = Timers::super_rapid_connect (
1405 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1406 );
1407
1408 /* register for undo history */
1409 _session->register_with_memento_command_factory(id(), this);
1410 _session->register_with_memento_command_factory(_selection_memento->id(), _selection_memento);
1411
1412 LuaInstance::instance()->set_session(_session);
1413
1414 start_updating_meters ();
1415 }
1416
1417 void
fill_xfade_menu(Menu_Helpers::MenuList & items,bool start)1418 Editor::fill_xfade_menu (Menu_Helpers::MenuList& items, bool start)
1419 {
1420 using namespace Menu_Helpers;
1421
1422 void (Editor::*emf)(FadeShape);
1423 std::map<ARDOUR::FadeShape,Gtk::Image*>* images;
1424
1425 if (start) {
1426 images = &_xfade_in_images;
1427 emf = &Editor::set_fade_in_shape;
1428 } else {
1429 images = &_xfade_out_images;
1430 emf = &Editor::set_fade_out_shape;
1431 }
1432
1433 items.push_back (
1434 ImageMenuElem (
1435 _("Linear (for highly correlated material)"),
1436 *(*images)[FadeLinear],
1437 sigc::bind (sigc::mem_fun (*this, emf), FadeLinear)
1438 )
1439 );
1440
1441 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1442
1443 items.push_back (
1444 ImageMenuElem (
1445 _("Constant power"),
1446 *(*images)[FadeConstantPower],
1447 sigc::bind (sigc::mem_fun (*this, emf), FadeConstantPower)
1448 ));
1449
1450 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1451
1452 items.push_back (
1453 ImageMenuElem (
1454 _("Symmetric"),
1455 *(*images)[FadeSymmetric],
1456 sigc::bind (sigc::mem_fun (*this, emf), FadeSymmetric)
1457 )
1458 );
1459
1460 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1461
1462 items.push_back (
1463 ImageMenuElem (
1464 _("Slow"),
1465 *(*images)[FadeSlow],
1466 sigc::bind (sigc::mem_fun (*this, emf), FadeSlow)
1467 ));
1468
1469 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1470
1471 items.push_back (
1472 ImageMenuElem (
1473 _("Fast"),
1474 *(*images)[FadeFast],
1475 sigc::bind (sigc::mem_fun (*this, emf), FadeFast)
1476 ));
1477
1478 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1479 }
1480
1481 /** Pop up a context menu for when the user clicks on a start crossfade */
1482 void
popup_xfade_in_context_menu(int button,int32_t time,ArdourCanvas::Item * item,ItemType)1483 Editor::popup_xfade_in_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType /*item_type*/)
1484 {
1485 using namespace Menu_Helpers;
1486 AudioRegionView* arv = dynamic_cast<AudioRegionView*> ((RegionView*)item->get_data ("regionview"));
1487 if (!arv) {
1488 return;
1489 }
1490
1491 MenuList& items (xfade_in_context_menu.items());
1492 items.clear ();
1493
1494 if (arv->audio_region()->fade_in_active()) {
1495 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1496 } else {
1497 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1498 }
1499
1500 items.push_back (SeparatorElem());
1501 fill_xfade_menu (items, true);
1502
1503 xfade_in_context_menu.popup (button, time);
1504 }
1505
1506 /** Pop up a context menu for when the user clicks on an end crossfade */
1507 void
popup_xfade_out_context_menu(int button,int32_t time,ArdourCanvas::Item * item,ItemType)1508 Editor::popup_xfade_out_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType /*item_type*/)
1509 {
1510 using namespace Menu_Helpers;
1511 AudioRegionView* arv = dynamic_cast<AudioRegionView*> ((RegionView*)item->get_data ("regionview"));
1512 if (!arv) {
1513 return;
1514 }
1515
1516 MenuList& items (xfade_out_context_menu.items());
1517 items.clear ();
1518
1519 if (arv->audio_region()->fade_out_active()) {
1520 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1521 } else {
1522 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1523 }
1524
1525 items.push_back (SeparatorElem());
1526 fill_xfade_menu (items, false);
1527
1528 xfade_out_context_menu.popup (button, time);
1529 }
1530
1531 void
popup_track_context_menu(int button,int32_t time,ItemType item_type,bool with_selection)1532 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1533 {
1534 using namespace Menu_Helpers;
1535 Menu* (Editor::*build_menu_function)();
1536 Menu *menu;
1537
1538 switch (item_type) {
1539 case RegionItem:
1540 case RegionViewName:
1541 case RegionViewNameHighlight:
1542 case LeftFrameHandle:
1543 case RightFrameHandle:
1544 if (with_selection) {
1545 build_menu_function = &Editor::build_track_selection_context_menu;
1546 } else {
1547 build_menu_function = &Editor::build_track_region_context_menu;
1548 }
1549 break;
1550
1551 case SelectionItem:
1552 if (with_selection) {
1553 build_menu_function = &Editor::build_track_selection_context_menu;
1554 } else {
1555 build_menu_function = &Editor::build_track_context_menu;
1556 }
1557 break;
1558
1559 case StreamItem:
1560 if (clicked_routeview != 0 && clicked_routeview->track()) {
1561 build_menu_function = &Editor::build_track_context_menu;
1562 } else {
1563 build_menu_function = &Editor::build_track_bus_context_menu;
1564 }
1565 break;
1566
1567 default:
1568 /* probably shouldn't happen but if it does, we don't care */
1569 return;
1570 }
1571
1572 menu = (this->*build_menu_function)();
1573 menu->set_name ("ArdourContextMenu");
1574
1575 /* now handle specific situations */
1576
1577 switch (item_type) {
1578 case RegionItem:
1579 case RegionViewName:
1580 case RegionViewNameHighlight:
1581 case LeftFrameHandle:
1582 case RightFrameHandle:
1583 break;
1584
1585 case SelectionItem:
1586 break;
1587
1588 case StreamItem:
1589 break;
1590
1591 default:
1592 /* probably shouldn't happen but if it does, we don't care */
1593 return;
1594 }
1595
1596 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1597
1598 /* Bounce to disk */
1599
1600 using namespace Menu_Helpers;
1601 MenuList& edit_items = menu->items();
1602
1603 edit_items.push_back (SeparatorElem());
1604
1605 switch (clicked_routeview->audio_track()->freeze_state()) {
1606 case AudioTrack::NoFreeze:
1607 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1608 break;
1609
1610 case AudioTrack::Frozen:
1611 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1612 break;
1613
1614 case AudioTrack::UnFrozen:
1615 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1616 break;
1617 }
1618
1619 }
1620
1621 if (item_type == StreamItem && clicked_routeview) {
1622 clicked_routeview->build_underlay_menu(menu);
1623 }
1624
1625 /* When the region menu is opened, we setup the actions so that they look right
1626 in the menu.
1627 */
1628 sensitize_the_right_region_actions (false);
1629 _last_region_menu_was_main = false;
1630
1631 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1632 menu->popup (button, time);
1633 }
1634
1635 Menu*
build_track_context_menu()1636 Editor::build_track_context_menu ()
1637 {
1638 using namespace Menu_Helpers;
1639
1640 MenuList& edit_items = track_context_menu.items();
1641 edit_items.clear();
1642
1643 add_dstream_context_items (edit_items);
1644 return &track_context_menu;
1645 }
1646
1647 Menu*
build_track_bus_context_menu()1648 Editor::build_track_bus_context_menu ()
1649 {
1650 using namespace Menu_Helpers;
1651
1652 MenuList& edit_items = track_context_menu.items();
1653 edit_items.clear();
1654
1655 add_bus_context_items (edit_items);
1656 return &track_context_menu;
1657 }
1658
1659 Menu*
build_track_region_context_menu()1660 Editor::build_track_region_context_menu ()
1661 {
1662 using namespace Menu_Helpers;
1663 MenuList& edit_items = track_region_context_menu.items();
1664 edit_items.clear();
1665
1666 /* we've just cleared the track region context menu, so the menu that these
1667 two items were on will have disappeared; stop them dangling.
1668 */
1669 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1670
1671 if (rtv) {
1672 boost::shared_ptr<Track> tr;
1673 boost::shared_ptr<Playlist> pl;
1674
1675 if ((tr = rtv->track())) {
1676 add_region_context_items (edit_items, tr);
1677 }
1678 }
1679
1680 add_dstream_context_items (edit_items);
1681
1682 return &track_region_context_menu;
1683 }
1684
1685 void
loudness_analyze_region_selection()1686 Editor::loudness_analyze_region_selection ()
1687 {
1688 if (!_session) {
1689 return;
1690 }
1691 Selection& s (PublicEditor::instance ().get_selection ());
1692 RegionSelection ars = s.regions;
1693 ARDOUR::AnalysisGraph ag (_session);
1694 samplecnt_t total_work = 0;
1695
1696 for (RegionSelection::iterator j = ars.begin (); j != ars.end (); ++j) {
1697 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*j);
1698 if (!arv) {
1699 continue;
1700 }
1701 if (!boost::dynamic_pointer_cast<AudioRegion> (arv->region ())) {
1702 continue;
1703 }
1704 assert (dynamic_cast<RouteTimeAxisView *> (&arv->get_time_axis_view ()));
1705 total_work += arv->region ()->length ();
1706 }
1707
1708 SimpleProgressDialog spd (_("Region Loudness Analysis"), sigc::mem_fun (ag, &AnalysisGraph::cancel));
1709 ScopedConnection c;
1710 ag.set_total_samples (total_work);
1711 ag.Progress.connect_same_thread (c, boost::bind (&SimpleProgressDialog::update_progress, &spd, _1, _2));
1712 spd.show();
1713
1714 for (RegionSelection::iterator j = ars.begin (); j != ars.end (); ++j) {
1715 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*j);
1716 if (!arv) {
1717 continue;
1718 }
1719 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (arv->region ());
1720 if (!ar) {
1721 continue;
1722 }
1723 ag.analyze_region (ar);
1724 }
1725 spd.hide();
1726 if (!ag.canceled ()) {
1727 ExportReport er (_("Audio Report/Analysis"), ag.results ());
1728 er.run();
1729 }
1730 }
1731
1732 void
loudness_analyze_range_selection()1733 Editor::loudness_analyze_range_selection ()
1734 {
1735 if (!_session) {
1736 return;
1737 }
1738 Selection& s (PublicEditor::instance ().get_selection ());
1739 TimeSelection ts = s.time;
1740 ARDOUR::AnalysisGraph ag (_session);
1741 samplecnt_t total_work = 0;
1742
1743 for (TrackSelection::iterator i = s.tracks.begin (); i != s.tracks.end (); ++i) {
1744 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist> ((*i)->playlist ());
1745 if (!pl) {
1746 continue;
1747 }
1748 RouteUI *rui = dynamic_cast<RouteUI *> (*i);
1749 if (!pl || !rui) {
1750 continue;
1751 }
1752 for (std::list<AudioRange>::iterator j = ts.begin (); j != ts.end (); ++j) {
1753 total_work += j->length ();
1754 }
1755 }
1756
1757 SimpleProgressDialog spd (_("Range Loudness Analysis"), sigc::mem_fun (ag, &AnalysisGraph::cancel));
1758 ScopedConnection c;
1759 ag.set_total_samples (total_work);
1760 ag.Progress.connect_same_thread (c, boost::bind (&SimpleProgressDialog::update_progress, &spd, _1, _2));
1761 spd.show();
1762
1763 for (TrackSelection::iterator i = s.tracks.begin (); i != s.tracks.end (); ++i) {
1764 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist> ((*i)->playlist ());
1765 if (!pl) {
1766 continue;
1767 }
1768 RouteUI *rui = dynamic_cast<RouteUI *> (*i);
1769 if (!pl || !rui) {
1770 continue;
1771 }
1772 ag.analyze_range (rui->route (), pl, ts);
1773 }
1774 spd.hide();
1775 if (!ag.canceled ()) {
1776 ExportReport er (_("Audio Report/Analysis"), ag.results ());
1777 er.run();
1778 }
1779 }
1780
1781 void
spectral_analyze_region_selection()1782 Editor::spectral_analyze_region_selection ()
1783 {
1784 if (analysis_window == 0) {
1785 analysis_window = new AnalysisWindow();
1786
1787 if (_session != 0)
1788 analysis_window->set_session(_session);
1789
1790 analysis_window->show_all();
1791 }
1792
1793 analysis_window->set_regionmode();
1794 analysis_window->analyze();
1795
1796 analysis_window->present();
1797 }
1798
1799 void
spectral_analyze_range_selection()1800 Editor::spectral_analyze_range_selection()
1801 {
1802 if (analysis_window == 0) {
1803 analysis_window = new AnalysisWindow();
1804
1805 if (_session != 0)
1806 analysis_window->set_session(_session);
1807
1808 analysis_window->show_all();
1809 }
1810
1811 analysis_window->set_rangemode();
1812 analysis_window->analyze();
1813
1814 analysis_window->present();
1815 }
1816
1817 Menu*
build_track_selection_context_menu()1818 Editor::build_track_selection_context_menu ()
1819 {
1820 using namespace Menu_Helpers;
1821 MenuList& edit_items = track_selection_context_menu.items();
1822 edit_items.clear ();
1823
1824 add_selection_context_items (edit_items);
1825 // edit_items.push_back (SeparatorElem());
1826 // add_dstream_context_items (edit_items);
1827
1828 return &track_selection_context_menu;
1829 }
1830
1831 void
add_region_context_items(Menu_Helpers::MenuList & edit_items,boost::shared_ptr<Track> track)1832 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr<Track> track)
1833 {
1834 using namespace Menu_Helpers;
1835
1836 /* OK, stick the region submenu at the top of the list, and then add
1837 the standard items.
1838 */
1839
1840 RegionSelection rs = get_regions_from_selection_and_entered ();
1841
1842 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1843
1844 if (_popup_region_menu_item == 0) {
1845 _popup_region_menu_item = new MenuItem (menu_item_name, false);
1846 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1847 _popup_region_menu_item->show ();
1848 } else {
1849 _popup_region_menu_item->set_label (menu_item_name);
1850 }
1851
1852 /* No layering allowed in later is higher layering model */
1853 RefPtr<Action> act = ActionManager::get_action (X_("EditorMenu"), X_("RegionMenuLayering"));
1854 if (act && Config->get_layer_model() == LaterHigher) {
1855 act->set_sensitive (false);
1856 } else if (act) {
1857 act->set_sensitive (true);
1858 }
1859
1860 const samplepos_t position = get_preferred_edit_position (EDIT_IGNORE_NONE, true);
1861
1862 edit_items.push_back (*_popup_region_menu_item);
1863 if (Config->get_layer_model() == Manual && track->playlist()->count_regions_at (position) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1864 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region-context-menu")->create_menu_item ()));
1865 }
1866 edit_items.push_back (SeparatorElem());
1867 }
1868
1869 /** Add context menu items relevant to selection ranges.
1870 * @param edit_items List to add the items to.
1871 */
1872 void
add_selection_context_items(Menu_Helpers::MenuList & edit_items)1873 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1874 {
1875 using namespace Menu_Helpers;
1876
1877 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1878 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1879
1880 edit_items.push_back (SeparatorElem());
1881 edit_items.push_back (MenuElem (_("Zoom to Range"), sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_selection), Horizontal)));
1882
1883 edit_items.push_back (SeparatorElem());
1884 edit_items.push_back (MenuElem (_("Loudness Analysis"), sigc::mem_fun(*this, &Editor::loudness_analyze_range_selection)));
1885 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::spectral_analyze_range_selection)));
1886 edit_items.push_back (SeparatorElem());
1887 edit_items.push_back (MenuElem (_("Loudness Assistant..."), sigc::bind (sigc::mem_fun (*this, &Editor::loudness_assistant), true)));
1888 edit_items.push_back (SeparatorElem());
1889
1890 edit_items.push_back (
1891 MenuElem (
1892 _("Move Range Start to Previous Region Boundary"),
1893 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, false)
1894 )
1895 );
1896
1897 edit_items.push_back (
1898 MenuElem (
1899 _("Move Range Start to Next Region Boundary"),
1900 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, true)
1901 )
1902 );
1903
1904 edit_items.push_back (
1905 MenuElem (
1906 _("Move Range End to Previous Region Boundary"),
1907 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, false)
1908 )
1909 );
1910
1911 edit_items.push_back (
1912 MenuElem (
1913 _("Move Range End to Next Region Boundary"),
1914 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, true)
1915 )
1916 );
1917
1918 edit_items.push_back (SeparatorElem());
1919 edit_items.push_back (MenuElem (_("Separate"), mem_fun(*this, &Editor::separate_region_from_selection)));
1920 // edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1921
1922 edit_items.push_back (SeparatorElem());
1923 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1924
1925 edit_items.push_back (SeparatorElem());
1926 edit_items.push_back (MenuElem (_("Set Loop from Selection"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1927 edit_items.push_back (MenuElem (_("Set Punch from Selection"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1928 edit_items.push_back (MenuElem (_("Set Session Start/End from Selection"), sigc::mem_fun(*this, &Editor::set_session_extents_from_selection)));
1929
1930 edit_items.push_back (SeparatorElem());
1931 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1932
1933 edit_items.push_back (SeparatorElem());
1934 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1935 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_range), false)));
1936
1937 edit_items.push_back (SeparatorElem());
1938 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1939 edit_items.push_back (MenuElem (_("Consolidate Range with Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1940 edit_items.push_back (MenuElem (_("Bounce Range to Source List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1941 edit_items.push_back (MenuElem (_("Bounce Range to Source List with Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1942 edit_items.push_back (MenuElem (_("Export Range..."), sigc::mem_fun(*this, &Editor::export_selection)));
1943 if (ARDOUR_UI::instance()->video_timeline->get_duration() > 0) {
1944 edit_items.push_back (MenuElem (_("Export Video Range..."), sigc::bind (sigc::mem_fun(*(ARDOUR_UI::instance()), &ARDOUR_UI::export_video), true)));
1945 }
1946 }
1947
1948
1949 void
add_dstream_context_items(Menu_Helpers::MenuList & edit_items)1950 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1951 {
1952 using namespace Menu_Helpers;
1953
1954 /* Playback */
1955
1956 Menu *play_menu = manage (new Menu);
1957 MenuList& play_items = play_menu->items();
1958 play_menu->set_name ("ArdourContextMenu");
1959
1960 play_items.push_back (MenuElem (_("Play from Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1961 play_items.push_back (MenuElem (_("Play from Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1962 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1963 play_items.push_back (SeparatorElem());
1964 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1965
1966 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1967
1968 /* Selection */
1969
1970 Menu *select_menu = manage (new Menu);
1971 MenuList& select_items = select_menu->items();
1972 select_menu->set_name ("ArdourContextMenu");
1973
1974 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1975 select_items.push_back (MenuElem (_("Select All Objects"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_objects), Selection::Set)));
1976 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1977 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1978 select_items.push_back (SeparatorElem());
1979 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1980 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1981 select_items.push_back (MenuElem (_("Set Range to Selected Regions"), sigc::mem_fun(*this, &Editor::set_selection_from_region)));
1982 select_items.push_back (SeparatorElem());
1983 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true, true)));
1984 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false, true)));
1985 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), _playhead_cursor, true)));
1986 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), _playhead_cursor, false)));
1987 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1988 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1989 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1990
1991 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1992
1993 /* Cut-n-Paste */
1994
1995 Menu *cutnpaste_menu = manage (new Menu);
1996 MenuList& cutnpaste_items = cutnpaste_menu->items();
1997 cutnpaste_menu->set_name ("ArdourContextMenu");
1998
1999 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2000 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2001 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f, true)));
2002
2003 cutnpaste_items.push_back (SeparatorElem());
2004
2005 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
2006 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
2007
2008 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
2009
2010 /* Adding new material */
2011
2012 edit_items.push_back (SeparatorElem());
2013 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_source_list_selection), 1.0f)));
2014 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
2015
2016 /* Nudge track */
2017
2018 Menu *nudge_menu = manage (new Menu());
2019 MenuList& nudge_items = nudge_menu->items();
2020 nudge_menu->set_name ("ArdourContextMenu");
2021
2022 edit_items.push_back (SeparatorElem());
2023 nudge_items.push_back (MenuElem (_("Nudge Entire Track Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2024 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2025 nudge_items.push_back (MenuElem (_("Nudge Entire Track Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2026 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2027
2028 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2029 }
2030
2031 void
add_bus_context_items(Menu_Helpers::MenuList & edit_items)2032 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
2033 {
2034 using namespace Menu_Helpers;
2035
2036 /* Playback */
2037
2038 Menu *play_menu = manage (new Menu);
2039 MenuList& play_items = play_menu->items();
2040 play_menu->set_name ("ArdourContextMenu");
2041
2042 play_items.push_back (MenuElem (_("Play from Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
2043 play_items.push_back (MenuElem (_("Play from Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
2044 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2045
2046 /* Selection */
2047
2048 Menu *select_menu = manage (new Menu);
2049 MenuList& select_items = select_menu->items();
2050 select_menu->set_name ("ArdourContextMenu");
2051
2052 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2053 select_items.push_back (MenuElem (_("Select All Objects"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_objects), Selection::Set)));
2054 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2055 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2056 select_items.push_back (SeparatorElem());
2057 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true, true)));
2058 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false, true)));
2059 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), _playhead_cursor, true)));
2060 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), _playhead_cursor, false)));
2061
2062 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2063
2064 /* Cut-n-Paste */
2065 #if 0 // unused, why?
2066 Menu *cutnpaste_menu = manage (new Menu);
2067 MenuList& cutnpaste_items = cutnpaste_menu->items();
2068 cutnpaste_menu->set_name ("ArdourContextMenu");
2069
2070 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2071 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2072 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f, true)));
2073 #endif
2074
2075 Menu *nudge_menu = manage (new Menu());
2076 MenuList& nudge_items = nudge_menu->items();
2077 nudge_menu->set_name ("ArdourContextMenu");
2078
2079 edit_items.push_back (SeparatorElem());
2080 nudge_items.push_back (MenuElem (_("Nudge Entire Track Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2081 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2082 nudge_items.push_back (MenuElem (_("Nudge Entire Track Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2083 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2084
2085 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2086 }
2087
2088 GridType
grid_type() const2089 Editor::grid_type() const
2090 {
2091 return _grid_type;
2092 }
2093
2094 bool
grid_musical() const2095 Editor::grid_musical() const
2096 {
2097 return grid_type_is_musical (_grid_type);
2098 }
2099
2100 bool
grid_type_is_musical(GridType gt) const2101 Editor::grid_type_is_musical(GridType gt) const
2102 {
2103 switch (gt) {
2104 case GridTypeBeatDiv32:
2105 case GridTypeBeatDiv28:
2106 case GridTypeBeatDiv24:
2107 case GridTypeBeatDiv20:
2108 case GridTypeBeatDiv16:
2109 case GridTypeBeatDiv14:
2110 case GridTypeBeatDiv12:
2111 case GridTypeBeatDiv10:
2112 case GridTypeBeatDiv8:
2113 case GridTypeBeatDiv7:
2114 case GridTypeBeatDiv6:
2115 case GridTypeBeatDiv5:
2116 case GridTypeBeatDiv4:
2117 case GridTypeBeatDiv3:
2118 case GridTypeBeatDiv2:
2119 case GridTypeBeat:
2120 case GridTypeBar:
2121 return true;
2122 case GridTypeNone:
2123 case GridTypeTimecode:
2124 case GridTypeMinSec:
2125 case GridTypeCDFrame:
2126 return false;
2127 }
2128 return false;
2129 }
2130
2131 SnapMode
snap_mode() const2132 Editor::snap_mode() const
2133 {
2134 return _snap_mode;
2135 }
2136
2137 void
show_rulers_for_grid()2138 Editor::show_rulers_for_grid ()
2139 {
2140 /* show appropriate rulers for this grid setting. */
2141 if (grid_musical()) {
2142 ruler_tempo_action->set_active(true);
2143 ruler_meter_action->set_active(true);
2144 ruler_bbt_action->set_active(true);
2145
2146 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2147 ruler_timecode_action->set_active(false);
2148 ruler_minsec_action->set_active(false);
2149 ruler_samples_action->set_active(false);
2150 }
2151 } else if (_grid_type == GridTypeTimecode) {
2152 ruler_timecode_action->set_active(true);
2153
2154 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2155 ruler_tempo_action->set_active(false);
2156 ruler_meter_action->set_active(false);
2157 ruler_bbt_action->set_active(false);
2158 ruler_minsec_action->set_active(false);
2159 ruler_samples_action->set_active(false);
2160 }
2161 } else if (_grid_type == GridTypeMinSec) {
2162 ruler_minsec_action->set_active(true);
2163
2164 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2165 ruler_tempo_action->set_active(false);
2166 ruler_meter_action->set_active(false);
2167 ruler_bbt_action->set_active(false);
2168 ruler_timecode_action->set_active(false);
2169 ruler_samples_action->set_active(false);
2170 }
2171 } else if (_grid_type == GridTypeCDFrame) {
2172 ruler_cd_marker_action->set_active(true);
2173 ruler_minsec_action->set_active(true);
2174
2175 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2176 ruler_tempo_action->set_active(false);
2177 ruler_meter_action->set_active(false);
2178 ruler_bbt_action->set_active(false);
2179 ruler_timecode_action->set_active(false);
2180 ruler_samples_action->set_active(false);
2181 }
2182 }
2183 }
2184
2185 void
set_grid_to(GridType gt)2186 Editor::set_grid_to (GridType gt)
2187 {
2188 if (_grid_type == gt) { // already set
2189 return;
2190 }
2191
2192 unsigned int grid_ind = (unsigned int)gt;
2193
2194 if (internal_editing() && UIConfiguration::instance().get_grid_follows_internal()) {
2195 internal_grid_type = gt;
2196 } else {
2197 pre_internal_grid_type = gt;
2198 }
2199
2200 bool grid_type_changed = true;
2201 if ( grid_type_is_musical(_grid_type) && grid_type_is_musical(gt))
2202 grid_type_changed = false;
2203
2204 _grid_type = gt;
2205
2206 if (grid_ind > grid_type_strings.size() - 1) {
2207 grid_ind = 0;
2208 _grid_type = (GridType)grid_ind;
2209 }
2210
2211 string str = grid_type_strings[grid_ind];
2212
2213 if (str != grid_type_selector.get_text()) {
2214 grid_type_selector.set_text (str);
2215 }
2216
2217 if (grid_type_changed && UIConfiguration::instance().get_show_grids_ruler()) {
2218 show_rulers_for_grid ();
2219 }
2220
2221 instant_save ();
2222
2223 if (grid_musical()) {
2224 compute_bbt_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
2225 update_tempo_based_rulers ();
2226 }
2227
2228 mark_region_boundary_cache_dirty ();
2229
2230 redisplay_grid (false);
2231
2232 SnapChanged (); /* EMIT SIGNAL */
2233 }
2234
2235 void
set_snap_mode(SnapMode mode)2236 Editor::set_snap_mode (SnapMode mode)
2237 {
2238 if (internal_editing()) {
2239 internal_snap_mode = mode;
2240 } else {
2241 pre_internal_snap_mode = mode;
2242 }
2243
2244 _snap_mode = mode;
2245
2246 if (_snap_mode == SnapOff) {
2247 snap_mode_button.set_active_state (Gtkmm2ext::Off);
2248 } else {
2249 snap_mode_button.set_active_state (Gtkmm2ext::ExplicitActive);
2250 }
2251
2252 instant_save ();
2253 }
2254
2255 void
set_edit_point_preference(EditPoint ep,bool force)2256 Editor::set_edit_point_preference (EditPoint ep, bool force)
2257 {
2258 if (Profile->get_mixbus()) {
2259 if (ep == EditAtSelectedMarker) {
2260 ep = EditAtPlayhead;
2261 }
2262 }
2263
2264 bool changed = (_edit_point != ep);
2265
2266 _edit_point = ep;
2267
2268 string str = edit_point_strings[(int)ep];
2269 if (str != edit_point_selector.get_text ()) {
2270 edit_point_selector.set_text (str);
2271 }
2272
2273 update_all_enter_cursors();
2274
2275 if (!force && !changed) {
2276 return;
2277 }
2278
2279 const char* action=NULL;
2280
2281 switch (_edit_point) {
2282 case EditAtPlayhead:
2283 action = "edit-at-playhead";
2284 break;
2285 case EditAtSelectedMarker:
2286 action = "edit-at-selected-marker";
2287 break;
2288 case EditAtMouse:
2289 action = "edit-at-mouse";
2290 break;
2291 }
2292
2293 Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action ("Editor", action);
2294 tact->set_active (true);
2295
2296 samplepos_t foo;
2297 bool in_track_canvas;
2298
2299 if (!mouse_sample (foo, in_track_canvas)) {
2300 in_track_canvas = false;
2301 }
2302
2303 reset_canvas_action_sensitivity (in_track_canvas);
2304 sensitize_the_right_region_actions (false);
2305
2306 instant_save ();
2307 }
2308
2309 int
set_state(const XMLNode & node,int version)2310 Editor::set_state (const XMLNode& node, int version)
2311 {
2312 set_id (node);
2313 PBD::Unwinder<bool> nsi (no_save_instant, true);
2314 bool yn;
2315
2316 Tabbable::set_state (node, version);
2317
2318 samplepos_t ph_pos;
2319 if (_session && node.get_property ("playhead", ph_pos)) {
2320 if (ph_pos >= 0) {
2321 _playhead_cursor->set_position (ph_pos);
2322 } else {
2323 warning << _("Playhead position stored with a negative value - ignored (use zero instead)") << endmsg;
2324 _playhead_cursor->set_position (0);
2325 }
2326 } else {
2327 _playhead_cursor->set_position (0);
2328 }
2329
2330 node.get_property ("mixer-width", editor_mixer_strip_width);
2331
2332 node.get_property ("zoom-focus", zoom_focus);
2333 zoom_focus_selection_done (zoom_focus);
2334
2335 double z;
2336 if (node.get_property ("zoom", z)) {
2337 /* older versions of ardour used floating point samples_per_pixel */
2338 reset_zoom (llrintf (z));
2339 } else {
2340 reset_zoom (samples_per_pixel);
2341 }
2342
2343 int32_t cnt;
2344 if (node.get_property ("visible-track-count", cnt)) {
2345 set_visible_track_count (cnt);
2346 }
2347
2348 GridType grid_type;
2349 if (!node.get_property ("grid-type", grid_type)) {
2350 grid_type = _grid_type;
2351 }
2352 set_grid_to (grid_type);
2353
2354 SnapMode sm;
2355 if (node.get_property ("snap-mode", sm)) {
2356 snap_mode_selection_done(sm);
2357 /* set text of Dropdown. in case _snap_mode == SnapOff (default)
2358 * snap_mode_selection_done() will only mark an already active item as active
2359 * which does not trigger set_text().
2360 */
2361 set_snap_mode (sm);
2362 } else {
2363 set_snap_mode (_snap_mode);
2364 }
2365
2366 node.get_property ("internal-grid-type", internal_grid_type);
2367 node.get_property ("internal-snap-mode", internal_snap_mode);
2368 node.get_property ("pre-internal-grid-type", pre_internal_grid_type);
2369 node.get_property ("pre-internal-snap-mode", pre_internal_snap_mode);
2370
2371 std::string mm_str;
2372 if (node.get_property ("mouse-mode", mm_str)) {
2373 MouseMode m = str2mousemode(mm_str);
2374 set_mouse_mode (m, true);
2375 } else {
2376 set_mouse_mode (MouseObject, true);
2377 }
2378
2379 samplepos_t lf_pos;
2380 if (node.get_property ("left-frame", lf_pos)) {
2381 if (lf_pos < 0) {
2382 lf_pos = 0;
2383 }
2384 reset_x_origin (lf_pos);
2385 }
2386
2387 double y_origin;
2388 if (node.get_property ("y-origin", y_origin)) {
2389 reset_y_origin (y_origin);
2390 }
2391
2392 yn = false;
2393 node.get_property ("join-object-range", yn);
2394 {
2395 RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("MouseMode"), X_("set-mouse-mode-object-range"));
2396 /* do it twice to force the change */
2397 tact->set_active (!yn);
2398 tact->set_active (yn);
2399 set_mouse_mode(mouse_mode, true);
2400 }
2401
2402 EditPoint ep;
2403 if (node.get_property ("edit-point", ep)) {
2404 set_edit_point_preference (ep, true);
2405 } else {
2406 set_edit_point_preference (_edit_point);
2407 }
2408
2409 if (node.get_property ("follow-playhead", yn)) {
2410 set_follow_playhead (yn);
2411 }
2412
2413 if (node.get_property ("stationary-playhead", yn)) {
2414 set_stationary_playhead (yn);
2415 }
2416
2417 if (node.get_property ("show-editor-mixer", yn)) {
2418
2419 Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Editor"), X_("show-editor-mixer"));
2420 /* do it twice to force the change */
2421 tact->set_active (!yn);
2422 tact->set_active (yn);
2423 }
2424
2425 yn = false;
2426 node.get_property ("show-editor-list", yn);
2427 {
2428 Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Editor"), X_("show-editor-list"));
2429 /* do it twice to force the change */
2430 tact->set_active (!yn);
2431 tact->set_active (yn);
2432 }
2433
2434 int32_t el_page;
2435 if (node.get_property (X_("editor-list-page"), el_page)) {
2436 _the_notebook.set_current_page (el_page);
2437 }
2438
2439 yn = false;
2440 node.get_property (X_("show-marker-lines"), yn);
2441 {
2442 Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Editor"), X_("show-marker-lines"));
2443 /* do it twice to force the change */
2444 tact->set_active (!yn);
2445 tact->set_active (yn);
2446 }
2447
2448 yn = false;
2449 node.get_property (X_("show-touched-automation"), yn);
2450 {
2451 Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Editor"), X_("show-touched-automation"));
2452 /* do it twice to force the change */
2453 tact->set_active (!yn);
2454 tact->set_active (yn);
2455 }
2456
2457 XMLNodeList children = node.children ();
2458 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2459 selection->set_state (**i, Stateful::current_state_version);
2460 _regions->set_state (**i);
2461 _locations->set_state (**i);
2462 }
2463
2464 if (node.get_property ("maximised", yn)) {
2465 Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Common"), X_("ToggleMaximalEditor"));
2466 bool fs = tact->get_active();
2467 if (yn ^ fs) {
2468 ActionManager::do_action ("Common", "ToggleMaximalEditor");
2469 }
2470 }
2471
2472 samplepos_t nudge_clock_value;
2473 if (node.get_property ("nudge-clock-value", nudge_clock_value)) {
2474 nudge_clock->set (nudge_clock_value);
2475 } else {
2476 nudge_clock->set_mode (AudioClock::Timecode);
2477 nudge_clock->set (_session->sample_rate() * 5, true);
2478 }
2479
2480 {
2481 /* apply state
2482 * Not all properties may have been in XML, but
2483 * those that are linked to a private variable may need changing
2484 */
2485 RefPtr<ToggleAction> tact;
2486
2487 tact = ActionManager::get_toggle_action (X_("Editor"), X_("toggle-follow-playhead"));
2488 yn = _follow_playhead;
2489 if (tact->get_active() != yn) {
2490 tact->set_active (yn);
2491 }
2492
2493 tact = ActionManager::get_toggle_action (X_("Editor"), X_("toggle-stationary-playhead"));
2494 yn = _stationary_playhead;
2495 if (tact->get_active() != yn) {
2496 tact->set_active (yn);
2497 }
2498 }
2499
2500 return 0;
2501 }
2502
2503 XMLNode&
get_state()2504 Editor::get_state ()
2505 {
2506 XMLNode* node = new XMLNode (X_("Editor"));
2507
2508 node->set_property ("id", id().to_s ());
2509
2510 node->add_child_nocopy (Tabbable::get_state());
2511
2512 node->set_property("edit-horizontal-pane-pos", edit_pane.get_divider ());
2513 node->set_property("notebook-shrunk", _notebook_shrunk);
2514 node->set_property("edit-vertical-pane-pos", editor_summary_pane.get_divider());
2515
2516 maybe_add_mixer_strip_width (*node);
2517
2518 node->set_property ("zoom-focus", zoom_focus);
2519
2520 node->set_property ("zoom", samples_per_pixel);
2521 node->set_property ("grid-type", _grid_type);
2522 node->set_property ("snap-mode", _snap_mode);
2523 node->set_property ("internal-grid-type", internal_grid_type);
2524 node->set_property ("internal-snap-mode", internal_snap_mode);
2525 node->set_property ("pre-internal-grid-type", pre_internal_grid_type);
2526 node->set_property ("pre-internal-snap-mode", pre_internal_snap_mode);
2527 node->set_property ("edit-point", _edit_point);
2528 node->set_property ("visible-track-count", _visible_track_count);
2529
2530 node->set_property ("playhead", _playhead_cursor->current_sample ());
2531 node->set_property ("left-frame", _leftmost_sample);
2532 node->set_property ("y-origin", vertical_adjustment.get_value ());
2533
2534 node->set_property ("maximised", _maximised);
2535 node->set_property ("follow-playhead", _follow_playhead);
2536 node->set_property ("stationary-playhead", _stationary_playhead);
2537 node->set_property ("mouse-mode", mouse_mode);
2538 node->set_property ("join-object-range", smart_mode_action->get_active ());
2539
2540 Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Editor"), X_("show-editor-mixer"));
2541 node->set_property (X_("show-editor-mixer"), tact->get_active());
2542
2543 tact = ActionManager::get_toggle_action (X_("Editor"), X_("show-editor-list"));
2544 node->set_property (X_("show-editor-list"), tact->get_active());
2545
2546 node->set_property (X_("editor-list-page"), _the_notebook.get_current_page ());
2547
2548 if (button_bindings) {
2549 XMLNode* bb = new XMLNode (X_("Buttons"));
2550 button_bindings->save (*bb);
2551 node->add_child_nocopy (*bb);
2552 }
2553
2554 node->set_property (X_("show-marker-lines"), _show_marker_lines);
2555 node->set_property (X_("show-touched-automation"), _show_touched_automation);
2556
2557 node->add_child_nocopy (selection->get_state ());
2558 node->add_child_nocopy (_regions->get_state ());
2559
2560 node->set_property ("nudge-clock-value", nudge_clock->current_duration());
2561
2562 node->add_child_nocopy (_locations->get_state ());
2563
2564 return *node;
2565 }
2566
2567 /** if @param trackview_relative_offset is true, @param y y is an offset into the trackview area, in pixel units
2568 * if @param trackview_relative_offset is false, @param y y is a global canvas * coordinate, in pixel units
2569 *
2570 * @return pair: TimeAxisView that y is over, layer index.
2571 *
2572 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2573 * in stacked or expanded region display mode, otherwise 0.
2574 */
2575 std::pair<TimeAxisView *, double>
trackview_by_y_position(double y,bool trackview_relative_offset) const2576 Editor::trackview_by_y_position (double y, bool trackview_relative_offset) const
2577 {
2578 if (!trackview_relative_offset) {
2579 y -= _trackview_group->canvas_origin().y;
2580 }
2581
2582 if (y < 0) {
2583 return std::make_pair ((TimeAxisView *) 0, 0);
2584 }
2585
2586 for (TrackViewList::const_iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2587
2588 std::pair<TimeAxisView*, double> const r = (*iter)->covers_y_position (y);
2589
2590 if (r.first) {
2591 return r;
2592 }
2593 }
2594
2595 return std::make_pair ((TimeAxisView *) 0, 0);
2596 }
2597
2598 void
set_snapped_cursor_position(samplepos_t pos)2599 Editor::set_snapped_cursor_position (samplepos_t pos)
2600 {
2601 if (_edit_point == EditAtMouse) {
2602 _snapped_cursor->set_position(pos);
2603 }
2604 }
2605
2606
2607 /** Snap a position to the grid, if appropriate, taking into account current
2608 * grid settings and also the state of any snap modifier keys that may be pressed.
2609 * @param start Position to snap.
2610 * @param event Event to get current key modifier information from, or 0.
2611 */
2612 void
snap_to_with_modifier(MusicSample & start,GdkEvent const * event,RoundMode direction,SnapPref pref)2613 Editor::snap_to_with_modifier (MusicSample& start, GdkEvent const * event, RoundMode direction, SnapPref pref)
2614 {
2615 if (!_session || !event) {
2616 return;
2617 }
2618
2619 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2620 if (_snap_mode == SnapOff) {
2621 snap_to_internal (start, direction, pref);
2622 } else {
2623 start.set (start.sample, 0);
2624 }
2625 } else {
2626 if (_snap_mode != SnapOff) {
2627 snap_to_internal (start, direction, pref);
2628 } else if (ArdourKeyboard::indicates_snap_delta (event->button.state)) {
2629 /* SnapOff, but we pressed the snap_delta modifier */
2630 snap_to_internal (start, direction, pref);
2631 } else {
2632 start.set (start.sample, 0);
2633 }
2634 }
2635 }
2636
2637 void
snap_to(MusicSample & start,RoundMode direction,SnapPref pref,bool ensure_snap)2638 Editor::snap_to (MusicSample& start, RoundMode direction, SnapPref pref, bool ensure_snap)
2639 {
2640 if (!_session || (_snap_mode == SnapOff && !ensure_snap)) {
2641 start.set (start.sample, 0);
2642 return;
2643 }
2644
2645 snap_to_internal (start, direction, pref, ensure_snap);
2646 }
2647
2648 static void
check_best_snap(samplepos_t presnap,samplepos_t & test,samplepos_t & dist,samplepos_t & best)2649 check_best_snap (samplepos_t presnap, samplepos_t &test, samplepos_t &dist, samplepos_t &best)
2650 {
2651 samplepos_t diff = abs (test - presnap);
2652 if (diff < dist) {
2653 dist = diff;
2654 best = test;
2655 }
2656
2657 test = max_samplepos; // reset this so it doesn't get accidentally reused
2658 }
2659
2660 MusicSample
snap_to_timecode(MusicSample presnap,RoundMode direction,SnapPref gpref)2661 Editor::snap_to_timecode (MusicSample presnap, RoundMode direction, SnapPref gpref)
2662 {
2663 samplepos_t start = presnap.sample;
2664 const samplepos_t one_timecode_second = (samplepos_t)(rint(_session->timecode_frames_per_second()) * _session->samples_per_timecode_frame());
2665 samplepos_t one_timecode_minute = (samplepos_t)(rint(_session->timecode_frames_per_second()) * _session->samples_per_timecode_frame() * 60);
2666
2667 TimecodeRulerScale scale = (gpref != SnapToGrid_Unscaled) ? timecode_ruler_scale : timecode_show_samples;
2668
2669 switch (scale) {
2670 case timecode_show_bits:
2671 case timecode_show_samples:
2672 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2673 fmod((double)start, (double)_session->samples_per_timecode_frame()) == 0) {
2674 /* start is already on a whole timecode frame, do nothing */
2675 } else if (((direction == 0) && (fmod((double)start, (double)_session->samples_per_timecode_frame()) > (_session->samples_per_timecode_frame() / 2))) || (direction > 0)) {
2676 start = (samplepos_t) (ceil ((double) start / _session->samples_per_timecode_frame()) * _session->samples_per_timecode_frame());
2677 } else {
2678 start = (samplepos_t) (floor ((double) start / _session->samples_per_timecode_frame()) * _session->samples_per_timecode_frame());
2679 }
2680 break;
2681
2682 case timecode_show_seconds:
2683 if (_session->config.get_timecode_offset_negative()) {
2684 start += _session->config.get_timecode_offset ();
2685 } else {
2686 start -= _session->config.get_timecode_offset ();
2687 }
2688 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2689 (start % one_timecode_second == 0)) {
2690 /* start is already on a whole second, do nothing */
2691 } else if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2692 start = (samplepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2693 } else {
2694 start = (samplepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2695 }
2696
2697 if (_session->config.get_timecode_offset_negative()) {
2698 start -= _session->config.get_timecode_offset ();
2699 } else {
2700 start += _session->config.get_timecode_offset ();
2701 }
2702 break;
2703
2704 case timecode_show_minutes:
2705 case timecode_show_hours:
2706 case timecode_show_many_hours:
2707 if (_session->config.get_timecode_offset_negative()) {
2708 start += _session->config.get_timecode_offset ();
2709 } else {
2710 start -= _session->config.get_timecode_offset ();
2711 }
2712 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2713 (start % one_timecode_minute == 0)) {
2714 /* start is already on a whole minute, do nothing */
2715 } else if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2716 start = (samplepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2717 } else {
2718 start = (samplepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2719 }
2720 if (_session->config.get_timecode_offset_negative()) {
2721 start -= _session->config.get_timecode_offset ();
2722 } else {
2723 start += _session->config.get_timecode_offset ();
2724 }
2725 break;
2726 default:
2727 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2728 }
2729
2730 MusicSample ret(start,0);
2731 return ret;
2732 }
2733
2734 MusicSample
snap_to_minsec(MusicSample presnap,RoundMode direction,SnapPref gpref)2735 Editor::snap_to_minsec (MusicSample presnap, RoundMode direction, SnapPref gpref)
2736 {
2737 MusicSample ret(presnap);
2738
2739 const samplepos_t one_second = _session->sample_rate();
2740 const samplepos_t one_minute = one_second * 60;
2741 const samplepos_t one_hour = one_minute * 60;
2742
2743 MinsecRulerScale scale = (gpref != SnapToGrid_Unscaled) ? minsec_ruler_scale : minsec_show_seconds;
2744
2745 switch (scale) {
2746 case minsec_show_msecs:
2747 case minsec_show_seconds: {
2748 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2749 presnap.sample % one_second == 0) {
2750 /* start is already on a whole second, do nothing */
2751 } else if (((direction == 0) && (presnap.sample % one_second > one_second / 2)) || (direction > 0)) {
2752 ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_second) * one_second;
2753 } else {
2754 ret.sample = (samplepos_t) floor ((double) presnap.sample / one_second) * one_second;
2755 }
2756 } break;
2757
2758 case minsec_show_minutes: {
2759 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2760 presnap.sample % one_minute == 0) {
2761 /* start is already on a whole minute, do nothing */
2762 } else if (((direction == 0) && (presnap.sample % one_minute > one_minute / 2)) || (direction > 0)) {
2763 ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_minute) * one_minute;
2764 } else {
2765 ret.sample = (samplepos_t) floor ((double) presnap.sample / one_minute) * one_minute;
2766 }
2767 } break;
2768
2769 default: {
2770 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2771 presnap.sample % one_hour == 0) {
2772 /* start is already on a whole hour, do nothing */
2773 } else if (((direction == 0) && (presnap.sample % one_hour > one_hour / 2)) || (direction > 0)) {
2774 ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_hour) * one_hour;
2775 } else {
2776 ret.sample = (samplepos_t) floor ((double) presnap.sample / one_hour) * one_hour;
2777 }
2778 } break;
2779 }
2780
2781 return ret;
2782 }
2783
2784 MusicSample
snap_to_cd_frames(MusicSample presnap,RoundMode direction,SnapPref gpref)2785 Editor::snap_to_cd_frames (MusicSample presnap, RoundMode direction, SnapPref gpref)
2786 {
2787 if ((gpref != SnapToGrid_Unscaled) && (minsec_ruler_scale != minsec_show_msecs)) {
2788 return snap_to_minsec (presnap, direction, gpref);
2789 }
2790
2791 const samplepos_t one_second = _session->sample_rate();
2792
2793 MusicSample ret(presnap);
2794
2795 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2796 presnap.sample % (one_second/75) == 0) {
2797 /* start is already on a whole CD sample, do nothing */
2798 } else if (((direction == 0) && (presnap.sample % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2799 ret.sample = (samplepos_t) ceil ((double) presnap.sample / (one_second / 75)) * (one_second / 75);
2800 } else {
2801 ret.sample = (samplepos_t) floor ((double) presnap.sample / (one_second / 75)) * (one_second / 75);
2802 }
2803
2804 return ret;
2805 }
2806
2807 MusicSample
snap_to_bbt(MusicSample presnap,RoundMode direction,SnapPref gpref)2808 Editor::snap_to_bbt (MusicSample presnap, RoundMode direction, SnapPref gpref)
2809 {
2810 MusicSample ret(presnap);
2811
2812 if (gpref != SnapToGrid_Unscaled) { // use the visual grid lines which are limited by the zoom scale that the user selected
2813
2814 int divisor = 2;
2815 switch (_grid_type) {
2816 case GridTypeBeatDiv3:
2817 case GridTypeBeatDiv6:
2818 case GridTypeBeatDiv12:
2819 case GridTypeBeatDiv24:
2820 divisor = 3;
2821 break;
2822 case GridTypeBeatDiv5:
2823 case GridTypeBeatDiv10:
2824 case GridTypeBeatDiv20:
2825 divisor = 5;
2826 break;
2827 case GridTypeBeatDiv7:
2828 case GridTypeBeatDiv14:
2829 case GridTypeBeatDiv28:
2830 divisor = 7;
2831 break;
2832 default:
2833 divisor = 2;
2834 };
2835
2836 BBTRulerScale scale = bbt_ruler_scale;
2837 switch (scale) {
2838 case bbt_show_many:
2839 case bbt_show_64:
2840 case bbt_show_16:
2841 case bbt_show_4:
2842 case bbt_show_1:
2843 ret = _session->tempo_map().round_to_bar (presnap.sample, direction);
2844 break;
2845 case bbt_show_quarters:
2846 ret = _session->tempo_map().round_to_beat (presnap.sample, direction);
2847 break;
2848 case bbt_show_eighths:
2849 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 1 * divisor, direction);
2850 break;
2851 case bbt_show_sixteenths:
2852 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 2 * divisor, direction);
2853 break;
2854 case bbt_show_thirtyseconds:
2855 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 4 * divisor, direction);
2856 break;
2857 case bbt_show_sixtyfourths:
2858 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 8 * divisor, direction);
2859 break;
2860 case bbt_show_onetwentyeighths:
2861 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 16 * divisor, direction);
2862 break;
2863 }
2864 } else {
2865 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, get_grid_beat_divisions(_grid_type), direction);
2866 }
2867
2868 return ret;
2869 }
2870
2871 ARDOUR::MusicSample
snap_to_grid(MusicSample presnap,RoundMode direction,SnapPref gpref)2872 Editor::snap_to_grid (MusicSample presnap, RoundMode direction, SnapPref gpref)
2873 {
2874 MusicSample ret(presnap);
2875
2876 if (grid_musical()) {
2877 ret = snap_to_bbt (presnap, direction, gpref);
2878 }
2879
2880 switch (_grid_type) {
2881 case GridTypeTimecode:
2882 ret = snap_to_timecode(presnap, direction, gpref);
2883 break;
2884 case GridTypeMinSec:
2885 ret = snap_to_minsec(presnap, direction, gpref);
2886 break;
2887 case GridTypeCDFrame:
2888 ret = snap_to_cd_frames(presnap, direction, gpref);
2889 break;
2890 default:
2891 {}
2892 };
2893
2894 return ret;
2895 }
2896
2897 samplepos_t
snap_to_marker(samplepos_t presnap,RoundMode direction)2898 Editor::snap_to_marker (samplepos_t presnap, RoundMode direction)
2899 {
2900 samplepos_t before;
2901 samplepos_t after;
2902 samplepos_t test;
2903
2904 if (_session->locations()->list().empty()) {
2905 /* No marks to snap to, so just don't snap */
2906 return 0;
2907 }
2908
2909 _session->locations()->marks_either_side (presnap, before, after);
2910
2911 if (before == max_samplepos) {
2912 test = after;
2913 } else if (after == max_samplepos) {
2914 test = before;
2915 } else {
2916 switch (direction) {
2917 case RoundUpAlways:
2918 case RoundUpMaybe:
2919 test = after;
2920 break;
2921 case RoundDownMaybe:
2922 case RoundDownAlways:
2923 test = before;
2924 break;
2925 case RoundNearest:
2926 default:
2927 if ((presnap - before) < (after - presnap)) {
2928 test = before;
2929 } else {
2930 test = after;
2931 }
2932 }
2933 }
2934
2935 return test;
2936 }
2937
2938 void
snap_to_internal(MusicSample & start,RoundMode direction,SnapPref pref,bool ensure_snap)2939 Editor::snap_to_internal (MusicSample& start, RoundMode direction, SnapPref pref, bool ensure_snap)
2940 {
2941 UIConfiguration const& uic (UIConfiguration::instance ());
2942 const samplepos_t presnap = start.sample;
2943
2944 samplepos_t test = max_samplepos; // for each snap, we'll use this value
2945 samplepos_t dist = max_samplepos; // this records the distance of the best snap result we've found so far
2946 samplepos_t best = max_samplepos; // this records the best snap-result we've found so far
2947
2948 /* check snap-to-marker */
2949 if ((pref == SnapToAny_Visual) && uic.get_snap_to_marks ()) {
2950 test = snap_to_marker (presnap, direction);
2951 check_best_snap (presnap, test, dist, best);
2952 }
2953
2954 /* check snap-to-region-{start/end/sync} */
2955 if ((pref == SnapToAny_Visual) && (uic.get_snap_to_region_start () || uic.get_snap_to_region_end () || uic.get_snap_to_region_sync ())) {
2956
2957 if (!region_boundary_cache.empty ()) {
2958
2959 vector<samplepos_t>::iterator prev = region_boundary_cache.begin ();
2960 vector<samplepos_t>::iterator next = std::upper_bound (region_boundary_cache.begin (), region_boundary_cache.end (), presnap);
2961 if (next != region_boundary_cache.begin ()) {
2962 prev = next;
2963 prev--;
2964 }
2965 if (next == region_boundary_cache.end ()) {
2966 next--;
2967 }
2968
2969 if ((direction == RoundUpMaybe || direction == RoundUpAlways)) {
2970 test = *next;
2971 } else if ((direction == RoundDownMaybe || direction == RoundDownAlways)) {
2972 test = *prev;
2973 } else if (direction == 0) {
2974 if ((presnap - *prev) < (*next - presnap)) {
2975 test = *prev;
2976 } else {
2977 test = *next;
2978 }
2979 }
2980
2981 }
2982
2983 check_best_snap (presnap, test, dist, best);
2984 }
2985
2986 /* check Grid */
2987 if (uic.get_snap_to_grid () && (_grid_type != GridTypeNone)) {
2988 MusicSample pre (presnap, 0);
2989 MusicSample post = snap_to_grid (pre, direction, pref);
2990 check_best_snap (presnap, post.sample, dist, best);
2991 }
2992
2993 if (max_samplepos == best) {
2994 return;
2995 }
2996
2997 /* now check "magnetic" state: is the grid within reasonable on-screen distance to trigger a snap?
2998 * this also helps to avoid snapping to somewhere the user can't see. (i.e.: I clicked on a region and it disappeared!!)
2999 * ToDo: Perhaps this should only occur if EditPointMouse?
3000 */
3001 samplecnt_t snap_threshold_s = pixel_to_sample (uic.get_snap_threshold ());
3002
3003 if (!ensure_snap && ::llabs (presnap - best) > snap_threshold_s) {
3004 return;
3005 }
3006
3007 start.set (best, 0);
3008 }
3009
3010
3011 void
setup_toolbar()3012 Editor::setup_toolbar ()
3013 {
3014 HBox* mode_box = manage(new HBox);
3015 mode_box->set_border_width (2);
3016 mode_box->set_spacing(2);
3017
3018 HBox* mouse_mode_box = manage (new HBox);
3019 HBox* mouse_mode_hbox = manage (new HBox);
3020 VBox* mouse_mode_vbox = manage (new VBox);
3021 Alignment* mouse_mode_align = manage (new Alignment);
3022
3023 Glib::RefPtr<SizeGroup> mouse_mode_size_group = SizeGroup::create (SIZE_GROUP_VERTICAL);
3024 mouse_mode_size_group->add_widget (smart_mode_button);
3025 mouse_mode_size_group->add_widget (mouse_move_button);
3026 mouse_mode_size_group->add_widget (mouse_cut_button);
3027 mouse_mode_size_group->add_widget (mouse_select_button);
3028 mouse_mode_size_group->add_widget (mouse_timefx_button);
3029 if (!Profile->get_mixbus()) {
3030 mouse_mode_size_group->add_widget (mouse_audition_button);
3031 }
3032 mouse_mode_size_group->add_widget (mouse_draw_button);
3033 mouse_mode_size_group->add_widget (mouse_content_button);
3034
3035 if (!Profile->get_mixbus()) {
3036 mouse_mode_size_group->add_widget (zoom_in_button);
3037 mouse_mode_size_group->add_widget (zoom_out_button);
3038 mouse_mode_size_group->add_widget (zoom_out_full_button);
3039 mouse_mode_size_group->add_widget (zoom_focus_selector);
3040 mouse_mode_size_group->add_widget (tav_shrink_button);
3041 mouse_mode_size_group->add_widget (tav_expand_button);
3042 } else {
3043 mouse_mode_size_group->add_widget (zoom_preset_selector);
3044 mouse_mode_size_group->add_widget (visible_tracks_selector);
3045 }
3046
3047 mouse_mode_size_group->add_widget (grid_type_selector);
3048 mouse_mode_size_group->add_widget (snap_mode_button);
3049
3050 mouse_mode_size_group->add_widget (edit_point_selector);
3051 mouse_mode_size_group->add_widget (edit_mode_selector);
3052
3053 mouse_mode_size_group->add_widget (*nudge_clock);
3054 mouse_mode_size_group->add_widget (nudge_forward_button);
3055 mouse_mode_size_group->add_widget (nudge_backward_button);
3056
3057 mouse_mode_hbox->set_spacing (2);
3058 mouse_mode_hbox->pack_start (smart_mode_button, false, false);
3059
3060 mouse_mode_hbox->pack_start (mouse_move_button, false, false);
3061 mouse_mode_hbox->pack_start (mouse_select_button, false, false);
3062
3063 mouse_mode_hbox->pack_start (mouse_cut_button, false, false);
3064
3065 if (!ARDOUR::Profile->get_mixbus()) {
3066 mouse_mode_hbox->pack_start (mouse_audition_button, false, false);
3067 }
3068
3069 mouse_mode_hbox->pack_start (mouse_timefx_button, false, false);
3070 mouse_mode_hbox->pack_start (mouse_draw_button, false, false);
3071 mouse_mode_hbox->pack_start (mouse_content_button, false, false);
3072
3073 mouse_mode_vbox->pack_start (*mouse_mode_hbox);
3074
3075 mouse_mode_align->add (*mouse_mode_vbox);
3076 mouse_mode_align->set (0.5, 1.0, 0.0, 0.0);
3077
3078 mouse_mode_box->pack_start (*mouse_mode_align, false, false);
3079
3080 edit_mode_selector.set_name ("mouse mode button");
3081
3082 mode_box->pack_start (edit_mode_selector, false, false);
3083 mode_box->pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3084 mode_box->pack_start (edit_point_selector, false, false);
3085 mode_box->pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3086
3087 mode_box->pack_start (*mouse_mode_box, false, false);
3088
3089 /* Zoom */
3090
3091 _zoom_box.set_spacing (2);
3092 _zoom_box.set_border_width (2);
3093
3094 RefPtr<Action> act;
3095
3096 zoom_preset_selector.set_name ("zoom button");
3097 zoom_preset_selector.set_icon (ArdourIcon::ZoomExpand);
3098
3099 zoom_in_button.set_name ("zoom button");
3100 zoom_in_button.set_icon (ArdourIcon::ZoomIn);
3101 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-in"));
3102 zoom_in_button.set_related_action (act);
3103
3104 zoom_out_button.set_name ("zoom button");
3105 zoom_out_button.set_icon (ArdourIcon::ZoomOut);
3106 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-out"));
3107 zoom_out_button.set_related_action (act);
3108
3109 zoom_out_full_button.set_name ("zoom button");
3110 zoom_out_full_button.set_icon (ArdourIcon::ZoomFull);
3111 act = ActionManager::get_action (X_("Editor"), X_("zoom-to-session"));
3112 zoom_out_full_button.set_related_action (act);
3113
3114 zoom_focus_selector.set_name ("zoom button");
3115
3116 if (ARDOUR::Profile->get_mixbus()) {
3117 _zoom_box.pack_start (zoom_preset_selector, false, false);
3118 } else {
3119 _zoom_box.pack_start (zoom_out_button, false, false);
3120 _zoom_box.pack_start (zoom_in_button, false, false);
3121 _zoom_box.pack_start (zoom_out_full_button, false, false);
3122 _zoom_box.pack_start (zoom_focus_selector, false, false);
3123 }
3124
3125 /* Track zoom buttons */
3126 _track_box.set_spacing (2);
3127 _track_box.set_border_width (2);
3128
3129 visible_tracks_selector.set_name ("zoom button");
3130 if (Profile->get_mixbus()) {
3131 visible_tracks_selector.set_icon (ArdourIcon::TimeAxisExpand);
3132 } else {
3133 set_size_request_to_display_given_text (visible_tracks_selector, _("All"), 30, 2);
3134 }
3135
3136 tav_expand_button.set_name ("zoom button");
3137 tav_expand_button.set_icon (ArdourIcon::TimeAxisExpand);
3138 act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
3139 tav_expand_button.set_related_action (act);
3140
3141 tav_shrink_button.set_name ("zoom button");
3142 tav_shrink_button.set_icon (ArdourIcon::TimeAxisShrink);
3143 act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
3144 tav_shrink_button.set_related_action (act);
3145
3146 if (ARDOUR::Profile->get_mixbus()) {
3147 _track_box.pack_start (visible_tracks_selector);
3148 } else {
3149 _track_box.pack_start (visible_tracks_selector);
3150 _track_box.pack_start (tav_shrink_button);
3151 _track_box.pack_start (tav_expand_button);
3152 }
3153
3154 snap_box.set_spacing (2);
3155 snap_box.set_border_width (2);
3156
3157 grid_type_selector.set_name ("mouse mode button");
3158
3159 snap_mode_button.set_name ("mouse mode button");
3160
3161 edit_point_selector.set_name ("mouse mode button");
3162
3163 snap_box.pack_start (snap_mode_button, false, false);
3164 snap_box.pack_start (grid_type_selector, false, false);
3165
3166 /* Nudge */
3167
3168 HBox *nudge_box = manage (new HBox);
3169 nudge_box->set_spacing (2);
3170 nudge_box->set_border_width (2);
3171
3172 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
3173 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
3174
3175 nudge_box->pack_start (nudge_backward_button, false, false);
3176 nudge_box->pack_start (nudge_forward_button, false, false);
3177 nudge_box->pack_start (*nudge_clock, false, false);
3178
3179
3180 /* Pack everything in... */
3181
3182 toolbar_hbox.set_spacing (2);
3183 toolbar_hbox.set_border_width (2);
3184
3185 ArdourWidgets::ArdourDropShadow *tool_shadow = manage (new (ArdourWidgets::ArdourDropShadow));
3186 tool_shadow->set_size_request (4, -1);
3187 tool_shadow->show();
3188
3189 ebox_hpacker.pack_start (*tool_shadow, false, false);
3190 ebox_hpacker.pack_start(ebox_vpacker, true, true);
3191
3192 Gtk::EventBox* spacer = manage (new Gtk::EventBox); // extra space under the mouse toolbar, for aesthetics
3193 spacer->set_name("EditorWindow");
3194 spacer->set_size_request(-1,4);
3195 spacer->show();
3196
3197 ebox_vpacker.pack_start(toolbar_hbox, false, false);
3198 ebox_vpacker.pack_start(*spacer, false, false);
3199 ebox_vpacker.show();
3200
3201 toolbar_hbox.pack_start (*mode_box, false, false);
3202 toolbar_hbox.pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3203 toolbar_hbox.pack_start (snap_box, false, false);
3204 toolbar_hbox.pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3205 toolbar_hbox.pack_start (*nudge_box, false, false);
3206 toolbar_hbox.pack_end (_zoom_box, false, false, 2);
3207 toolbar_hbox.pack_end (*(manage (new ArdourVSpacer ())), false, false, 3);
3208 toolbar_hbox.pack_end (_track_box, false, false);
3209
3210 toolbar_hbox.show_all ();
3211 }
3212
3213 void
build_edit_point_menu()3214 Editor::build_edit_point_menu ()
3215 {
3216 using namespace Menu_Helpers;
3217
3218 edit_point_selector.AddMenuElem (MenuElem (edit_point_strings[(int)EditAtPlayhead], sigc::bind (sigc::mem_fun(*this, &Editor::edit_point_selection_done), (EditPoint) EditAtPlayhead)));
3219 if(!Profile->get_mixbus())
3220 edit_point_selector.AddMenuElem (MenuElem (edit_point_strings[(int)EditAtSelectedMarker], sigc::bind (sigc::mem_fun(*this, &Editor::edit_point_selection_done), (EditPoint) EditAtSelectedMarker)));
3221 edit_point_selector.AddMenuElem (MenuElem (edit_point_strings[(int)EditAtMouse], sigc::bind (sigc::mem_fun(*this, &Editor::edit_point_selection_done), (EditPoint) EditAtMouse)));
3222
3223 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_TRIANGLE_WIDTH, 2);
3224 }
3225
3226 void
build_edit_mode_menu()3227 Editor::build_edit_mode_menu ()
3228 {
3229 using namespace Menu_Helpers;
3230
3231 edit_mode_selector.AddMenuElem (MenuElem (edit_mode_strings[(int)Slide], sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Slide)));
3232 edit_mode_selector.AddMenuElem (MenuElem (edit_mode_strings[(int)Ripple], sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Ripple)));
3233 edit_mode_selector.AddMenuElem (MenuElem (edit_mode_strings[(int)Lock], sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Lock)));
3234 /* Note: Splice was removed */
3235
3236 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_TRIANGLE_WIDTH, 2);
3237 }
3238
3239 void
build_grid_type_menu()3240 Editor::build_grid_type_menu ()
3241 {
3242 using namespace Menu_Helpers;
3243
3244 /* main grid: bars, quarter-notes, etc */
3245 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeNone], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeNone)));
3246 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBar], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBar)));
3247 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeat], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeat)));
3248 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv2], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv2)));
3249 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv4], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv4)));
3250 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv8], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv8)));
3251 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv16], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv16)));
3252 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv32], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv32)));
3253
3254 /* triplet grid */
3255 grid_type_selector.AddMenuElem(SeparatorElem());
3256 Gtk::Menu *_triplet_menu = manage (new Menu);
3257 MenuList& triplet_items (_triplet_menu->items());
3258 {
3259 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv3], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv3)));
3260 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv6], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv6)));
3261 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv12], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv12)));
3262 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv24], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv24)));
3263 }
3264 grid_type_selector.AddMenuElem (Menu_Helpers::MenuElem (_("Triplets"), *_triplet_menu));
3265
3266 /* quintuplet grid */
3267 Gtk::Menu *_quintuplet_menu = manage (new Menu);
3268 MenuList& quintuplet_items (_quintuplet_menu->items());
3269 {
3270 quintuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv5], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv5)));
3271 quintuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv10], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv10)));
3272 quintuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv20], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv20)));
3273 }
3274 grid_type_selector.AddMenuElem (Menu_Helpers::MenuElem (_("Quintuplets"), *_quintuplet_menu));
3275
3276 /* septuplet grid */
3277 Gtk::Menu *_septuplet_menu = manage (new Menu);
3278 MenuList& septuplet_items (_septuplet_menu->items());
3279 {
3280 septuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv7], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv7)));
3281 septuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv14], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv14)));
3282 septuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv28], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv28)));
3283 }
3284 grid_type_selector.AddMenuElem (Menu_Helpers::MenuElem (_("Septuplets"), *_septuplet_menu));
3285
3286 grid_type_selector.AddMenuElem(SeparatorElem());
3287 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeTimecode], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeTimecode)));
3288 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeMinSec], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeMinSec)));
3289 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeCDFrame], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeCDFrame)));
3290 }
3291
3292 void
setup_tooltips()3293 Editor::setup_tooltips ()
3294 {
3295 set_tooltip (smart_mode_button, _("Smart Mode (add range functions to Grab Mode)"));
3296 set_tooltip (mouse_move_button, _("Grab Mode (select/move objects)"));
3297 set_tooltip (mouse_cut_button, _("Cut Mode (split regions)"));
3298 set_tooltip (mouse_select_button, _("Range Mode (select time ranges)"));
3299 set_tooltip (mouse_draw_button, _("Draw Mode (draw and edit gain/notes/automation)"));
3300 set_tooltip (mouse_timefx_button, _("Stretch Mode (time-stretch audio and midi regions, preserving pitch)"));
3301 set_tooltip (mouse_audition_button, _("Audition Mode (listen to regions)"));
3302 set_tooltip (mouse_content_button, _("Internal Edit Mode (edit notes and automation points)"));
3303 set_tooltip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
3304 set_tooltip (nudge_forward_button, _("Nudge Region/Selection Later"));
3305 set_tooltip (nudge_backward_button, _("Nudge Region/Selection Earlier"));
3306 set_tooltip (zoom_in_button, _("Zoom In"));
3307 set_tooltip (zoom_out_button, _("Zoom Out"));
3308 set_tooltip (zoom_preset_selector, _("Zoom to Time Scale"));
3309 set_tooltip (zoom_out_full_button, _("Zoom to Session"));
3310 set_tooltip (zoom_focus_selector, _("Zoom Focus"));
3311 set_tooltip (tav_expand_button, _("Expand Tracks"));
3312 set_tooltip (tav_shrink_button, _("Shrink Tracks"));
3313 set_tooltip (visible_tracks_selector, _("Number of visible tracks"));
3314 set_tooltip (grid_type_selector, _("Grid Mode"));
3315 set_tooltip (snap_mode_button, _("Snap Mode\n\nRight-click to visit Snap preferences."));
3316 set_tooltip (edit_point_selector, _("Edit Point"));
3317 set_tooltip (edit_mode_selector, _("Edit Mode"));
3318 set_tooltip (nudge_clock, _("Nudge Clock\n(controls distance used to nudge regions and selections)"));
3319 }
3320
3321 int
convert_drop_to_paths(vector<string> & paths,const RefPtr<Gdk::DragContext> &,gint,gint,const SelectionData & data,guint,guint)3322 Editor::convert_drop_to_paths (
3323 vector<string>& paths,
3324 const RefPtr<Gdk::DragContext>& /*context*/,
3325 gint /*x*/,
3326 gint /*y*/,
3327 const SelectionData& data,
3328 guint /*info*/,
3329 guint /*time*/)
3330 {
3331 if (_session == 0) {
3332 return -1;
3333 }
3334
3335 vector<string> uris = data.get_uris();
3336
3337 if (uris.empty()) {
3338
3339 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3340 are actually URI lists. So do it by hand.
3341 */
3342
3343 if (data.get_target() != "text/plain") {
3344 return -1;
3345 }
3346
3347 /* Parse the "uri-list" format that Nautilus provides,
3348 where each pathname is delimited by \r\n.
3349
3350 THERE MAY BE NO NULL TERMINATING CHAR!!!
3351 */
3352
3353 string txt = data.get_text();
3354 char* p;
3355 const char* q;
3356
3357 p = (char *) malloc (txt.length() + 1);
3358 txt.copy (p, txt.length(), 0);
3359 p[txt.length()] = '\0';
3360
3361 while (p)
3362 {
3363 if (*p != '#')
3364 {
3365 while (g_ascii_isspace (*p))
3366 p++;
3367
3368 q = p;
3369 while (*q && (*q != '\n') && (*q != '\r')) {
3370 q++;
3371 }
3372
3373 if (q > p)
3374 {
3375 q--;
3376 while (q > p && g_ascii_isspace (*q))
3377 q--;
3378
3379 if (q > p)
3380 {
3381 uris.push_back (string (p, q - p + 1));
3382 }
3383 }
3384 }
3385 p = strchr (p, '\n');
3386 if (p)
3387 p++;
3388 }
3389
3390 free ((void*)p);
3391
3392 if (uris.empty()) {
3393 return -1;
3394 }
3395 }
3396
3397 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3398 if ((*i).substr (0,7) == "file://") {
3399 paths.push_back (Glib::filename_from_uri (*i));
3400 }
3401 }
3402
3403 return 0;
3404 }
3405
3406 void
new_tempo_section()3407 Editor::new_tempo_section ()
3408 {
3409 }
3410
3411 void
map_transport_state()3412 Editor::map_transport_state ()
3413 {
3414 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state);
3415
3416 if (_session && _session->transport_stopped()) {
3417 have_pending_keyboard_selection = false;
3418 }
3419
3420 update_loop_range_view ();
3421 }
3422
3423 void
transport_looped()3424 Editor::transport_looped ()
3425 {
3426 /* reset Playhead position interpolation.
3427 * see Editor::super_rapid_screen_update
3428 */
3429 _last_update_time = 0;
3430 }
3431
3432 /* UNDO/REDO */
3433
3434 void
begin_selection_op_history()3435 Editor::begin_selection_op_history ()
3436 {
3437 selection_op_cmd_depth = 0;
3438 selection_op_history_it = 0;
3439
3440 while(!selection_op_history.empty()) {
3441 delete selection_op_history.front();
3442 selection_op_history.pop_front();
3443 }
3444
3445 selection_undo_action->set_sensitive (false);
3446 selection_redo_action->set_sensitive (false);
3447 selection_op_history.push_front (&_selection_memento->get_state ());
3448 }
3449
3450 void
begin_reversible_selection_op(string name)3451 Editor::begin_reversible_selection_op (string name)
3452 {
3453 if (_session) {
3454 //cerr << name << endl;
3455 /* begin/commit pairs can be nested */
3456 selection_op_cmd_depth++;
3457 }
3458 }
3459
3460 void
commit_reversible_selection_op()3461 Editor::commit_reversible_selection_op ()
3462 {
3463 if (_session) {
3464 if (selection_op_cmd_depth == 1) {
3465
3466 if (selection_op_history_it > 0 && selection_op_history_it < selection_op_history.size()) {
3467 /* The user has undone some selection ops and then made a new one,
3468 * making anything earlier in the list invalid.
3469 */
3470
3471 list<XMLNode *>::iterator it = selection_op_history.begin();
3472 list<XMLNode *>::iterator e_it = it;
3473 advance (e_it, selection_op_history_it);
3474
3475 for (; it != e_it; ++it) {
3476 delete *it;
3477 }
3478 selection_op_history.erase (selection_op_history.begin(), e_it);
3479 }
3480
3481 selection_op_history.push_front (&_selection_memento->get_state ());
3482 selection_op_history_it = 0;
3483
3484 selection_undo_action->set_sensitive (true);
3485 selection_redo_action->set_sensitive (false);
3486 }
3487
3488 if (selection_op_cmd_depth > 0) {
3489 selection_op_cmd_depth--;
3490 }
3491 }
3492 }
3493
3494 void
undo_selection_op()3495 Editor::undo_selection_op ()
3496 {
3497 if (_session) {
3498 selection_op_history_it++;
3499 uint32_t n = 0;
3500 for (std::list<XMLNode *>::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) {
3501 if (n == selection_op_history_it) {
3502 _selection_memento->set_state (*(*i), Stateful::current_state_version);
3503 selection_redo_action->set_sensitive (true);
3504 }
3505 ++n;
3506 }
3507 /* is there an earlier entry? */
3508 if ((selection_op_history_it + 1) >= selection_op_history.size()) {
3509 selection_undo_action->set_sensitive (false);
3510 }
3511 }
3512 }
3513
3514 void
redo_selection_op()3515 Editor::redo_selection_op ()
3516 {
3517 if (_session) {
3518 if (selection_op_history_it > 0) {
3519 selection_op_history_it--;
3520 }
3521 uint32_t n = 0;
3522 for (std::list<XMLNode *>::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) {
3523 if (n == selection_op_history_it) {
3524 _selection_memento->set_state (*(*i), Stateful::current_state_version);
3525 selection_undo_action->set_sensitive (true);
3526 }
3527 ++n;
3528 }
3529
3530 if (selection_op_history_it == 0) {
3531 selection_redo_action->set_sensitive (false);
3532 }
3533 }
3534 }
3535
3536 void
begin_reversible_command(string name)3537 Editor::begin_reversible_command (string name)
3538 {
3539 if (_session) {
3540 before.push_back (&_selection_memento->get_state ());
3541 _session->begin_reversible_command (name);
3542 }
3543 }
3544
3545 void
begin_reversible_command(GQuark q)3546 Editor::begin_reversible_command (GQuark q)
3547 {
3548 if (_session) {
3549 before.push_back (&_selection_memento->get_state ());
3550 _session->begin_reversible_command (q);
3551 }
3552 }
3553
3554 void
abort_reversible_command()3555 Editor::abort_reversible_command ()
3556 {
3557 if (_session) {
3558 while(!before.empty()) {
3559 delete before.front();
3560 before.pop_front();
3561 }
3562 _session->abort_reversible_command ();
3563 }
3564 }
3565
3566 void
commit_reversible_command()3567 Editor::commit_reversible_command ()
3568 {
3569 if (_session) {
3570 if (before.size() == 1) {
3571 _session->add_command (new MementoCommand<SelectionMemento>(*(_selection_memento), before.front(), &_selection_memento->get_state ()));
3572 redo_action->set_sensitive(false);
3573 undo_action->set_sensitive(true);
3574 begin_selection_op_history ();
3575 }
3576
3577 if (before.empty()) {
3578 cerr << "Please call begin_reversible_command() before commit_reversible_command()." << endl;
3579 } else {
3580 before.pop_back();
3581 }
3582
3583 _session->commit_reversible_command ();
3584 }
3585 }
3586
3587 void
history_changed()3588 Editor::history_changed ()
3589 {
3590 string label;
3591
3592 if (undo_action && _session) {
3593 if (_session->undo_depth() == 0) {
3594 label = S_("Command|Undo");
3595 } else {
3596 label = string_compose(S_("Command|Undo (%1)"), _session->next_undo());
3597 }
3598 undo_action->property_label() = label;
3599 }
3600
3601 if (redo_action && _session) {
3602 if (_session->redo_depth() == 0) {
3603 label = _("Redo");
3604 redo_action->set_sensitive (false);
3605 } else {
3606 label = string_compose(_("Redo (%1)"), _session->next_redo());
3607 redo_action->set_sensitive (true);
3608 }
3609 redo_action->property_label() = label;
3610 }
3611 }
3612
3613 void
duplicate_range(bool with_dialog)3614 Editor::duplicate_range (bool with_dialog)
3615 {
3616 float times = 1.0f;
3617
3618 RegionSelection rs = get_regions_from_selection_and_entered ();
3619
3620 if (selection->time.length() == 0 && rs.empty()) {
3621 return;
3622 }
3623
3624 if (with_dialog) {
3625
3626 ArdourDialog win (_("Duplicate"));
3627 Label label (_("Number of duplications:"));
3628 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3629 SpinButton spinner (adjustment, 0.0, 1);
3630 HBox hbox;
3631
3632 win.get_vbox()->set_spacing (12);
3633 win.get_vbox()->pack_start (hbox);
3634 hbox.set_border_width (6);
3635 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3636
3637 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3638 place, visually. so do this by hand.
3639 */
3640
3641 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3642 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3643 spinner.grab_focus();
3644
3645 hbox.show ();
3646 label.show ();
3647 spinner.show ();
3648
3649 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3650 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3651 win.set_default_response (RESPONSE_ACCEPT);
3652
3653 spinner.grab_focus ();
3654
3655 switch (win.run ()) {
3656 case RESPONSE_ACCEPT:
3657 break;
3658 default:
3659 return;
3660 }
3661
3662 times = adjustment.get_value();
3663 }
3664
3665 if ((current_mouse_mode() == MouseRange)) {
3666 if (selection->time.length()) {
3667 duplicate_selection (times);
3668 }
3669 } else if (get_smart_mode()) {
3670 if (selection->time.length()) {
3671 duplicate_selection (times);
3672 } else
3673 duplicate_some_regions (rs, times);
3674 } else {
3675 duplicate_some_regions (rs, times);
3676 }
3677 }
3678
3679 void
set_edit_mode(EditMode m)3680 Editor::set_edit_mode (EditMode m)
3681 {
3682 Config->set_edit_mode (m);
3683 }
3684
3685 void
cycle_edit_mode()3686 Editor::cycle_edit_mode ()
3687 {
3688 switch (Config->get_edit_mode()) {
3689 case Slide:
3690 Config->set_edit_mode (Ripple);
3691 break;
3692 case Splice:
3693 case Ripple:
3694 Config->set_edit_mode (Lock);
3695 break;
3696 case Lock:
3697 Config->set_edit_mode (Slide);
3698 break;
3699 }
3700 }
3701
3702 void
edit_mode_selection_done(EditMode m)3703 Editor::edit_mode_selection_done (EditMode m)
3704 {
3705 Config->set_edit_mode (m);
3706 }
3707
3708 void
grid_type_selection_done(GridType gridtype)3709 Editor::grid_type_selection_done (GridType gridtype)
3710 {
3711 RefPtr<RadioAction> ract = grid_type_action (gridtype);
3712 if (ract) {
3713 ract->set_active ();
3714 }
3715 }
3716
3717 void
snap_mode_selection_done(SnapMode mode)3718 Editor::snap_mode_selection_done (SnapMode mode)
3719 {
3720 RefPtr<RadioAction> ract = snap_mode_action (mode);
3721
3722 if (ract) {
3723 ract->set_active (true);
3724 }
3725 }
3726
3727 void
cycle_edit_point(bool with_marker)3728 Editor::cycle_edit_point (bool with_marker)
3729 {
3730 if(Profile->get_mixbus())
3731 with_marker = false;
3732
3733 switch (_edit_point) {
3734 case EditAtMouse:
3735 set_edit_point_preference (EditAtPlayhead);
3736 break;
3737 case EditAtPlayhead:
3738 if (with_marker) {
3739 set_edit_point_preference (EditAtSelectedMarker);
3740 } else {
3741 set_edit_point_preference (EditAtMouse);
3742 }
3743 break;
3744 case EditAtSelectedMarker:
3745 set_edit_point_preference (EditAtMouse);
3746 break;
3747 }
3748 }
3749
3750 void
edit_point_selection_done(EditPoint ep)3751 Editor::edit_point_selection_done (EditPoint ep)
3752 {
3753 set_edit_point_preference (ep);
3754 }
3755
3756 void
build_zoom_focus_menu()3757 Editor::build_zoom_focus_menu ()
3758 {
3759 using namespace Menu_Helpers;
3760
3761 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusLeft], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusLeft)));
3762 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusRight], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusRight)));
3763 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusCenter], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusCenter)));
3764 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusPlayhead], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusPlayhead)));
3765 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusMouse], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusMouse)));
3766 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusEdit], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusEdit)));
3767
3768 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_TRIANGLE_WIDTH, 2);
3769 }
3770
3771 void
zoom_focus_selection_done(ZoomFocus f)3772 Editor::zoom_focus_selection_done (ZoomFocus f)
3773 {
3774 RefPtr<RadioAction> ract = zoom_focus_action (f);
3775 if (ract) {
3776 ract->set_active ();
3777 }
3778 }
3779
3780 void
build_track_count_menu()3781 Editor::build_track_count_menu ()
3782 {
3783 using namespace Menu_Helpers;
3784
3785 if (!Profile->get_mixbus()) {
3786 visible_tracks_selector.AddMenuElem (MenuElem (X_("1"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 1)));
3787 visible_tracks_selector.AddMenuElem (MenuElem (X_("2"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 2)));
3788 visible_tracks_selector.AddMenuElem (MenuElem (X_("3"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 3)));
3789 visible_tracks_selector.AddMenuElem (MenuElem (X_("4"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 4)));
3790 visible_tracks_selector.AddMenuElem (MenuElem (X_("8"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 8)));
3791 visible_tracks_selector.AddMenuElem (MenuElem (X_("12"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 12)));
3792 visible_tracks_selector.AddMenuElem (MenuElem (X_("16"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 16)));
3793 visible_tracks_selector.AddMenuElem (MenuElem (X_("20"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 20)));
3794 visible_tracks_selector.AddMenuElem (MenuElem (X_("24"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 24)));
3795 visible_tracks_selector.AddMenuElem (MenuElem (X_("32"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 32)));
3796 visible_tracks_selector.AddMenuElem (MenuElem (X_("64"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 64)));
3797 visible_tracks_selector.AddMenuElem (MenuElem (_("Selection"), sigc::mem_fun(*this, &Editor::fit_selection)));
3798 visible_tracks_selector.AddMenuElem (MenuElem (_("All"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 0)));
3799 } else {
3800 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 1 track"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 1)));
3801 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 2 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 2)));
3802 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 4 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 4)));
3803 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 8 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 8)));
3804 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 16 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 16)));
3805 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 24 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 24)));
3806 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 32 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 32)));
3807 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 48 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 48)));
3808 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit All tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 0)));
3809 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit Selection"), sigc::mem_fun(*this, &Editor::fit_selection)));
3810
3811 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 10 ms"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 10)));
3812 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 100 ms"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 100)));
3813 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 1 sec"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 1 * 1000)));
3814 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 10 sec"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 10 * 1000)));
3815 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 1 min"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 60 * 1000)));
3816 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 10 min"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 10 * 60 * 1000)));
3817 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 1 hour"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 60 * 60 * 1000)));
3818 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 8 hours"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 8 * 60 * 60 * 1000)));
3819 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 24 hours"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 24 * 60 * 60 * 1000)));
3820 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Session"), sigc::mem_fun(*this, &Editor::temporal_zoom_session)));
3821 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Extents"), sigc::mem_fun(*this, &Editor::temporal_zoom_extents)));
3822 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Range/Region Selection"), sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_selection), Horizontal)));
3823 }
3824 }
3825
3826 void
set_zoom_preset(int64_t ms)3827 Editor::set_zoom_preset (int64_t ms)
3828 {
3829 if (ms <= 0) {
3830 temporal_zoom_session();
3831 return;
3832 }
3833
3834 ARDOUR::samplecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
3835 temporal_zoom ((sample_rate * ms / 1000) / _visible_canvas_width);
3836 }
3837
3838 void
set_visible_track_count(int32_t n)3839 Editor::set_visible_track_count (int32_t n)
3840 {
3841 _visible_track_count = n;
3842
3843 /* if the canvas hasn't really been allocated any size yet, just
3844 record the desired number of visible tracks and return. when canvas
3845 allocation happens, we will get called again and then we can do the
3846 real work.
3847 */
3848
3849 if (_visible_canvas_height <= 1) {
3850 return;
3851 }
3852
3853 int h;
3854 string str;
3855 DisplaySuspender ds;
3856
3857 if (_visible_track_count > 0) {
3858 h = trackviews_height() / _visible_track_count;
3859 std::ostringstream s;
3860 s << _visible_track_count;
3861 str = s.str();
3862 } else if (_visible_track_count == 0) {
3863 uint32_t n = 0;
3864 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
3865 if ((*i)->marked_for_display()) {
3866 ++n;
3867 TimeAxisView::Children cl ((*i)->get_child_list ());
3868 for (TimeAxisView::Children::const_iterator j = cl.begin(); j != cl.end(); ++j) {
3869 if ((*j)->marked_for_display()) {
3870 ++n;
3871 }
3872 }
3873 }
3874 }
3875 if (n == 0) {
3876 visible_tracks_selector.set_text (X_("*"));
3877 return;
3878 }
3879 h = trackviews_height() / n;
3880 str = _("All");
3881 } else {
3882 /* negative value means that the visible track count has
3883 been overridden by explicit track height changes.
3884 */
3885 visible_tracks_selector.set_text (X_("*"));
3886 return;
3887 }
3888
3889 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3890 (*i)->set_height (h, TimeAxisView::HeightPerLane);
3891 }
3892
3893 if (str != visible_tracks_selector.get_text()) {
3894 visible_tracks_selector.set_text (str);
3895 }
3896 }
3897
3898 void
override_visible_track_count()3899 Editor::override_visible_track_count ()
3900 {
3901 _visible_track_count = -1;
3902 visible_tracks_selector.set_text (_("*"));
3903 }
3904
3905 bool
edit_controls_button_event(GdkEventButton * ev)3906 Editor::edit_controls_button_event (GdkEventButton* ev)
3907 {
3908 if ((ev->type == GDK_2BUTTON_PRESS && ev->button == 1) || (ev->type == GDK_BUTTON_RELEASE && Keyboard::is_context_menu_event (ev))) {
3909 ARDOUR_UI::instance()->add_route ();
3910 } else if (ev->button == 1 && ev->type == GDK_BUTTON_PRESS) {
3911 selection->clear_tracks ();
3912 }
3913 return true;
3914 }
3915
3916 bool
mouse_select_button_release(GdkEventButton * ev)3917 Editor::mouse_select_button_release (GdkEventButton* ev)
3918 {
3919 /* this handles just right-clicks */
3920
3921 if (ev->button != 3) {
3922 return false;
3923 }
3924
3925 return true;
3926 }
3927
3928 void
set_zoom_focus(ZoomFocus f)3929 Editor::set_zoom_focus (ZoomFocus f)
3930 {
3931 string str = zoom_focus_strings[(int)f];
3932
3933 if (str != zoom_focus_selector.get_text()) {
3934 zoom_focus_selector.set_text (str);
3935 }
3936
3937 if (zoom_focus != f) {
3938 zoom_focus = f;
3939 instant_save ();
3940 }
3941 }
3942
3943 void
cycle_zoom_focus()3944 Editor::cycle_zoom_focus ()
3945 {
3946 switch (zoom_focus) {
3947 case ZoomFocusLeft:
3948 set_zoom_focus (ZoomFocusRight);
3949 break;
3950 case ZoomFocusRight:
3951 set_zoom_focus (ZoomFocusCenter);
3952 break;
3953 case ZoomFocusCenter:
3954 set_zoom_focus (ZoomFocusPlayhead);
3955 break;
3956 case ZoomFocusPlayhead:
3957 set_zoom_focus (ZoomFocusMouse);
3958 break;
3959 case ZoomFocusMouse:
3960 set_zoom_focus (ZoomFocusEdit);
3961 break;
3962 case ZoomFocusEdit:
3963 set_zoom_focus (ZoomFocusLeft);
3964 break;
3965 }
3966 }
3967
3968 void
update_grid()3969 Editor::update_grid ()
3970 {
3971 if (_grid_type == GridTypeNone) {
3972 hide_grid_lines ();
3973 } else if (grid_musical()) {
3974 std::vector<TempoMap::BBTPoint> grid;
3975 if (bbt_ruler_scale != bbt_show_many) {
3976 compute_current_bbt_points (grid, _leftmost_sample, _leftmost_sample + current_page_samples());
3977 }
3978 maybe_draw_grid_lines ();
3979 } else {
3980 maybe_draw_grid_lines ();
3981 }
3982 }
3983
3984 void
toggle_follow_playhead()3985 Editor::toggle_follow_playhead ()
3986 {
3987 RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Editor"), X_("toggle-follow-playhead"));
3988 set_follow_playhead (tact->get_active());
3989 }
3990
3991 /** @param yn true to follow playhead, otherwise false.
3992 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3993 */
3994 void
set_follow_playhead(bool yn,bool catch_up)3995 Editor::set_follow_playhead (bool yn, bool catch_up)
3996 {
3997 if (_follow_playhead != yn) {
3998 if ((_follow_playhead = yn) == true && catch_up) {
3999 /* catch up */
4000 reset_x_origin_to_follow_playhead ();
4001 }
4002 instant_save ();
4003 }
4004 }
4005
4006 void
toggle_stationary_playhead()4007 Editor::toggle_stationary_playhead ()
4008 {
4009 RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Editor"), X_("toggle-stationary-playhead"));
4010 set_stationary_playhead (tact->get_active());
4011 }
4012
4013 void
set_stationary_playhead(bool yn)4014 Editor::set_stationary_playhead (bool yn)
4015 {
4016 if (_stationary_playhead != yn) {
4017 if ((_stationary_playhead = yn) == true) {
4018 /* catch up -- FIXME need a 3.0 equivalent of this 2.X call */
4019 // update_current_screen ();
4020 }
4021 instant_save ();
4022 }
4023 }
4024
4025 bool
show_touched_automation() const4026 Editor::show_touched_automation () const
4027 {
4028 if (!contents().is_mapped()) {
4029 return false;
4030 }
4031 return _show_touched_automation;
4032 }
4033
4034 void
toggle_show_touched_automation()4035 Editor::toggle_show_touched_automation ()
4036 {
4037 RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Editor"), X_("show-touched-automation"));
4038 set_show_touched_automation (tact->get_active());
4039 }
4040
4041 void
set_show_touched_automation(bool yn)4042 Editor::set_show_touched_automation (bool yn)
4043 {
4044 if (_show_touched_automation == yn) {
4045 return;
4046 }
4047 _show_touched_automation = yn;
4048 if (!yn) {
4049 RouteTimeAxisView::signal_ctrl_touched (true);
4050 }
4051 instant_save ();
4052 }
4053
4054 samplecnt_t
get_paste_offset(samplepos_t pos,unsigned paste_count,samplecnt_t duration)4055 Editor::get_paste_offset (samplepos_t pos, unsigned paste_count, samplecnt_t duration)
4056 {
4057 if (paste_count == 0) {
4058 /* don't bother calculating an offset that will be zero anyway */
4059 return 0;
4060 }
4061
4062 /* calculate basic unsnapped multi-paste offset */
4063 samplecnt_t offset = paste_count * duration;
4064
4065 /* snap offset so pos + offset is aligned to the grid */
4066 MusicSample offset_pos (pos + offset, 0);
4067 snap_to(offset_pos, RoundUpMaybe);
4068 offset = offset_pos.sample - pos;
4069
4070 return offset;
4071 }
4072
4073 unsigned
get_grid_beat_divisions(samplepos_t position)4074 Editor::get_grid_beat_divisions(samplepos_t position)
4075 {
4076 switch (_grid_type) {
4077 case GridTypeBeatDiv32: return 32;
4078 case GridTypeBeatDiv28: return 28;
4079 case GridTypeBeatDiv24: return 24;
4080 case GridTypeBeatDiv20: return 20;
4081 case GridTypeBeatDiv16: return 16;
4082 case GridTypeBeatDiv14: return 14;
4083 case GridTypeBeatDiv12: return 12;
4084 case GridTypeBeatDiv10: return 10;
4085 case GridTypeBeatDiv8: return 8;
4086 case GridTypeBeatDiv7: return 7;
4087 case GridTypeBeatDiv6: return 6;
4088 case GridTypeBeatDiv5: return 5;
4089 case GridTypeBeatDiv4: return 4;
4090 case GridTypeBeatDiv3: return 3;
4091 case GridTypeBeatDiv2: return 2;
4092 case GridTypeBeat: return 1;
4093 case GridTypeBar: return 1;
4094
4095 case GridTypeNone: return 0;
4096 case GridTypeTimecode: return 0;
4097 case GridTypeMinSec: return 0;
4098 case GridTypeCDFrame: return 0;
4099 default: return 0;
4100 }
4101 return 0;
4102 }
4103
4104 /** returns the current musical grid divisiions using the supplied modifier mask from a GtkEvent.
4105 if the grid is non-musical, returns 0.
4106 if the grid is snapped to bars, returns -1.
4107 @param event_state the current keyboard modifier mask.
4108 */
4109 int32_t
get_grid_music_divisions(uint32_t event_state)4110 Editor::get_grid_music_divisions (uint32_t event_state)
4111 {
4112 if (snap_mode() == SnapOff && !ArdourKeyboard::indicates_snap (event_state)) {
4113 return 0;
4114 }
4115
4116 if (snap_mode() != SnapOff && ArdourKeyboard::indicates_snap (event_state)) {
4117 return 0;
4118 }
4119
4120 switch (_grid_type) {
4121 case GridTypeBeatDiv32: return 32;
4122 case GridTypeBeatDiv28: return 28;
4123 case GridTypeBeatDiv24: return 24;
4124 case GridTypeBeatDiv20: return 20;
4125 case GridTypeBeatDiv16: return 16;
4126 case GridTypeBeatDiv14: return 14;
4127 case GridTypeBeatDiv12: return 12;
4128 case GridTypeBeatDiv10: return 10;
4129 case GridTypeBeatDiv8: return 8;
4130 case GridTypeBeatDiv7: return 7;
4131 case GridTypeBeatDiv6: return 6;
4132 case GridTypeBeatDiv5: return 5;
4133 case GridTypeBeatDiv4: return 4;
4134 case GridTypeBeatDiv3: return 3;
4135 case GridTypeBeatDiv2: return 2;
4136 case GridTypeBeat: return 1;
4137 case GridTypeBar : return -1;
4138
4139 case GridTypeNone: return 0;
4140 case GridTypeTimecode: return 0;
4141 case GridTypeMinSec: return 0;
4142 case GridTypeCDFrame: return 0;
4143 }
4144 return 0;
4145 }
4146
4147 Temporal::Beats
get_grid_type_as_beats(bool & success,samplepos_t position)4148 Editor::get_grid_type_as_beats (bool& success, samplepos_t position)
4149 {
4150 success = true;
4151
4152 const unsigned divisions = get_grid_beat_divisions(position);
4153 if (divisions) {
4154 return Temporal::Beats(1.0 / (double)get_grid_beat_divisions(position));
4155 }
4156
4157 switch (_grid_type) {
4158 case GridTypeBeat:
4159 return Temporal::Beats(4.0 / _session->tempo_map().meter_at_sample (position).note_divisor());
4160 case GridTypeBar:
4161 if (_session) {
4162 const Meter& m = _session->tempo_map().meter_at_sample (position);
4163 return Temporal::Beats((4.0 * m.divisions_per_bar()) / m.note_divisor());
4164 }
4165 break;
4166 default:
4167 success = false;
4168 break;
4169 }
4170
4171 return Temporal::Beats();
4172 }
4173
4174 samplecnt_t
get_nudge_distance(samplepos_t pos,samplecnt_t & next)4175 Editor::get_nudge_distance (samplepos_t pos, samplecnt_t& next)
4176 {
4177 samplecnt_t ret;
4178
4179 ret = nudge_clock->current_duration (pos);
4180 next = ret + 1; /* XXXX fix me */
4181
4182 return ret;
4183 }
4184
4185 int
playlist_deletion_dialog(boost::shared_ptr<Playlist> pl)4186 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
4187 {
4188 ArdourDialog dialog (_("Playlist Deletion"));
4189 Label label (string_compose (_("Playlist %1 is currently unused.\n"
4190 "If it is kept, its audio files will not be cleaned.\n"
4191 "If it is deleted, audio files used by it alone will be cleaned."),
4192 pl->name()));
4193
4194 dialog.set_position (WIN_POS_CENTER);
4195 dialog.get_vbox()->pack_start (label);
4196
4197 label.show ();
4198
4199 dialog.add_button (_("Delete All Unused"), RESPONSE_YES); // needs clarification. this and all remaining ones
4200 dialog.add_button (_("Delete Playlist"), RESPONSE_ACCEPT);
4201 Button* keep = dialog.add_button (_("Keep Playlist"), RESPONSE_REJECT);
4202 dialog.add_button (_("Keep Remaining"), RESPONSE_NO); // ditto
4203 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
4204
4205 /* by default gtk uses the left most button */
4206 keep->grab_focus ();
4207
4208 switch (dialog.run ()) {
4209 case RESPONSE_NO:
4210 /* keep this and all remaining ones */
4211 return -2;
4212 break;
4213
4214 case RESPONSE_YES:
4215 /* delete this and all others */
4216 return 2;
4217 break;
4218
4219 case RESPONSE_ACCEPT:
4220 /* delete the playlist */
4221 return 1;
4222 break;
4223
4224 case RESPONSE_REJECT:
4225 /* keep the playlist */
4226 return 0;
4227 break;
4228
4229 default:
4230 break;
4231 }
4232
4233 return -1;
4234 }
4235
4236 bool
audio_region_selection_covers(samplepos_t where)4237 Editor::audio_region_selection_covers (samplepos_t where)
4238 {
4239 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
4240 if ((*a)->region()->covers (where)) {
4241 return true;
4242 }
4243 }
4244
4245 return false;
4246 }
4247
4248 void
cleanup_regions()4249 Editor::cleanup_regions ()
4250 {
4251 _regions->remove_unused_regions();
4252 }
4253
4254
4255 void
prepare_for_cleanup()4256 Editor::prepare_for_cleanup ()
4257 {
4258 cut_buffer->clear_regions ();
4259 cut_buffer->clear_playlists ();
4260
4261 selection->clear_regions ();
4262 selection->clear_playlists ();
4263
4264 _regions->suspend_redisplay ();
4265 }
4266
4267 void
finish_cleanup()4268 Editor::finish_cleanup ()
4269 {
4270 _regions->resume_redisplay ();
4271 }
4272
4273 Location*
transport_loop_location()4274 Editor::transport_loop_location()
4275 {
4276 if (_session) {
4277 return _session->locations()->auto_loop_location();
4278 } else {
4279 return 0;
4280 }
4281 }
4282
4283 Location*
transport_punch_location()4284 Editor::transport_punch_location()
4285 {
4286 if (_session) {
4287 return _session->locations()->auto_punch_location();
4288 } else {
4289 return 0;
4290 }
4291 }
4292
4293 bool
control_layout_scroll(GdkEventScroll * ev)4294 Editor::control_layout_scroll (GdkEventScroll* ev)
4295 {
4296 /* Just forward to the normal canvas scroll method. The coordinate
4297 systems are different but since the canvas is always larger than the
4298 track headers, and aligned with the trackview area, this will work.
4299
4300 In the not too distant future this layout is going away anyway and
4301 headers will be on the canvas.
4302 */
4303 return canvas_scroll_event (ev, false);
4304 }
4305
4306 void
session_state_saved(string)4307 Editor::session_state_saved (string)
4308 {
4309 update_title ();
4310 _snapshots->redisplay ();
4311 }
4312
4313 void
maximise_editing_space()4314 Editor::maximise_editing_space ()
4315 {
4316 if (_maximised) {
4317 return;
4318 }
4319
4320 Gtk::Window* toplevel = current_toplevel();
4321
4322 if (toplevel) {
4323 toplevel->fullscreen ();
4324 _maximised = true;
4325 }
4326 }
4327
4328 void
restore_editing_space()4329 Editor::restore_editing_space ()
4330 {
4331 if (!_maximised) {
4332 return;
4333 }
4334
4335 Gtk::Window* toplevel = current_toplevel();
4336
4337 if (toplevel) {
4338 toplevel->unfullscreen();
4339 _maximised = false;
4340 }
4341 }
4342
4343 bool
stamp_new_playlist(string & name,string & pgroup,bool copy)4344 Editor::stamp_new_playlist (string &name, string &pgroup, bool copy)
4345 {
4346 time_t now;
4347 time (&now);
4348 Glib::DateTime tm (Glib::DateTime::create_now_local (now));
4349 string gid (tm.format ("%F %H.%M.%S"));
4350 pgroup = gid;
4351
4352 if (name.length()==0) {
4353 name = _("Take.1");
4354 if (_session->playlists()->by_name (name)) {
4355 name = Playlist::bump_name (name, *_session);
4356 }
4357 }
4358
4359 Prompter prompter (true);
4360 if (copy) {
4361 prompter.set_title (_("Copy Playlist(s)"));
4362 } else {
4363 prompter.set_title (_("New (Empty) Playlist(s)"));
4364 }
4365 prompter.set_prompt (_("Name for new playlist:"));
4366 prompter.set_initial_text (name);
4367 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
4368 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
4369 prompter.show_all ();
4370
4371 while (true) {
4372 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
4373 return false;
4374 }
4375 prompter.get_result (name);
4376 if (name.length()) {
4377 if (_session->playlists()->by_name (name)) {
4378 prompter.set_prompt (_("That name is already in use. Use this instead?"));
4379 prompter.set_initial_text (Playlist::bump_name (name, *_session));
4380 } else {
4381 break;
4382 }
4383 }
4384 }
4385
4386 return true;
4387 }
4388
4389 /** Clear the current playlist for a given track and also any others that belong
4390 * to the same active route group with the `select' property.
4391 * @param v Track.
4392 */
4393
4394 void
clear_grouped_playlists(RouteUI * rui)4395 Editor::clear_grouped_playlists (RouteUI* rui)
4396 {
4397 begin_reversible_command (_("clear playlists"));
4398 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4399 _session->playlists()->get (playlists);
4400 mapover_grouped_routes (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), rui, ARDOUR::Properties::group_select.property_id);
4401 commit_reversible_command ();
4402 }
4403
4404 void
mapped_use_new_playlist(RouteUI & rui,std::string name,string gid,bool copy,vector<boost::shared_ptr<ARDOUR::Playlist>> const & playlists)4405 Editor::mapped_use_new_playlist (RouteUI& rui, std::string name, string gid, bool copy, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4406 {
4407 rui.use_new_playlist (name, gid, playlists, copy);
4408 }
4409
4410 void
mapped_clear_playlist(RouteUI & rui)4411 Editor::mapped_clear_playlist (RouteUI& rui)
4412 {
4413 rui.clear_playlist ();
4414 }
4415
4416 void
new_playlists_for_all_tracks(bool copy)4417 Editor::new_playlists_for_all_tracks (bool copy)
4418 {
4419 string name, gid;
4420 stamp_new_playlist(name,gid,copy);
4421
4422 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4423 _session->playlists()->get (playlists);
4424 mapover_all_routes (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), name, gid, copy, playlists));
4425 }
4426
4427 void
new_playlists_for_grouped_tracks(RouteUI * rui,bool copy)4428 Editor::new_playlists_for_grouped_tracks (RouteUI* rui, bool copy)
4429 {
4430 string name, gid;
4431 stamp_new_playlist(name,gid,copy);
4432
4433 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4434 _session->playlists()->get (playlists);
4435 mapover_grouped_routes (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), name, gid, copy, playlists), rui, ARDOUR::Properties::group_select.property_id);
4436 }
4437
4438 void
new_playlists_for_selected_tracks(bool copy)4439 Editor::new_playlists_for_selected_tracks (bool copy)
4440 {
4441 string name, gid;
4442 stamp_new_playlist(name,gid,copy);
4443
4444 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4445 _session->playlists()->get (playlists);
4446 mapover_selected_routes (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), name, gid, copy, playlists));
4447 }
4448
4449 void
new_playlists_for_armed_tracks(bool copy)4450 Editor::new_playlists_for_armed_tracks (bool copy)
4451 {
4452 string name, gid;
4453 stamp_new_playlist(name,gid,copy);
4454
4455 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4456 _session->playlists()->get (playlists);
4457 mapover_armed_routes (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), name, gid, copy, playlists));
4458 }
4459
4460 double
get_y_origin() const4461 Editor::get_y_origin () const
4462 {
4463 return vertical_adjustment.get_value ();
4464 }
4465
4466 /** Queue up a change to the viewport x origin.
4467 * @param sample New x origin.
4468 */
4469 void
reset_x_origin(samplepos_t sample)4470 Editor::reset_x_origin (samplepos_t sample)
4471 {
4472 pending_visual_change.add (VisualChange::TimeOrigin);
4473 pending_visual_change.time_origin = sample;
4474 ensure_visual_change_idle_handler ();
4475 }
4476
4477 void
reset_y_origin(double y)4478 Editor::reset_y_origin (double y)
4479 {
4480 pending_visual_change.add (VisualChange::YOrigin);
4481 pending_visual_change.y_origin = y;
4482 ensure_visual_change_idle_handler ();
4483 }
4484
4485 void
reset_zoom(samplecnt_t spp)4486 Editor::reset_zoom (samplecnt_t spp)
4487 {
4488 if (spp == samples_per_pixel) {
4489 return;
4490 }
4491
4492 pending_visual_change.add (VisualChange::ZoomLevel);
4493 pending_visual_change.samples_per_pixel = spp;
4494 ensure_visual_change_idle_handler ();
4495 }
4496
4497 void
reposition_and_zoom(samplepos_t sample,double fpu)4498 Editor::reposition_and_zoom (samplepos_t sample, double fpu)
4499 {
4500 reset_x_origin (sample);
4501 reset_zoom (fpu);
4502
4503 if (!no_save_visual) {
4504 undo_visual_stack.push_back (current_visual_state(false));
4505 }
4506 }
4507
VisualState(bool with_tracks)4508 Editor::VisualState::VisualState (bool with_tracks)
4509 : gui_state (with_tracks ? new GUIObjectState : 0)
4510 {
4511 }
4512
~VisualState()4513 Editor::VisualState::~VisualState ()
4514 {
4515 delete gui_state;
4516 }
4517
4518 Editor::VisualState*
current_visual_state(bool with_tracks)4519 Editor::current_visual_state (bool with_tracks)
4520 {
4521 VisualState* vs = new VisualState (with_tracks);
4522 vs->y_position = vertical_adjustment.get_value();
4523 vs->samples_per_pixel = samples_per_pixel;
4524 vs->_leftmost_sample = _leftmost_sample;
4525 vs->zoom_focus = zoom_focus;
4526
4527 if (with_tracks) {
4528 vs->gui_state->set_state (ARDOUR_UI::instance()->gui_object_state->get_state());
4529 }
4530
4531 return vs;
4532 }
4533
4534 void
undo_visual_state()4535 Editor::undo_visual_state ()
4536 {
4537 if (undo_visual_stack.empty()) {
4538 return;
4539 }
4540
4541 VisualState* vs = undo_visual_stack.back();
4542 undo_visual_stack.pop_back();
4543
4544
4545 redo_visual_stack.push_back (current_visual_state (vs ? vs->gui_state != 0 : false));
4546
4547 if (vs) {
4548 use_visual_state (*vs);
4549 }
4550 }
4551
4552 void
redo_visual_state()4553 Editor::redo_visual_state ()
4554 {
4555 if (redo_visual_stack.empty()) {
4556 return;
4557 }
4558
4559 VisualState* vs = redo_visual_stack.back();
4560 redo_visual_stack.pop_back();
4561
4562 /* XXX: can 'vs' really be 0? Is there a place that puts NULL pointers onto the stack? */
4563 undo_visual_stack.push_back (current_visual_state (vs ? (vs->gui_state != 0) : false));
4564
4565 if (vs) {
4566 use_visual_state (*vs);
4567 }
4568 }
4569
4570 void
swap_visual_state()4571 Editor::swap_visual_state ()
4572 {
4573 if (undo_visual_stack.empty()) {
4574 redo_visual_state ();
4575 } else {
4576 undo_visual_state ();
4577 }
4578 }
4579
4580 void
use_visual_state(VisualState & vs)4581 Editor::use_visual_state (VisualState& vs)
4582 {
4583 PBD::Unwinder<bool> nsv (no_save_visual, true);
4584 DisplaySuspender ds;
4585
4586 vertical_adjustment.set_value (vs.y_position);
4587
4588 set_zoom_focus (vs.zoom_focus);
4589 reposition_and_zoom (vs._leftmost_sample, vs.samples_per_pixel);
4590
4591 if (vs.gui_state) {
4592 ARDOUR_UI::instance()->gui_object_state->set_state (vs.gui_state->get_state());
4593
4594 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4595 (*i)->clear_property_cache();
4596 (*i)->reset_visual_state ();
4597 }
4598 }
4599
4600 _routes->update_visibility ();
4601 }
4602
4603 /** This is the core function that controls the zoom level of the canvas. It is called
4604 * whenever one or more calls are made to reset_zoom(). It executes in an idle handler.
4605 * @param spp new number of samples per pixel
4606 */
4607 void
set_samples_per_pixel(samplecnt_t spp)4608 Editor::set_samples_per_pixel (samplecnt_t spp)
4609 {
4610 if (spp < 1) {
4611 return;
4612 }
4613
4614 const samplecnt_t three_days = 3 * 24 * 60 * 60 * (_session ? _session->sample_rate() : 48000);
4615 const samplecnt_t lots_of_pixels = 4000;
4616
4617 /* if the zoom level is greater than what you'd get trying to display 3
4618 * days of audio on a really big screen, then it's too big.
4619 */
4620
4621 if (spp * lots_of_pixels > three_days) {
4622 return;
4623 }
4624
4625 samples_per_pixel = spp;
4626 }
4627
4628 void
on_samples_per_pixel_changed()4629 Editor::on_samples_per_pixel_changed ()
4630 {
4631 bool const showing_time_selection = selection->time.length() > 0;
4632
4633 if (showing_time_selection && selection->time.start () != selection->time.end_sample ()) {
4634 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4635 (*i)->reshow_selection (selection->time);
4636 }
4637 }
4638
4639 ZoomChanged (); /* EMIT_SIGNAL */
4640
4641 ArdourCanvas::GtkCanvasViewport* c;
4642
4643 c = get_track_canvas();
4644 if (c) {
4645 c->canvas()->zoomed ();
4646 }
4647
4648 if (_playhead_cursor) {
4649 _playhead_cursor->set_position (_playhead_cursor->current_sample ());
4650 }
4651
4652 refresh_location_display();
4653 _summary->set_overlays_dirty ();
4654
4655 update_marker_labels ();
4656
4657 instant_save ();
4658 }
4659
4660 samplepos_t
playhead_cursor_sample() const4661 Editor::playhead_cursor_sample () const
4662 {
4663 return _playhead_cursor->current_sample();
4664 }
4665
4666 void
queue_visual_videotimeline_update()4667 Editor::queue_visual_videotimeline_update ()
4668 {
4669 pending_visual_change.add (VisualChange::VideoTimeline);
4670 ensure_visual_change_idle_handler ();
4671 }
4672
4673 void
ensure_visual_change_idle_handler()4674 Editor::ensure_visual_change_idle_handler ()
4675 {
4676 if (pending_visual_change.idle_handler_id < 0) {
4677 /* see comment in add_to_idle_resize above. */
4678 pending_visual_change.idle_handler_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, _idle_visual_changer, this, NULL);
4679 pending_visual_change.being_handled = false;
4680 }
4681 }
4682
4683 int
_idle_visual_changer(void * arg)4684 Editor::_idle_visual_changer (void* arg)
4685 {
4686 return static_cast<Editor*>(arg)->idle_visual_changer ();
4687 }
4688
4689 void
pre_render()4690 Editor::pre_render ()
4691 {
4692 visual_change_queued = false;
4693
4694 if (pending_visual_change.pending != 0) {
4695 ensure_visual_change_idle_handler();
4696 }
4697 }
4698
4699 int
idle_visual_changer()4700 Editor::idle_visual_changer ()
4701 {
4702 pending_visual_change.idle_handler_id = -1;
4703
4704 if (pending_visual_change.pending == 0) {
4705 return 0;
4706 }
4707
4708 /* set_horizontal_position() below (and maybe other calls) call
4709 gtk_main_iteration(), so it's possible that a signal will be handled
4710 half-way through this method. If this signal wants an
4711 idle_visual_changer we must schedule another one after this one, so
4712 mark the idle_handler_id as -1 here to allow that. Also make a note
4713 that we are doing the visual change, so that changes in response to
4714 super-rapid-screen-update can be dropped if we are still processing
4715 the last one.
4716 */
4717
4718 if (visual_change_queued) {
4719 return 0;
4720 }
4721
4722 pending_visual_change.being_handled = true;
4723
4724 VisualChange vc = pending_visual_change;
4725
4726 pending_visual_change.pending = (VisualChange::Type) 0;
4727
4728 visual_changer (vc);
4729
4730 pending_visual_change.being_handled = false;
4731
4732 visual_change_queued = true;
4733
4734 return 0; /* this is always a one-shot call */
4735 }
4736
4737 void
visual_changer(const VisualChange & vc)4738 Editor::visual_changer (const VisualChange& vc)
4739 {
4740 /**
4741 * Changed first so the correct horizontal canvas position is calculated in
4742 * Editor::set_horizontal_position
4743 */
4744 if (vc.pending & VisualChange::ZoomLevel) {
4745 set_samples_per_pixel (vc.samples_per_pixel);
4746 }
4747
4748 if (vc.pending & VisualChange::TimeOrigin) {
4749 double new_time_origin = sample_to_pixel_unrounded (vc.time_origin);
4750 set_horizontal_position (new_time_origin);
4751 }
4752
4753 if (vc.pending & VisualChange::YOrigin) {
4754 vertical_adjustment.set_value (vc.y_origin);
4755 }
4756
4757 /**
4758 * Now the canvas is in the final state before render the canvas items that
4759 * support the Item::prepare_for_render interface can calculate the correct
4760 * item to visible canvas intersection.
4761 */
4762 if (vc.pending & VisualChange::ZoomLevel) {
4763 on_samples_per_pixel_changed ();
4764
4765 compute_fixed_ruler_scale ();
4766
4767 compute_bbt_ruler_scale (vc.time_origin, pending_visual_change.time_origin + current_page_samples());
4768 update_tempo_based_rulers ();
4769 }
4770
4771 if (!(vc.pending & VisualChange::ZoomLevel)) {
4772 /* If the canvas is not being zoomed then the canvas items will not change
4773 * and cause Item::prepare_for_render to be called so do it here manually.
4774 * Not ideal, but I can't think of a better solution atm.
4775 */
4776 _track_canvas->prepare_for_render();
4777 }
4778
4779 /* If we are only scrolling vertically there is no need to update these */
4780 if (vc.pending != VisualChange::YOrigin) {
4781 update_fixed_rulers ();
4782 redisplay_grid (true);
4783
4784 /* video frames & position need to be updated for zoom, horiz-scroll
4785 * and (explicitly) VisualChange::VideoTimeline.
4786 */
4787 update_video_timeline();
4788 }
4789
4790 _region_peak_cursor->hide ();
4791 _summary->set_overlays_dirty ();
4792 }
4793
4794 struct EditorOrderTimeAxisSorter {
operator ()EditorOrderTimeAxisSorter4795 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4796 return a->order () < b->order ();
4797 }
4798 };
4799
4800 void
sort_track_selection(TrackViewList & sel)4801 Editor::sort_track_selection (TrackViewList& sel)
4802 {
4803 EditorOrderTimeAxisSorter cmp;
4804 sel.sort (cmp);
4805 }
4806
4807 samplepos_t
get_preferred_edit_position(EditIgnoreOption ignore,bool from_context_menu,bool from_outside_canvas)4808 Editor::get_preferred_edit_position (EditIgnoreOption ignore, bool from_context_menu, bool from_outside_canvas)
4809 {
4810 bool ignored;
4811 samplepos_t where = 0;
4812 EditPoint ep = _edit_point;
4813
4814 if (Profile->get_mixbus()) {
4815 if (ep == EditAtSelectedMarker) {
4816 ep = EditAtPlayhead;
4817 }
4818 }
4819
4820 if (from_outside_canvas && (ep == EditAtMouse)) {
4821 ep = EditAtPlayhead;
4822 } else if (from_context_menu && (ep == EditAtMouse)) {
4823 return canvas_event_sample (&context_click_event, 0, 0);
4824 }
4825
4826 if (entered_marker) {
4827 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4828 return entered_marker->position();
4829 }
4830
4831 if ((ignore == EDIT_IGNORE_PHEAD) && ep == EditAtPlayhead) {
4832 ep = EditAtSelectedMarker;
4833 }
4834
4835 if ((ignore == EDIT_IGNORE_MOUSE) && ep == EditAtMouse) {
4836 ep = EditAtPlayhead;
4837 }
4838
4839 MusicSample snap_mf (0, 0);
4840
4841 switch (ep) {
4842 case EditAtPlayhead:
4843 if (_dragging_playhead) {
4844 /* NOTE: since the user is dragging with the mouse, this operation will implicitly be Snapped */
4845 where = _playhead_cursor->current_sample();
4846 } else {
4847 where = _session->audible_sample();
4848 }
4849 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4850 break;
4851
4852 case EditAtSelectedMarker:
4853 if (!selection->markers.empty()) {
4854 bool is_start;
4855 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4856 if (loc) {
4857 if (is_start) {
4858 where = loc->start();
4859 } else {
4860 where = loc->end();
4861 }
4862 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4863 break;
4864 }
4865 }
4866 /* fallthrough */
4867
4868 default:
4869 case EditAtMouse:
4870 if (!mouse_sample (where, ignored)) {
4871 /* XXX not right but what can we do ? */
4872 return 0;
4873 }
4874 snap_mf.sample = where;
4875 snap_to (snap_mf);
4876 where = snap_mf.sample;
4877 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4878 break;
4879 }
4880
4881 return where;
4882 }
4883
4884 void
set_loop_range(samplepos_t start,samplepos_t end,string cmd)4885 Editor::set_loop_range (samplepos_t start, samplepos_t end, string cmd)
4886 {
4887 if (!_session) return;
4888
4889 begin_reversible_command (cmd);
4890
4891 Location* tll;
4892
4893 if ((tll = transport_loop_location()) == 0) {
4894 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop, get_grid_music_divisions(0));
4895 XMLNode &before = _session->locations()->get_state();
4896 _session->locations()->add (loc, true);
4897 _session->set_auto_loop_location (loc);
4898 XMLNode &after = _session->locations()->get_state();
4899 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4900 } else {
4901 XMLNode &before = tll->get_state();
4902 tll->set_hidden (false, this);
4903 tll->set (start, end);
4904 XMLNode &after = tll->get_state();
4905 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4906 }
4907
4908 commit_reversible_command ();
4909 }
4910
4911 void
set_punch_range(samplepos_t start,samplepos_t end,string cmd)4912 Editor::set_punch_range (samplepos_t start, samplepos_t end, string cmd)
4913 {
4914 if (!_session) return;
4915
4916 begin_reversible_command (cmd);
4917
4918 Location* tpl;
4919
4920 if ((tpl = transport_punch_location()) == 0) {
4921 Location* loc = new Location (*_session, start, end, _("Punch"), Location::IsAutoPunch, get_grid_music_divisions(0));
4922 XMLNode &before = _session->locations()->get_state();
4923 _session->locations()->add (loc, true);
4924 _session->set_auto_punch_location (loc);
4925 XMLNode &after = _session->locations()->get_state();
4926 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4927 } else {
4928 XMLNode &before = tpl->get_state();
4929 tpl->set_hidden (false, this);
4930 tpl->set (start, end);
4931 XMLNode &after = tpl->get_state();
4932 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4933 }
4934
4935 commit_reversible_command ();
4936 }
4937
4938 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4939 * @param rs List to which found regions are added.
4940 * @param where Time to look at.
4941 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4942 */
4943 void
get_regions_at(RegionSelection & rs,samplepos_t where,const TrackViewList & ts) const4944 Editor::get_regions_at (RegionSelection& rs, samplepos_t where, const TrackViewList& ts) const
4945 {
4946 const TrackViewList* tracks;
4947
4948 if (ts.empty()) {
4949 tracks = &track_views;
4950 } else {
4951 tracks = &ts;
4952 }
4953
4954 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4955
4956 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4957
4958 if (rtv) {
4959 boost::shared_ptr<Track> tr;
4960 boost::shared_ptr<Playlist> pl;
4961
4962 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4963
4964 boost::shared_ptr<RegionList> regions = pl->regions_at (where);
4965
4966 for (RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4967 RegionView* rv = rtv->view()->find_view (*i);
4968 if (rv) {
4969 rs.add (rv);
4970 }
4971 }
4972 }
4973 }
4974 }
4975 }
4976
4977 void
get_regions_after(RegionSelection & rs,samplepos_t where,const TrackViewList & ts) const4978 Editor::get_regions_after (RegionSelection& rs, samplepos_t where, const TrackViewList& ts) const
4979 {
4980 const TrackViewList* tracks;
4981
4982 if (ts.empty()) {
4983 tracks = &track_views;
4984 } else {
4985 tracks = &ts;
4986 }
4987
4988 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4989 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4990 if (rtv) {
4991 boost::shared_ptr<Track> tr;
4992 boost::shared_ptr<Playlist> pl;
4993
4994 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4995
4996 boost::shared_ptr<RegionList> regions = pl->regions_touched (where, max_samplepos);
4997
4998 for (RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4999
5000 RegionView* rv = rtv->view()->find_view (*i);
5001
5002 if (rv) {
5003 rs.add (rv);
5004 }
5005 }
5006 }
5007 }
5008 }
5009 }
5010
5011 /** Get regions using the following method:
5012 *
5013 * Make a region list using:
5014 * (a) any selected regions
5015 * (b) the intersection of any selected tracks and the edit point(*)
5016 * (c) if neither exists, and edit_point == mouse, then whatever region is under the mouse
5017 *
5018 * (*) NOTE: in this case, if 'No Selection = All Tracks' is active, search all tracks
5019 *
5020 * Note that we have forced the rule that selected regions and selected tracks are mutually exclusive
5021 */
5022
5023 RegionSelection
get_regions_from_selection_and_edit_point(EditIgnoreOption ignore,bool from_context_menu,bool from_outside_canvas)5024 Editor::get_regions_from_selection_and_edit_point (EditIgnoreOption ignore, bool from_context_menu, bool from_outside_canvas)
5025 {
5026 RegionSelection regions;
5027
5028 if (_edit_point == EditAtMouse && entered_regionview && selection->tracks.empty() && selection->regions.empty()) {
5029 regions.add (entered_regionview);
5030 } else {
5031 regions = selection->regions;
5032 }
5033
5034 if (regions.empty()) {
5035 TrackViewList tracks = selection->tracks;
5036
5037 if (!tracks.empty()) {
5038 /* no region selected or entered, but some selected tracks:
5039 * act on all regions on the selected tracks at the edit point
5040 */
5041 samplepos_t const where = get_preferred_edit_position (ignore, from_context_menu, from_outside_canvas);
5042 get_regions_at(regions, where, tracks);
5043 }
5044 }
5045
5046 return regions;
5047 }
5048
5049 /** Get regions using the following method:
5050 *
5051 * Make a region list using:
5052 * (a) any selected regions
5053 * (b) the intersection of any selected tracks and the edit point(*)
5054 * (c) if neither exists, then whatever region is under the mouse
5055 *
5056 * (*) NOTE: in this case, if 'No Selection = All Tracks' is active, search all tracks
5057 *
5058 * Note that we have forced the rule that selected regions and selected tracks are mutually exclusive
5059 */
5060 RegionSelection
get_regions_from_selection_and_mouse(samplepos_t pos)5061 Editor::get_regions_from_selection_and_mouse (samplepos_t pos)
5062 {
5063 RegionSelection regions;
5064
5065 if (entered_regionview && selection->tracks.empty() && selection->regions.empty()) {
5066 regions.add (entered_regionview);
5067 } else {
5068 regions = selection->regions;
5069 }
5070
5071 if (regions.empty()) {
5072 TrackViewList tracks = selection->tracks;
5073
5074 if (!tracks.empty()) {
5075 /* no region selected or entered, but some selected tracks:
5076 * act on all regions on the selected tracks at the edit point
5077 */
5078 get_regions_at(regions, pos, tracks);
5079 }
5080 }
5081
5082 return regions;
5083 }
5084
5085 /** Start with regions that are selected, or the entered regionview if none are selected.
5086 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
5087 * of the regions that we started with.
5088 */
5089
5090 RegionSelection
get_regions_from_selection_and_entered() const5091 Editor::get_regions_from_selection_and_entered () const
5092 {
5093 RegionSelection regions = selection->regions;
5094
5095 if (regions.empty() && entered_regionview) {
5096 regions.add (entered_regionview);
5097 }
5098
5099 return regions;
5100 }
5101
5102 void
get_regionviews_by_id(PBD::ID const id,RegionSelection & regions) const5103 Editor::get_regionviews_by_id (PBD::ID const id, RegionSelection & regions) const
5104 {
5105 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5106 RouteTimeAxisView* rtav;
5107
5108 if ((rtav = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5109 boost::shared_ptr<Playlist> pl;
5110 std::vector<boost::shared_ptr<Region> > results;
5111 boost::shared_ptr<Track> tr;
5112
5113 if ((tr = rtav->track()) == 0) {
5114 /* bus */
5115 continue;
5116 }
5117
5118 if ((pl = (tr->playlist())) != 0) {
5119 boost::shared_ptr<Region> r = pl->region_by_id (id);
5120 if (r) {
5121 RegionView* rv = rtav->view()->find_view (r);
5122 if (rv) {
5123 regions.push_back (rv);
5124 }
5125 }
5126 }
5127 }
5128 }
5129 }
5130
5131 void
get_per_region_note_selection(list<pair<PBD::ID,set<boost::shared_ptr<Evoral::Note<Temporal::Beats>>>>> & selection) const5132 Editor::get_per_region_note_selection (list<pair<PBD::ID, set<boost::shared_ptr<Evoral::Note<Temporal::Beats> > > > > &selection) const
5133 {
5134
5135 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5136 MidiTimeAxisView* mtav;
5137
5138 if ((mtav = dynamic_cast<MidiTimeAxisView*> (*i)) != 0) {
5139
5140 mtav->get_per_region_note_selection (selection);
5141 }
5142 }
5143
5144 }
5145
5146 void
get_regionview_corresponding_to(boost::shared_ptr<Region> region,vector<RegionView * > & regions)5147 Editor::get_regionview_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
5148 {
5149 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5150
5151 RouteTimeAxisView* tatv;
5152
5153 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5154
5155 boost::shared_ptr<Playlist> pl;
5156 RegionView* marv;
5157 boost::shared_ptr<Track> tr;
5158
5159 if ((tr = tatv->track()) == 0) {
5160 /* bus */
5161 continue;
5162 }
5163
5164 if ((marv = tatv->view()->find_view (region)) != 0) {
5165 regions.push_back (marv);
5166 }
5167 }
5168 }
5169 }
5170
5171 RegionView*
regionview_from_region(boost::shared_ptr<Region> region) const5172 Editor::regionview_from_region (boost::shared_ptr<Region> region) const
5173 {
5174 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5175 RouteTimeAxisView* tatv;
5176 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5177 if (!tatv->track()) {
5178 continue;
5179 }
5180 RegionView* marv = tatv->view()->find_view (region);
5181 if (marv) {
5182 return marv;
5183 }
5184 }
5185 }
5186 return NULL;
5187 }
5188
5189 RouteTimeAxisView*
rtav_from_route(boost::shared_ptr<Route> route) const5190 Editor::rtav_from_route (boost::shared_ptr<Route> route) const
5191 {
5192 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5193 RouteTimeAxisView* rtav;
5194 if ((rtav = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5195 if (rtav->route() == route) {
5196 return rtav;
5197 }
5198 }
5199 }
5200 return NULL;
5201 }
5202
5203 void
show_rhythm_ferret()5204 Editor::show_rhythm_ferret ()
5205 {
5206 if (rhythm_ferret == 0) {
5207 rhythm_ferret = new RhythmFerret(*this);
5208 }
5209
5210 rhythm_ferret->set_session (_session);
5211 rhythm_ferret->show ();
5212 rhythm_ferret->present ();
5213 }
5214
5215 void
first_idle()5216 Editor::first_idle ()
5217 {
5218 ArdourMessageDialog* dialog = 0;
5219
5220 if (track_views.size() > 1) {
5221 Timers::TimerSuspender t;
5222 dialog = new ArdourMessageDialog (
5223 string_compose (_("Please wait while %1 loads visual data."), PROGRAM_NAME),
5224 true
5225 );
5226 dialog->present ();
5227 ARDOUR_UI::instance()->flush_pending (60);
5228 }
5229
5230 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
5231 (*t)->first_idle();
5232 }
5233
5234 /* now that all regionviews should exist, setup region selection */
5235
5236 RegionSelection rs;
5237
5238 for (list<PBD::ID>::iterator pr = selection->regions.pending.begin (); pr != selection->regions.pending.end (); ++pr) {
5239 /* this is cumulative: rs is NOT cleared each time */
5240 get_regionviews_by_id (*pr, rs);
5241 }
5242
5243 selection->set (rs);
5244
5245 /* first idle adds route children (automation tracks), so we need to redisplay here */
5246 _routes->redisplay ();
5247
5248 delete dialog;
5249
5250 if (_session->undo_depth() == 0) {
5251 undo_action->set_sensitive(false);
5252 }
5253 redo_action->set_sensitive(false);
5254 begin_selection_op_history ();
5255
5256 _have_idled = true;
5257 }
5258
5259 gboolean
_idle_resize(gpointer arg)5260 Editor::_idle_resize (gpointer arg)
5261 {
5262 return ((Editor*)arg)->idle_resize ();
5263 }
5264
5265 void
add_to_idle_resize(TimeAxisView * view,int32_t h)5266 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
5267 {
5268 if (resize_idle_id < 0) {
5269 /* https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#G-PRIORITY-HIGH-IDLE:CAPS
5270 * GTK+ uses G_PRIORITY_HIGH_IDLE + 10 for resizing operations, and G_PRIORITY_HIGH_IDLE + 20 for redrawing operations.
5271 * (This is done to ensure that any pending resizes are processed before any pending redraws, so that widgets are not redrawn twice unnecessarily.)
5272 */
5273 resize_idle_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, _idle_resize, this, NULL);
5274 _pending_resize_amount = 0;
5275 }
5276
5277 /* make a note of the smallest resulting height, so that we can clamp the
5278 lower limit at TimeAxisView::hSmall */
5279
5280 int32_t min_resulting = INT32_MAX;
5281
5282 _pending_resize_amount += h;
5283 _pending_resize_view = view;
5284
5285 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
5286
5287 if (selection->tracks.contains (_pending_resize_view)) {
5288 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5289 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
5290 }
5291 }
5292
5293 if (min_resulting < 0) {
5294 min_resulting = 0;
5295 }
5296
5297 /* clamp */
5298 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
5299 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
5300 }
5301 }
5302
5303 /** Handle pending resizing of tracks */
5304 bool
idle_resize()5305 Editor::idle_resize ()
5306 {
5307 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
5308
5309 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
5310 selection->tracks.contains (_pending_resize_view)) {
5311
5312 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5313 if (*i != _pending_resize_view) {
5314 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
5315 }
5316 }
5317 }
5318
5319 _pending_resize_amount = 0;
5320 _group_tabs->set_dirty ();
5321 resize_idle_id = -1;
5322
5323 return false;
5324 }
5325
5326 void
located()5327 Editor::located ()
5328 {
5329 ENSURE_GUI_THREAD (*this, &Editor::located);
5330
5331 if (_session) {
5332 _playhead_cursor->set_position (_session->audible_sample ());
5333 if (_follow_playhead && !_pending_initial_locate) {
5334 reset_x_origin_to_follow_playhead ();
5335 }
5336 }
5337
5338 _pending_locate_request = false;
5339 _pending_initial_locate = false;
5340 _last_update_time = 0;
5341 }
5342
5343 void
region_view_added(RegionView * rv)5344 Editor::region_view_added (RegionView * rv)
5345 {
5346 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (rv);
5347 if (mrv) {
5348 list<pair<PBD::ID const, list<Evoral::event_id_t> > >::iterator rnote;
5349 for (rnote = selection->pending_midi_note_selection.begin(); rnote != selection->pending_midi_note_selection.end(); ++rnote) {
5350 if (rv->region()->id () == (*rnote).first) {
5351 list<Evoral::event_id_t> notes ((*rnote).second);
5352 selection->pending_midi_note_selection.erase(rnote);
5353 mrv->select_notes (notes, false); // NB. this may change the selection
5354 break;
5355 }
5356 }
5357 }
5358
5359 _summary->set_background_dirty ();
5360
5361 mark_region_boundary_cache_dirty ();
5362 }
5363
5364 void
region_view_removed()5365 Editor::region_view_removed ()
5366 {
5367 _summary->set_background_dirty ();
5368
5369 mark_region_boundary_cache_dirty ();
5370 }
5371
5372 AxisView*
axis_view_by_stripable(boost::shared_ptr<Stripable> s) const5373 Editor::axis_view_by_stripable (boost::shared_ptr<Stripable> s) const
5374 {
5375 for (TrackViewList::const_iterator j = track_views.begin (); j != track_views.end(); ++j) {
5376 if ((*j)->stripable() == s) {
5377 return *j;
5378 }
5379 }
5380
5381 return 0;
5382 }
5383
5384 AxisView*
axis_view_by_control(boost::shared_ptr<AutomationControl> c) const5385 Editor::axis_view_by_control (boost::shared_ptr<AutomationControl> c) const
5386 {
5387 for (TrackViewList::const_iterator j = track_views.begin (); j != track_views.end(); ++j) {
5388 if ((*j)->control() == c) {
5389 return *j;
5390 }
5391
5392 TimeAxisView::Children kids = (*j)->get_child_list ();
5393
5394 for (TimeAxisView::Children::iterator k = kids.begin(); k != kids.end(); ++k) {
5395 if ((*k)->control() == c) {
5396 return (*k).get();
5397 }
5398 }
5399 }
5400
5401 return 0;
5402 }
5403
5404 TrackViewList
axis_views_from_routes(boost::shared_ptr<RouteList> r) const5405 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
5406 {
5407 TrackViewList t;
5408
5409 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
5410 TimeAxisView* tv = time_axis_view_from_stripable (*i);
5411 if (tv) {
5412 t.push_back (tv);
5413 }
5414 }
5415
5416 return t;
5417 }
5418
5419 void
suspend_route_redisplay()5420 Editor::suspend_route_redisplay ()
5421 {
5422 if (_routes) {
5423 _routes->suspend_redisplay();
5424 }
5425 }
5426
5427 void
resume_route_redisplay()5428 Editor::resume_route_redisplay ()
5429 {
5430 if (_routes) {
5431 _routes->redisplay(); // queue redisplay
5432 _routes->resume_redisplay();
5433 }
5434 }
5435
5436 void
add_vcas(VCAList & vlist)5437 Editor::add_vcas (VCAList& vlist)
5438 {
5439 StripableList sl;
5440
5441 for (VCAList::iterator v = vlist.begin(); v != vlist.end(); ++v) {
5442 sl.push_back (boost::dynamic_pointer_cast<Stripable> (*v));
5443 }
5444
5445 add_stripables (sl);
5446 }
5447
5448 void
add_routes(RouteList & rlist)5449 Editor::add_routes (RouteList& rlist)
5450 {
5451 StripableList sl;
5452
5453 for (RouteList::iterator r = rlist.begin(); r != rlist.end(); ++r) {
5454 sl.push_back (*r);
5455 }
5456
5457 add_stripables (sl);
5458 }
5459
5460 void
add_stripables(StripableList & sl)5461 Editor::add_stripables (StripableList& sl)
5462 {
5463 list<TimeAxisView*> new_views;
5464 boost::shared_ptr<VCA> v;
5465 boost::shared_ptr<Route> r;
5466 TrackViewList new_selection;
5467 bool from_scratch = (track_views.size() == 0);
5468
5469 sl.sort (Stripable::Sorter());
5470
5471 for (StripableList::iterator s = sl.begin(); s != sl.end(); ++s) {
5472
5473 if ((*s)->is_foldbackbus()) {
5474 continue;
5475 }
5476
5477 if ((v = boost::dynamic_pointer_cast<VCA> (*s)) != 0) {
5478
5479 VCATimeAxisView* vtv = new VCATimeAxisView (*this, _session, *_track_canvas);
5480 vtv->set_vca (v);
5481 new_views.push_back (vtv);
5482
5483 } else if ((r = boost::dynamic_pointer_cast<Route> (*s)) != 0) {
5484
5485 if (r->is_auditioner() || r->is_monitor()) {
5486 continue;
5487 }
5488
5489 RouteTimeAxisView* rtv;
5490 DataType dt = r->input()->default_type();
5491
5492 if (dt == ARDOUR::DataType::AUDIO) {
5493 rtv = new AudioTimeAxisView (*this, _session, *_track_canvas);
5494 rtv->set_route (r);
5495 } else if (dt == ARDOUR::DataType::MIDI) {
5496 rtv = new MidiTimeAxisView (*this, _session, *_track_canvas);
5497 rtv->set_route (r);
5498 } else {
5499 throw unknown_type();
5500 }
5501
5502 new_views.push_back (rtv);
5503 track_views.push_back (rtv);
5504 new_selection.push_back (rtv);
5505
5506 rtv->effective_gain_display ();
5507
5508 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
5509 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
5510 }
5511 }
5512
5513 if (new_views.size() > 0) {
5514 _routes->time_axis_views_added (new_views);
5515 //_summary->routes_added (new_selection); /* XXX requires RouteTimeAxisViewList */
5516 }
5517
5518 /* note: !new_selection.empty() means that we got some routes rather
5519 * than just VCAs
5520 */
5521
5522 if (!from_scratch && !new_selection.empty()) {
5523 selection->set (new_selection);
5524 begin_selection_op_history();
5525 }
5526
5527 if (show_editor_mixer_when_tracks_arrive && !new_selection.empty()) {
5528 show_editor_mixer (true);
5529 }
5530
5531 editor_list_button.set_sensitive (true);
5532 }
5533
5534 void
timeaxisview_deleted(TimeAxisView * tv)5535 Editor::timeaxisview_deleted (TimeAxisView *tv)
5536 {
5537 if (tv == entered_track) {
5538 entered_track = 0;
5539 }
5540
5541 if (_session && _session->deletion_in_progress()) {
5542 /* the situation is under control */
5543 return;
5544 }
5545
5546 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
5547
5548 if (dynamic_cast<AutomationTimeAxisView*> (tv)) {
5549 selection->remove (tv);
5550 return;
5551 }
5552
5553 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
5554
5555 _routes->route_removed (tv);
5556
5557 TimeAxisView::Children c = tv->get_child_list ();
5558 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
5559 if (entered_track == i->get()) {
5560 entered_track = 0;
5561 }
5562 }
5563
5564 /* remove it from the list of track views */
5565
5566 TrackViewList::iterator i;
5567
5568 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
5569 i = track_views.erase (i);
5570 }
5571
5572 /* Update the route that is shown in the editor-mixer. */
5573 if (!rtav) {
5574 return;
5575 }
5576
5577 boost::shared_ptr<Route> route = rtav->route ();
5578 if (current_mixer_strip && current_mixer_strip->route() == route) {
5579
5580 TimeAxisView* next_tv;
5581
5582 if (track_views.empty()) {
5583 next_tv = 0;
5584 } else if (i == track_views.end()) {
5585 next_tv = track_views.front();
5586 } else {
5587 next_tv = (*i);
5588 }
5589
5590 // skip VCAs (cannot be selected, n/a in editor-mixer)
5591 if (dynamic_cast<VCATimeAxisView*> (next_tv)) {
5592 /* VCAs are sorted last in line -- route_sorter.h, jump to top */
5593 next_tv = track_views.front();
5594 }
5595 if (dynamic_cast<VCATimeAxisView*> (next_tv)) {
5596 /* just in case: no master, only a VCA remains */
5597 next_tv = 0;
5598 }
5599
5600
5601 if (next_tv) {
5602 set_selected_mixer_strip (*next_tv);
5603 } else {
5604 /* make the editor mixer strip go away setting the
5605 * button to inactive (which also unticks the menu option)
5606 */
5607
5608 ActionManager::uncheck_toggleaction ("Editor/show-editor-mixer");
5609 }
5610 }
5611 }
5612
5613 void
hide_track_in_display(TimeAxisView * tv,bool apply_to_selection)5614 Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection)
5615 {
5616 if (!tv) {
5617 return;
5618 }
5619
5620 DisplaySuspender ds;
5621 PresentationInfo::ChangeSuspender cs;
5622
5623 if (apply_to_selection) {
5624 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end();) {
5625
5626 TrackSelection::iterator j = i;
5627 ++j;
5628
5629 hide_track_in_display (*i, false);
5630
5631 i = j;
5632 }
5633 } else {
5634 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
5635
5636 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
5637 /* this will hide the mixer strip */
5638 set_selected_mixer_strip (*tv);
5639 }
5640
5641 _routes->hide_track_in_display (*tv);
5642 }
5643 }
5644
5645 void
show_track_in_display(TimeAxisView * tv,bool move_into_view)5646 Editor::show_track_in_display (TimeAxisView* tv, bool move_into_view)
5647 {
5648 if (!tv) {
5649 return;
5650 }
5651 _routes->show_track_in_display (*tv);
5652 if (move_into_view) {
5653 ensure_time_axis_view_is_visible (*tv, false);
5654 }
5655 }
5656
5657 bool
sync_track_view_list_and_routes()5658 Editor::sync_track_view_list_and_routes ()
5659 {
5660 track_views = TrackViewList (_routes->views ());
5661
5662 _summary->set_background_dirty();
5663 _group_tabs->set_dirty ();
5664
5665 return false; // do not call again (until needed)
5666 }
5667
5668 void
foreach_time_axis_view(sigc::slot<void,TimeAxisView &> theslot)5669 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5670 {
5671 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5672 theslot (**i);
5673 }
5674 }
5675
5676 /** Find a StripableTimeAxisView by the ID of its stripable */
5677 StripableTimeAxisView*
get_stripable_time_axis_by_id(const PBD::ID & id) const5678 Editor::get_stripable_time_axis_by_id (const PBD::ID& id) const
5679 {
5680 StripableTimeAxisView* v;
5681
5682 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5683 if((v = dynamic_cast<StripableTimeAxisView*>(*i)) != 0) {
5684 if(v->stripable()->id() == id) {
5685 return v;
5686 }
5687 }
5688 }
5689
5690 return 0;
5691 }
5692
5693 void
fit_route_group(RouteGroup * g)5694 Editor::fit_route_group (RouteGroup *g)
5695 {
5696 TrackViewList ts = axis_views_from_routes (g->route_list ());
5697 fit_tracks (ts);
5698 }
5699
5700 void
consider_auditioning(boost::shared_ptr<Region> region)5701 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5702 {
5703 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5704
5705 if (r == 0) {
5706 _session->cancel_audition ();
5707 return;
5708 }
5709
5710 if (_session->is_auditioning()) {
5711 _session->cancel_audition ();
5712 if (r == last_audition_region) {
5713 return;
5714 }
5715 }
5716
5717 _session->audition_region (r);
5718 last_audition_region = r;
5719 }
5720
5721
5722 void
hide_a_region(boost::shared_ptr<Region> r)5723 Editor::hide_a_region (boost::shared_ptr<Region> r)
5724 {
5725 r->set_hidden (true);
5726 }
5727
5728 void
show_a_region(boost::shared_ptr<Region> r)5729 Editor::show_a_region (boost::shared_ptr<Region> r)
5730 {
5731 r->set_hidden (false);
5732 }
5733
5734 void
audition_region_from_region_list()5735 Editor::audition_region_from_region_list ()
5736 {
5737 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5738 }
5739
5740 void
step_edit_status_change(bool yn)5741 Editor::step_edit_status_change (bool yn)
5742 {
5743 if (yn) {
5744 start_step_editing ();
5745 } else {
5746 stop_step_editing ();
5747 }
5748 }
5749
5750 void
start_step_editing()5751 Editor::start_step_editing ()
5752 {
5753 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5754 }
5755
5756 void
stop_step_editing()5757 Editor::stop_step_editing ()
5758 {
5759 step_edit_connection.disconnect ();
5760 }
5761
5762 bool
check_step_edit()5763 Editor::check_step_edit ()
5764 {
5765 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5766 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5767 if (mtv) {
5768 mtv->check_step_edit ();
5769 }
5770 }
5771
5772 return true; // do it again, till we stop
5773 }
5774
5775 bool
scroll_press(Direction dir)5776 Editor::scroll_press (Direction dir)
5777 {
5778 ++_scroll_callbacks;
5779
5780 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5781 /* delay the first auto-repeat */
5782 return true;
5783 }
5784
5785 switch (dir) {
5786 case LEFT:
5787 scroll_backward (1);
5788 break;
5789
5790 case RIGHT:
5791 scroll_forward (1);
5792 break;
5793
5794 case UP:
5795 scroll_up_one_track ();
5796 break;
5797
5798 case DOWN:
5799 scroll_down_one_track ();
5800 break;
5801 }
5802
5803 /* do hacky auto-repeat */
5804 if (!_scroll_connection.connected ()) {
5805
5806 _scroll_connection = Glib::signal_timeout().connect (
5807 sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100
5808 );
5809
5810 _scroll_callbacks = 0;
5811 }
5812
5813 return true;
5814 }
5815
5816 void
scroll_release()5817 Editor::scroll_release ()
5818 {
5819 _scroll_connection.disconnect ();
5820 }
5821
5822 /** Queue a change for the Editor viewport x origin to follow the playhead */
5823 void
reset_x_origin_to_follow_playhead()5824 Editor::reset_x_origin_to_follow_playhead ()
5825 {
5826 samplepos_t const sample = _playhead_cursor->current_sample ();
5827
5828 if (sample < _leftmost_sample || sample > _leftmost_sample + current_page_samples()) {
5829
5830 if (_session->transport_speed() < 0) {
5831
5832 if (sample > (current_page_samples() / 2)) {
5833 center_screen (sample-(current_page_samples()/2));
5834 } else {
5835 center_screen (current_page_samples()/2);
5836 }
5837
5838 } else {
5839
5840 samplepos_t l = 0;
5841
5842 if (sample < _leftmost_sample) {
5843 /* moving left */
5844 if (_session->transport_rolling()) {
5845 /* rolling; end up with the playhead at the right of the page */
5846 l = sample - current_page_samples ();
5847 } else {
5848 /* not rolling: end up with the playhead 1/4 of the way along the page */
5849 l = sample - current_page_samples() / 4;
5850 }
5851 } else {
5852 /* moving right */
5853 if (_session->transport_rolling()) {
5854 /* rolling: end up with the playhead on the left of the page */
5855 l = sample;
5856 } else {
5857 /* not rolling: end up with the playhead 3/4 of the way along the page */
5858 l = sample - 3 * current_page_samples() / 4;
5859 }
5860 }
5861
5862 if (l < 0) {
5863 l = 0;
5864 }
5865
5866 center_screen_internal (l + (current_page_samples() / 2), current_page_samples ());
5867 }
5868 }
5869 }
5870
5871 void
super_rapid_screen_update()5872 Editor::super_rapid_screen_update ()
5873 {
5874 if (!_session || !_session->engine().running()) {
5875 return;
5876 }
5877
5878 /* METERING / MIXER STRIPS */
5879
5880 /* update track meters, if required */
5881 if (contents().is_mapped() && meters_running) {
5882 RouteTimeAxisView* rtv;
5883 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5884 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5885 rtv->fast_update ();
5886 }
5887 }
5888 }
5889
5890 /* and any current mixer strip */
5891 if (current_mixer_strip) {
5892 current_mixer_strip->fast_update ();
5893 }
5894
5895 bool latent_locate = false;
5896 samplepos_t sample = _session->audible_sample (&latent_locate);
5897 const int64_t now = g_get_monotonic_time ();
5898 double err = 0;
5899
5900 if (_session->exporting ()) {
5901 /* freewheel/export may be faster or slower than transport_speed() / SR.
5902 * Also exporting multiple ranges locates/jumps without a _pending_locate_request.
5903 */
5904 _last_update_time = 0;
5905 }
5906
5907 if (!_session->transport_rolling () || _session->is_auditioning ()) {
5908 /* Do not interpolate the playhead position; just set it */
5909 _last_update_time = 0;
5910 }
5911
5912 if (_last_update_time > 0) {
5913 /* interpolate and smoothen playhead position */
5914 const double ds = (now - _last_update_time) * _session->transport_speed() * _session->nominal_sample_rate () * 1e-6;
5915 samplepos_t guess = _playhead_cursor->current_sample () + rint (ds);
5916 err = sample - guess;
5917
5918 guess += err * .12 + _err_screen_engine; // time-constant based on 25fps (super_rapid_screen_update)
5919 _err_screen_engine += .0144 * (err - _err_screen_engine); // tc^2
5920
5921 #if 0 // DEBUG
5922 printf ("eng: %ld gui:%ld (%+6.1f) diff: %6.1f (err: %7.2f)\n",
5923 sample, guess, ds,
5924 err, _err_screen_engine);
5925 #endif
5926
5927 sample = guess;
5928 } else {
5929 _err_screen_engine = 0;
5930 }
5931
5932 if (err > 8192 || latent_locate) {
5933 // in case of xruns or freewheeling
5934 _last_update_time = 0;
5935 sample = _session->audible_sample ();
5936 } else {
5937 _last_update_time = now;
5938 }
5939
5940 /* snapped cursor stuff (the snapped_cursor shows where an operation is going to occur) */
5941 bool ignored;
5942 MusicSample where (sample, 0);
5943 if (!UIConfiguration::instance().get_show_snapped_cursor()) {
5944 _snapped_cursor->hide ();
5945 } else if (_edit_point == EditAtPlayhead && !_dragging_playhead) {
5946 /* EditAtPlayhead does not snap */
5947 } else if (_edit_point == EditAtSelectedMarker) {
5948 /* NOTE: I don't think EditAtSelectedMarker should snap. They are what they are.
5949 * however, the current editing code -does- snap so I'll draw it that way for now.
5950 */
5951 if (!selection->markers.empty()) {
5952 MusicSample ms (selection->markers.front()->position(), 0);
5953 snap_to (ms); // should use snap_to_with_modifier?
5954 _snapped_cursor->set_position (ms.sample);
5955 _snapped_cursor->show ();
5956 }
5957 } else if (_edit_point == EditAtMouse && mouse_sample (where.sample, ignored)) {
5958 /* cursor is in the editing canvas. show it. */
5959 _snapped_cursor->show ();
5960 } else {
5961 /* mouse is out of the editing canvas, or edit-point isn't mouse. Hide the snapped_cursor */
5962 _snapped_cursor->hide ();
5963 }
5964
5965 /* There are a few reasons why we might not update the playhead / viewport stuff:
5966 *
5967 * 1. we don't update things when there's a pending locate request, otherwise
5968 * when the editor requests a locate there is a chance that this method
5969 * will move the playhead before the locate request is processed, causing
5970 * a visual glitch.
5971 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5972 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5973 */
5974 if (_pending_locate_request) {
5975 _last_update_time = 0;
5976 return;
5977 }
5978
5979 if (_dragging_playhead) {
5980 _last_update_time = 0;
5981 return;
5982 }
5983
5984 if (_playhead_cursor->current_sample () == sample) {
5985 return;
5986 }
5987
5988 if (!_pending_locate_request && !_session->locate_initiated()) {
5989 _playhead_cursor->set_position (sample);
5990 }
5991
5992 if (_session->requested_return_sample() >= 0) {
5993 _last_update_time = 0;
5994 return;
5995 }
5996
5997 if (!_follow_playhead || pending_visual_change.being_handled) {
5998 /* We only do this if we aren't already
5999 * handling a visual change (ie if
6000 * pending_visual_change.being_handled is
6001 * false) so that these requests don't stack
6002 * up there are too many of them to handle in
6003 * time.
6004 */
6005 return;
6006 }
6007
6008 if (!_stationary_playhead) {
6009 reset_x_origin_to_follow_playhead ();
6010 } else {
6011 samplepos_t const sample = _playhead_cursor->current_sample ();
6012 double target = ((double)sample - (double)current_page_samples() / 2.0);
6013 if (target <= 0.0) {
6014 target = 0.0;
6015 }
6016 /* compare to EditorCursor::set_position() */
6017 double const old_pos = sample_to_pixel_unrounded (_leftmost_sample);
6018 double const new_pos = sample_to_pixel_unrounded (target);
6019 if (rint (new_pos) != rint (old_pos)) {
6020 reset_x_origin (pixel_to_sample (new_pos));
6021 }
6022 }
6023 }
6024
6025
6026 void
session_going_away()6027 Editor::session_going_away ()
6028 {
6029 _have_idled = false;
6030
6031 _session_connections.drop_connections ();
6032
6033 super_rapid_screen_update_connection.disconnect ();
6034
6035 selection->clear ();
6036 cut_buffer->clear ();
6037
6038 clicked_regionview = 0;
6039 clicked_axisview = 0;
6040 clicked_routeview = 0;
6041 entered_regionview = 0;
6042 entered_track = 0;
6043 _last_update_time = 0;
6044 _drags->abort ();
6045
6046 _playhead_cursor->hide ();
6047
6048 /* rip everything out of the list displays */
6049
6050 _regions->clear ();
6051 _sources->clear ();
6052 _routes->clear ();
6053 _route_groups->clear ();
6054
6055 /* do this first so that deleting a track doesn't reset cms to null
6056 and thus cause a leak.
6057 */
6058
6059 if (current_mixer_strip) {
6060 if (current_mixer_strip->get_parent() != 0) {
6061 global_hpacker.remove (*current_mixer_strip);
6062 }
6063 delete current_mixer_strip;
6064 current_mixer_strip = 0;
6065 }
6066
6067 /* delete all trackviews */
6068
6069 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6070 delete *i;
6071 }
6072 track_views.clear ();
6073
6074 nudge_clock->set_session (0);
6075
6076 editor_list_button.set_active(false);
6077 editor_list_button.set_sensitive(false);
6078
6079 /* clear tempo/meter rulers */
6080 remove_metric_marks ();
6081 clear_marker_display ();
6082
6083 hide_grid_lines ();
6084 delete grid_lines;
6085 grid_lines = 0;
6086
6087 stop_step_editing ();
6088
6089 if (own_window()) {
6090
6091 /* get rid of any existing editor mixer strip */
6092
6093 WindowTitle title(Glib::get_application_name());
6094 title += _("Editor");
6095
6096 own_window()->set_title (title.get_string());
6097 }
6098
6099 SessionHandlePtr::session_going_away ();
6100 }
6101
6102 void
trigger_script(int i)6103 Editor::trigger_script (int i)
6104 {
6105 LuaInstance::instance()-> call_action (i);
6106 }
6107
6108 void
show_editor_list(bool yn)6109 Editor::show_editor_list (bool yn)
6110 {
6111 if (yn) {
6112 _editor_list_vbox.show ();
6113 } else {
6114 _editor_list_vbox.hide ();
6115 }
6116 }
6117
6118 void
change_region_layering_order(bool from_context_menu)6119 Editor::change_region_layering_order (bool from_context_menu)
6120 {
6121 const samplepos_t position = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context_menu);
6122
6123 if (!clicked_routeview) {
6124 if (layering_order_editor) {
6125 layering_order_editor->hide ();
6126 }
6127 return;
6128 }
6129
6130 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
6131
6132 if (!track) {
6133 return;
6134 }
6135
6136 boost::shared_ptr<Playlist> pl = track->playlist();
6137
6138 if (!pl) {
6139 return;
6140 }
6141
6142 if (layering_order_editor == 0) {
6143 layering_order_editor = new RegionLayeringOrderEditor (*this);
6144 }
6145
6146 layering_order_editor->set_context (clicked_routeview->name(), _session, clicked_routeview, pl, position);
6147 layering_order_editor->maybe_present ();
6148 }
6149
6150 void
update_region_layering_order_editor()6151 Editor::update_region_layering_order_editor ()
6152 {
6153 if (layering_order_editor && layering_order_editor->is_visible ()) {
6154 change_region_layering_order (true);
6155 }
6156 }
6157
6158 void
setup_fade_images()6159 Editor::setup_fade_images ()
6160 {
6161 _xfade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadein-linear")));
6162 _xfade_in_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadein-symmetric")));
6163 _xfade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadein-fast-cut")));
6164 _xfade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadein-slow-cut")));
6165 _xfade_in_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadein-constant-power")));
6166
6167 _xfade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadeout-linear")));
6168 _xfade_out_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-symmetric")));
6169 _xfade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut")));
6170 _xfade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut")));
6171 _xfade_out_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-constant-power")));
6172
6173 }
6174
6175 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
6176 Gtk::MenuItem&
action_menu_item(std::string const & name)6177 Editor::action_menu_item (std::string const & name)
6178 {
6179 Glib::RefPtr<Action> a = editor_actions->get_action (name);
6180 assert (a);
6181
6182 return *manage (a->create_menu_item ());
6183 }
6184
6185 void
add_notebook_page(string const & name,Gtk::Widget & widget)6186 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
6187 {
6188 EventBox* b = manage (new EventBox);
6189 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
6190 Label* l = manage (new Label (name));
6191 l->set_angle (-90);
6192 b->add (*l);
6193 b->show_all ();
6194 _the_notebook.append_page (widget, *b);
6195 }
6196
6197 bool
notebook_tab_clicked(GdkEventButton * ev,Gtk::Widget * page)6198 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
6199 {
6200 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
6201 _the_notebook.set_current_page (_the_notebook.page_num (*page));
6202 }
6203
6204 if (ev->type == GDK_2BUTTON_PRESS) {
6205
6206 /* double-click on a notebook tab shrinks or expands the notebook */
6207
6208 if (_notebook_shrunk) {
6209 if (pre_notebook_shrink_pane_width) {
6210 edit_pane.set_divider (0, *pre_notebook_shrink_pane_width);
6211 }
6212 _notebook_shrunk = false;
6213 } else {
6214 pre_notebook_shrink_pane_width = edit_pane.get_divider();
6215
6216 /* this expands the LHS of the edit pane to cover the notebook
6217 PAGE but leaves the tabs visible.
6218 */
6219 edit_pane.set_divider (0, edit_pane.get_divider() + page->get_width());
6220 _notebook_shrunk = true;
6221 }
6222 }
6223
6224 return true;
6225 }
6226
6227 void
popup_control_point_context_menu(ArdourCanvas::Item * item,GdkEvent * event)6228 Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
6229 {
6230 using namespace Menu_Helpers;
6231
6232 MenuList& items = _control_point_context_menu.items ();
6233 items.clear ();
6234
6235 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &Editor::edit_control_point), item)));
6236 items.push_back (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun (*this, &Editor::remove_control_point), item)));
6237 if (!can_remove_control_point (item)) {
6238 items.back().set_sensitive (false);
6239 }
6240
6241 _control_point_context_menu.popup (event->button.button, event->button.time);
6242 }
6243
6244 void
popup_note_context_menu(ArdourCanvas::Item * item,GdkEvent * event)6245 Editor::popup_note_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
6246 {
6247 using namespace Menu_Helpers;
6248
6249 NoteBase* note = reinterpret_cast<NoteBase*>(item->get_data("notebase"));
6250 if (!note) {
6251 return;
6252 }
6253
6254 /* We need to get the selection here and pass it to the operations, since
6255 popping up the menu will cause a region leave event which clears
6256 entered_regionview. */
6257
6258 MidiRegionView& mrv = note->region_view();
6259 const RegionSelection rs = get_regions_from_selection_and_entered ();
6260 const uint32_t sel_size = mrv.selection_size ();
6261
6262 MenuList& items = _note_context_menu.items();
6263 items.clear();
6264
6265 if (sel_size > 0) {
6266 items.push_back(MenuElem(_("Delete"),
6267 sigc::mem_fun(mrv, &MidiRegionView::delete_selection)));
6268 }
6269
6270 items.push_back(MenuElem(_("Edit..."),
6271 sigc::bind(sigc::mem_fun(*this, &Editor::edit_notes), &mrv)));
6272 if (sel_size != 1) {
6273 items.back().set_sensitive (false);
6274 }
6275
6276 items.push_back(MenuElem(_("Transpose..."),
6277 sigc::bind(sigc::mem_fun(*this, &Editor::transpose_regions), rs)));
6278
6279
6280 items.push_back(MenuElem(_("Legatize"),
6281 sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, false)));
6282 if (sel_size < 2) {
6283 items.back().set_sensitive (false);
6284 }
6285
6286 items.push_back(MenuElem(_("Quantize..."),
6287 sigc::bind(sigc::mem_fun(*this, &Editor::quantize_regions), rs)));
6288
6289 items.push_back(MenuElem(_("Remove Overlap"),
6290 sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, true)));
6291 if (sel_size < 2) {
6292 items.back().set_sensitive (false);
6293 }
6294
6295 items.push_back(MenuElem(_("Transform..."),
6296 sigc::bind(sigc::mem_fun(*this, &Editor::transform_regions), rs)));
6297
6298 _note_context_menu.popup (event->button.button, event->button.time);
6299 }
6300
6301 void
zoom_vertical_modifier_released()6302 Editor::zoom_vertical_modifier_released()
6303 {
6304 _stepping_axis_view = 0;
6305 }
6306
6307 void
ui_parameter_changed(string parameter)6308 Editor::ui_parameter_changed (string parameter)
6309 {
6310 if (parameter == "icon-set") {
6311 while (!_cursor_stack.empty()) {
6312 _cursor_stack.pop_back();
6313 }
6314 _cursors->set_cursor_set (UIConfiguration::instance().get_icon_set());
6315 _cursor_stack.push_back(_cursors->grabber);
6316 edit_pane.set_drag_cursor (*_cursors->expand_left_right);
6317 editor_summary_pane.set_drag_cursor (*_cursors->expand_up_down);
6318
6319 } else if (parameter == "draggable-playhead") {
6320 if (_verbose_cursor) {
6321 _playhead_cursor->set_sensitive (UIConfiguration::instance().get_draggable_playhead());
6322 }
6323 } else if (parameter == "use-note-bars-for-velocity") {
6324 ArdourCanvas::Note::set_show_velocity_bars (UIConfiguration::instance().get_use_note_bars_for_velocity());
6325 _track_canvas->request_redraw (_track_canvas->visible_area());
6326 } else if (parameter == "use-note-color-for-velocity") {
6327 /* handled individually by each MidiRegionView */
6328 }
6329 }
6330
6331 Gtk::Window*
use_own_window(bool and_fill_it)6332 Editor::use_own_window (bool and_fill_it)
6333 {
6334 bool new_window = !own_window();
6335
6336 Gtk::Window* win = Tabbable::use_own_window (and_fill_it);
6337
6338 if (win && new_window) {
6339 win->set_name ("EditorWindow");
6340
6341 ARDOUR_UI::instance()->setup_toplevel_window (*win, _("Editor"), this);
6342
6343 // win->signal_realize().connect (*this, &Editor::on_realize);
6344 win->signal_event().connect (sigc::bind (sigc::ptr_fun (&Keyboard::catch_user_event_for_pre_dialog_focus), win));
6345 win->signal_event().connect (sigc::mem_fun (*this, &Editor::generic_event_handler));
6346 win->set_data ("ardour-bindings", bindings);
6347
6348 update_title ();
6349 }
6350
6351 DisplaySuspender ds;
6352 contents().show_all ();
6353
6354 /* XXX: this is a bit unfortunate; it would probably
6355 be nicer if we could just call show () above rather
6356 than needing the show_all ()
6357 */
6358
6359 /* re-hide stuff if necessary */
6360 editor_list_button_toggled ();
6361 parameter_changed ("show-summary");
6362 parameter_changed ("show-group-tabs");
6363 parameter_changed ("show-zoom-tools");
6364
6365 /* now reset all audio_time_axis heights, because widgets might need
6366 to be re-hidden
6367 */
6368
6369 TimeAxisView *tv;
6370
6371 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6372 tv = (static_cast<TimeAxisView*>(*i));
6373 tv->reset_height ();
6374 }
6375
6376 if (current_mixer_strip) {
6377 current_mixer_strip->hide_things ();
6378 current_mixer_strip->parameter_changed ("mixer-element-visibility");
6379 }
6380
6381 return win;
6382 }
6383