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>
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.
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.
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.
32 /* Note: public Editor methods are documented in public_editor.h */
42 #include "ardour_ui.h"
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.
49 #include <boost/none.hpp>
51 #include <sigc++/bind.h>
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/stacktrace.h"
60 #include "pbd/timersub.h"
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>
68 #include <gtkmm/menu.h>
69 #include <gtkmm/menuitem.h>
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"
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"
93 #include "canvas/debug.h"
94 #include "canvas/note.h"
95 #include "canvas/text.h"
97 #include "widgets/ardour_spacer.h"
98 #include "widgets/eventboxext.h"
99 #include "widgets/tooltips.h"
101 #include "control_protocol/control_protocol.h"
104 #include "analysis_window.h"
105 #include "audio_clock.h"
106 #include "audio_region_view.h"
107 #include "audio_streamview.h"
108 #include "audio_time_axis.h"
109 #include "automation_time_axis.h"
110 #include "bundle_manager.h"
111 #include "crossfade_edit.h"
114 #include "editing_convert.h"
116 #include "editor_cursors.h"
117 #include "editor_drag.h"
118 #include "editor_group_tabs.h"
119 #include "editor_locations.h"
120 #include "editor_regions.h"
121 #include "editor_route_groups.h"
122 #include "editor_routes.h"
123 #include "editor_snapshots.h"
124 #include "editor_sources.h"
125 #include "editor_summary.h"
126 #include "enums_convert.h"
127 #include "export_report.h"
128 #include "global_port_matrix.h"
129 #include "gui_object.h"
130 #include "gui_thread.h"
131 #include "keyboard.h"
132 #include "luainstance.h"
134 #include "midi_region_view.h"
135 #include "midi_time_axis.h"
136 #include "mixer_strip.h"
137 #include "mixer_ui.h"
138 #include "mouse_cursors.h"
139 #include "note_base.h"
140 #include "playlist_selector.h"
141 #include "public_editor.h"
142 #include "quantize_dialog.h"
143 #include "region_layering_order_editor.h"
144 #include "rgb_macros.h"
145 #include "rhythm_ferret.h"
146 #include "route_sorter.h"
147 #include "selection.h"
148 #include "simple_progress_dialog.h"
150 #include "grid_lines.h"
151 #include "time_axis_view.h"
152 #include "time_info_box.h"
154 #include "ui_config.h"
156 #include "vca_time_axis.h"
157 #include "verbose_cursor.h"
159 #include "pbd/i18n.h"
162 using namespace ARDOUR;
163 using namespace ArdourWidgets;
164 using namespace ARDOUR_UI_UTILS;
167 using namespace Glib;
168 using namespace Gtkmm2ext;
169 using namespace Editing;
171 using PBD::internationalize;
173 using Gtkmm2ext::Keyboard;
175 double Editor::timebar_height = 15.0;
177 static const gchar *_grid_type_strings[] = {
186 N_("1/3 (8th triplet)"), // or "1/12" ?
187 N_("1/6 (16th triplet)"),
188 N_("1/12 (32nd triplet)"),
189 N_("1/24 (64th triplet)"),
190 N_("1/5 (8th quintuplet)"),
191 N_("1/10 (16th quintuplet)"),
192 N_("1/20 (32nd quintuplet)"),
193 N_("1/7 (8th septuplet)"),
194 N_("1/14 (16th septuplet)"),
195 N_("1/28 (32nd septuplet)"),
202 static const gchar *_edit_point_strings[] = {
209 static const gchar *_edit_mode_strings[] = {
217 static const gchar *_zoom_focus_strings[] = {
227 #ifdef USE_RUBBERBAND
228 static const gchar *_rb_opt_strings[] = {
231 N_("Balanced multitimbral mixture"),
232 N_("Unpitched percussion with stable notes"),
233 N_("Crisp monophonic instrumental"),
234 N_("Unpitched solo percussion"),
235 N_("Resample without preserving pitch"),
240 /* Robin says: this should be odd to accomodate cairo drawing offset (width/2 rounds up to pixel boundary) */
242 #define COMBO_TRIANGLE_WIDTH 19 // ArdourButton _diameter (11) + 2 * arrow-padding (2*2) + 2 * text-padding (2*5)
244 #define COMBO_TRIANGLE_WIDTH 11 // as-measured for win/linux.
248 : PublicEditor (global_hpacker)
249 , editor_mixer_strip_width (Wide)
250 , constructed (false)
251 , _playlist_selector (0)
253 , no_save_visual (false)
254 , _leftmost_sample (0)
255 , samples_per_pixel (2048)
256 , zoom_focus (ZoomFocusPlayhead)
257 , mouse_mode (MouseObject)
258 , pre_internal_grid_type (GridTypeBeat)
259 , pre_internal_snap_mode (SnapOff)
260 , internal_grid_type (GridTypeBeat)
261 , internal_snap_mode (SnapOff)
262 , _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
263 , _notebook_shrunk (false)
264 , location_marker_color (0)
265 , location_range_color (0)
266 , location_loop_color (0)
267 , location_punch_color (0)
268 , location_cd_marker_color (0)
270 , _show_marker_lines (false)
271 , clicked_axisview (0)
272 , clicked_routeview (0)
273 , clicked_regionview (0)
274 , clicked_selection (0)
275 , clicked_control_point (0)
276 , button_release_can_deselect (true)
277 , _mouse_changed_selection (false)
278 , _popup_region_menu_item (0)
280 , _track_canvas_viewport (0)
281 , within_track_canvas (false)
282 , _verbose_cursor (0)
286 , range_marker_group (0)
287 , transport_marker_group (0)
288 , cd_marker_group (0)
289 , _time_markers_group (0)
290 , hv_scroll_group (0)
292 , cursor_scroll_group (0)
293 , no_scroll_group (0)
294 , _trackview_group (0)
295 , _drag_motion_group (0)
296 , _canvas_drop_zone (0)
297 , no_ruler_shown_update (false)
298 , ruler_grabbed_widget (0)
300 , minsec_mark_interval (0)
301 , minsec_mark_modulo (0)
303 , timecode_ruler_scale (timecode_show_many_hours)
304 , timecode_mark_modulo (0)
305 , timecode_nmarks (0)
306 , _samples_ruler_interval (0)
307 , bbt_ruler_scale (bbt_show_many)
310 , bbt_bar_helper_on (0)
311 , bbt_accent_modulo (0)
316 , visible_timebars (0)
317 , editor_ruler_menu (0)
321 , range_marker_bar (0)
322 , transport_marker_bar (0)
324 , minsec_label (_("Mins:Secs"))
325 , bbt_label (_("Bars:Beats"))
326 , timecode_label (_("Timecode"))
327 , samples_label (_("Samples"))
328 , tempo_label (_("Tempo"))
329 , meter_label (_("Meter"))
330 , mark_label (_("Location Markers"))
331 , range_mark_label (_("Range Markers"))
332 , transport_mark_label (_("Loop/Punch Ranges"))
333 , cd_mark_label (_("CD Markers"))
334 , videotl_label (_("Video Timeline"))
337 , playhead_cursor (0)
338 , _region_boundary_cache_dirty (true)
339 , edit_packer (4, 4, true)
340 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
341 , horizontal_adjustment (0.0, 0.0, 1e16)
342 , unused_adjustment (0.0, 0.0, 10.0, 400.0)
343 , controls_layout (unused_adjustment, vertical_adjustment)
344 , _scroll_callbacks (0)
345 , _visible_canvas_width (0)
346 , _visible_canvas_height (0)
347 , _full_canvas_height (0)
348 , edit_controls_left_menu (0)
349 , edit_controls_right_menu (0)
350 , visual_change_queued(false)
351 , _last_update_time (0)
352 , _err_screen_engine (0)
353 , cut_buffer_start (0)
354 , cut_buffer_length (0)
355 , button_bindings (0)
356 , last_paste_pos (-1)
359 , current_interthread_info (0)
360 , analysis_window (0)
361 , select_new_marker (false)
363 , scrubbing_direction (0)
364 , scrub_reversals (0)
365 , scrub_reverse_distance (0)
366 , have_pending_keyboard_selection (false)
367 , pending_keyboard_selection_start (0)
368 , _grid_type (GridTypeBeat)
369 , _snap_mode (SnapOff)
370 , ignore_gui_changes (false)
371 , _drags (new DragManager (this))
373 /* , last_event_time { 0, 0 } */ /* this initialization style requires C++11 */
374 , _dragging_playhead (false)
375 , _dragging_edit_point (false)
376 , _follow_playhead (true)
377 , _stationary_playhead (false)
380 , global_rect_group (0)
381 , time_line_group (0)
382 , tempo_marker_menu (0)
383 , meter_marker_menu (0)
385 , range_marker_menu (0)
386 , new_transport_marker_menu (0)
387 , marker_menu_item (0)
388 , bbt_beat_subdivision (4)
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 , cd_marker_bar_drag_rect (0)
401 , range_bar_drag_rect (0)
402 , transport_bar_drag_rect (0)
403 , transport_bar_range_rect (0)
404 , transport_bar_preroll_rect (0)
405 , transport_bar_postroll_rect (0)
406 , transport_loop_range_rect (0)
407 , transport_punch_range_rect (0)
408 , transport_punchin_line (0)
409 , transport_punchout_line (0)
410 , transport_preroll_rect (0)
411 , transport_postroll_rect (0)
413 , rubberband_rect (0)
419 , autoscroll_horizontal_allowed (false)
420 , autoscroll_vertical_allowed (false)
422 , autoscroll_widget (0)
423 , show_gain_after_trim (false)
424 , selection_op_cmd_depth (0)
425 , selection_op_history_it (0)
426 , no_save_instant (false)
428 , current_mixer_strip (0)
429 , show_editor_mixer_when_tracks_arrive (false)
430 , nudge_clock (new AudioClock (X_("nudge"), false, X_("nudge"), true, false, true))
431 , current_stepping_trackview (0)
432 , last_track_height_step_timestamp (0)
434 , entered_regionview (0)
435 , clear_entered_track (false)
436 , _edit_point (EditAtMouse)
437 , meters_running (false)
439 , _have_idled (false)
440 , resize_idle_id (-1)
441 , _pending_resize_amount (0)
442 , _pending_resize_view (0)
443 , _pending_locate_request (false)
444 , _pending_initial_locate (false)
448 , layering_order_editor (0)
449 , _last_cut_copy_source_track (0)
450 , _region_selection_change_updates_region_list (true)
452 , _following_mixer_selection (false)
453 , _control_point_toggled_on_press (false)
454 , _stepping_axis_view (0)
455 , quantize_dialog (0)
456 , _main_menu_disabler (0)
458 /* we are a singleton */
460 PublicEditor::_instance = this;
464 last_event_time.tv_sec = 0;
465 last_event_time.tv_usec = 0;
467 selection_op_history.clear();
470 grid_type_strings = I18N (_grid_type_strings);
471 zoom_focus_strings = I18N (_zoom_focus_strings);
472 edit_mode_strings = I18N (_edit_mode_strings);
473 edit_point_strings = I18N (_edit_point_strings);
474 #ifdef USE_RUBBERBAND
475 rb_opt_strings = I18N (_rb_opt_strings);
479 build_edit_mode_menu();
480 build_zoom_focus_menu();
481 build_track_count_menu();
482 build_grid_type_menu();
483 build_edit_point_menu();
485 location_marker_color = UIConfiguration::instance().color ("location marker");
486 location_range_color = UIConfiguration::instance().color ("location range");
487 location_cd_marker_color = UIConfiguration::instance().color ("location cd marker");
488 location_loop_color = UIConfiguration::instance().color ("location loop");
489 location_punch_color = UIConfiguration::instance().color ("location punch");
491 timebar_height = std::max (12., ceil (15. * UIConfiguration::instance().get_ui_scale()));
493 TimeAxisView::setup_sizes ();
494 ArdourMarker::setup_sizes (timebar_height);
495 TempoCurve::setup_sizes (timebar_height);
497 bbt_label.set_name ("EditorRulerLabel");
498 bbt_label.set_size_request (-1, (int)timebar_height);
499 bbt_label.set_alignment (1.0, 0.5);
500 bbt_label.set_padding (5,0);
502 bbt_label.set_no_show_all();
503 minsec_label.set_name ("EditorRulerLabel");
504 minsec_label.set_size_request (-1, (int)timebar_height);
505 minsec_label.set_alignment (1.0, 0.5);
506 minsec_label.set_padding (5,0);
507 minsec_label.hide ();
508 minsec_label.set_no_show_all();
509 timecode_label.set_name ("EditorRulerLabel");
510 timecode_label.set_size_request (-1, (int)timebar_height);
511 timecode_label.set_alignment (1.0, 0.5);
512 timecode_label.set_padding (5,0);
513 timecode_label.hide ();
514 timecode_label.set_no_show_all();
515 samples_label.set_name ("EditorRulerLabel");
516 samples_label.set_size_request (-1, (int)timebar_height);
517 samples_label.set_alignment (1.0, 0.5);
518 samples_label.set_padding (5,0);
519 samples_label.hide ();
520 samples_label.set_no_show_all();
522 tempo_label.set_name ("EditorRulerLabel");
523 tempo_label.set_size_request (-1, (int)timebar_height);
524 tempo_label.set_alignment (1.0, 0.5);
525 tempo_label.set_padding (5,0);
527 tempo_label.set_no_show_all();
529 meter_label.set_name ("EditorRulerLabel");
530 meter_label.set_size_request (-1, (int)timebar_height);
531 meter_label.set_alignment (1.0, 0.5);
532 meter_label.set_padding (5,0);
534 meter_label.set_no_show_all();
536 mark_label.set_name ("EditorRulerLabel");
537 mark_label.set_size_request (-1, (int)timebar_height);
538 mark_label.set_alignment (1.0, 0.5);
539 mark_label.set_padding (5,0);
541 mark_label.set_no_show_all();
543 cd_mark_label.set_name ("EditorRulerLabel");
544 cd_mark_label.set_size_request (-1, (int)timebar_height);
545 cd_mark_label.set_alignment (1.0, 0.5);
546 cd_mark_label.set_padding (5,0);
547 cd_mark_label.hide();
548 cd_mark_label.set_no_show_all();
550 videotl_bar_height = 4;
551 videotl_label.set_name ("EditorRulerLabel");
552 videotl_label.set_size_request (-1, (int)timebar_height * videotl_bar_height);
553 videotl_label.set_alignment (1.0, 0.5);
554 videotl_label.set_padding (5,0);
555 videotl_label.hide();
556 videotl_label.set_no_show_all();
558 range_mark_label.set_name ("EditorRulerLabel");
559 range_mark_label.set_size_request (-1, (int)timebar_height);
560 range_mark_label.set_alignment (1.0, 0.5);
561 range_mark_label.set_padding (5,0);
562 range_mark_label.hide();
563 range_mark_label.set_no_show_all();
565 transport_mark_label.set_name ("EditorRulerLabel");
566 transport_mark_label.set_size_request (-1, (int)timebar_height);
567 transport_mark_label.set_alignment (1.0, 0.5);
568 transport_mark_label.set_padding (5,0);
569 transport_mark_label.hide();
570 transport_mark_label.set_no_show_all();
572 initialize_canvas ();
574 CairoWidget::set_focus_handler (sigc::mem_fun (ARDOUR_UI::instance(), &ARDOUR_UI::reset_focus));
576 _summary = new EditorSummary (this);
578 selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
579 selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
581 editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
583 selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
584 selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
586 edit_controls_vbox.set_spacing (0);
587 vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
588 _track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
590 HBox* h = manage (new HBox);
591 _group_tabs = new EditorGroupTabs (this);
592 h->pack_start (*_group_tabs, PACK_SHRINK);
593 h->pack_start (edit_controls_vbox);
594 controls_layout.add (*h);
596 controls_layout.set_name ("EditControlsBase");
597 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK|Gdk::SCROLL_MASK);
598 controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
599 controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
601 _cursors = new MouseCursors;
602 _cursors->set_cursor_set (UIConfiguration::instance().get_icon_set());
603 cerr << "Set cursor set to " << UIConfiguration::instance().get_icon_set() << endl;
605 /* Push default cursor to ever-present bottom of cursor stack. */
606 push_canvas_cursor(_cursors->grabber);
608 ArdourCanvas::GtkCanvas* time_pad = manage (new ArdourCanvas::GtkCanvas ());
610 ArdourCanvas::Line* pad_line_1 = new ArdourCanvas::Line (time_pad->root());
611 pad_line_1->set (ArdourCanvas::Duple (0.0, 1.0), ArdourCanvas::Duple (100.0, 1.0));
612 pad_line_1->set_outline_color (0xFF0000FF);
618 edit_packer.set_col_spacings (0);
619 edit_packer.set_row_spacings (0);
620 edit_packer.set_homogeneous (false);
621 edit_packer.set_border_width (0);
622 edit_packer.set_name ("EditorWindow");
624 time_bars_event_box.add (time_bars_vbox);
625 time_bars_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
626 time_bars_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
628 ArdourWidgets::ArdourDropShadow *axis_view_shadow = manage (new (ArdourWidgets::ArdourDropShadow));
629 axis_view_shadow->set_size_request (4, -1);
630 axis_view_shadow->set_name("EditorWindow");
631 axis_view_shadow->show();
633 edit_packer.attach (*axis_view_shadow, 0, 1, 0, 2, FILL, FILL|EXPAND, 0, 0);
635 /* labels for the time bars */
636 edit_packer.attach (time_bars_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
638 edit_packer.attach (controls_layout, 1, 2, 1, 2, FILL, FILL|EXPAND, 0, 0);
640 edit_packer.attach (*_track_canvas_viewport, 2, 3, 0, 2, FILL|EXPAND, FILL|EXPAND, 0, 0);
642 bottom_hbox.set_border_width (2);
643 bottom_hbox.set_spacing (3);
645 PresentationInfo::Change.connect (*this, MISSING_INVALIDATOR, boost::bind (&Editor::presentation_info_changed, this, _1), gui_context());
647 _route_groups = new EditorRouteGroups (this);
648 _routes = new EditorRoutes (this);
649 _regions = new EditorRegions (this);
650 _sources = new EditorSources (this);
651 _snapshots = new EditorSnapshots (this);
652 _locations = new EditorLocations (this);
653 _time_info_box = new TimeInfoBox ("EditorTimeInfo", true);
655 /* these are static location signals */
657 Location::start_changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context());
658 Location::end_changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context());
659 Location::changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context());
661 add_notebook_page (_("Tracks & Busses"), _routes->widget ());
662 add_notebook_page (_("Sources"), _sources->widget ());
663 add_notebook_page (_("Regions"), _regions->widget ());
664 add_notebook_page (_("Snapshots"), _snapshots->widget ());
665 add_notebook_page (_("Track & Bus Groups"), _route_groups->widget ());
666 add_notebook_page (_("Ranges & Marks"), _locations->widget ());
668 _the_notebook.set_show_tabs (true);
669 _the_notebook.set_scrollable (true);
670 _the_notebook.popup_disable ();
671 _the_notebook.set_tab_pos (Gtk::POS_RIGHT);
672 _the_notebook.show_all ();
674 _notebook_shrunk = false;
677 /* Pick up some settings we need to cache, early */
679 XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
682 settings->get_property ("notebook-shrunk", _notebook_shrunk);
685 editor_summary_pane.set_check_divider_position (true);
686 editor_summary_pane.add (edit_packer);
688 Button* summary_arrow_left = manage (new Button);
689 summary_arrow_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
690 summary_arrow_left->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), LEFT)));
691 summary_arrow_left->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
693 Button* summary_arrow_right = manage (new Button);
694 summary_arrow_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
695 summary_arrow_right->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), RIGHT)));
696 summary_arrow_right->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
698 VBox* summary_arrows_left = manage (new VBox);
699 summary_arrows_left->pack_start (*summary_arrow_left);
701 VBox* summary_arrows_right = manage (new VBox);
702 summary_arrows_right->pack_start (*summary_arrow_right);
704 Frame* summary_frame = manage (new Frame);
705 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
707 summary_frame->add (*_summary);
708 summary_frame->show ();
710 _summary_hbox.pack_start (*summary_arrows_left, false, false);
711 _summary_hbox.pack_start (*summary_frame, true, true);
712 _summary_hbox.pack_start (*summary_arrows_right, false, false);
714 editor_summary_pane.add (_summary_hbox);
715 edit_pane.set_check_divider_position (true);
716 edit_pane.add (editor_summary_pane);
717 _editor_list_vbox.pack_start (*_time_info_box, false, false, 0);
718 _editor_list_vbox.pack_start (_the_notebook);
719 edit_pane.add (_editor_list_vbox);
720 edit_pane.set_child_minsize (_editor_list_vbox, 30); /* rough guess at width of notebook tabs */
722 edit_pane.set_drag_cursor (*_cursors->expand_left_right);
723 editor_summary_pane.set_drag_cursor (*_cursors->expand_up_down);
726 if (!settings || !settings->get_property ("edit-horizontal-pane-pos", fract) || fract > 1.0) {
727 /* initial allocation is 90% to canvas, 10% to notebook */
730 edit_pane.set_divider (0, fract);
732 if (!settings || !settings->get_property ("edit-vertical-pane-pos", fract) || fract > 1.0) {
733 /* initial allocation is 90% to canvas, 10% to summary */
736 editor_summary_pane.set_divider (0, fract);
738 global_vpacker.set_spacing (0);
739 global_vpacker.set_border_width (0);
741 /* the next three EventBoxes provide the ability for their child widgets to have a background color. That is all. */
743 Gtk::EventBox* ebox = manage (new Gtk::EventBox); // a themeable box
744 ebox->set_name("EditorWindow");
745 ebox->add (ebox_hpacker);
747 Gtk::EventBox* epane_box = manage (new EventBoxExt); // a themeable box
748 epane_box->set_name("EditorWindow");
749 epane_box->add (edit_pane);
751 Gtk::EventBox* epane_box2 = manage (new EventBoxExt); // a themeable box
752 epane_box2->set_name("EditorWindow");
753 epane_box2->add (global_vpacker);
755 ArdourWidgets::ArdourDropShadow *toolbar_shadow = manage (new (ArdourWidgets::ArdourDropShadow));
756 toolbar_shadow->set_size_request (-1, 4);
757 toolbar_shadow->set_mode(ArdourWidgets::ArdourDropShadow::DropShadowBoth);
758 toolbar_shadow->set_name("EditorWindow");
759 toolbar_shadow->show();
761 global_vpacker.pack_start (*toolbar_shadow, false, false);
762 global_vpacker.pack_start (*ebox, false, false);
763 global_vpacker.pack_start (*epane_box, true, true);
764 global_hpacker.pack_start (*epane_box2, true, true);
766 /* need to show the "contents" widget so that notebook will show if tab is switched to
769 global_hpacker.show ();
773 /* register actions now so that set_state() can find them and set toggles/checks etc */
780 _playlist_selector = new PlaylistSelector();
781 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
783 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), boost::bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
787 nudge_forward_button.set_name ("nudge button");
788 nudge_forward_button.set_icon(ArdourIcon::NudgeRight);
790 nudge_backward_button.set_name ("nudge button");
791 nudge_backward_button.set_icon(ArdourIcon::NudgeLeft);
793 fade_context_menu.set_name ("ArdourContextMenu");
795 Gtkmm2ext::Keyboard::the_keyboard().ZoomVerticalModifierReleased.connect (sigc::mem_fun (*this, &Editor::zoom_vertical_modifier_released));
797 /* allow external control surfaces/protocols to do various things */
799 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
800 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
801 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
802 ControlProtocol::Undo.connect (*this, invalidator (*this), boost::bind (&Editor::undo, this, true), gui_context());
803 ControlProtocol::Redo.connect (*this, invalidator (*this), boost::bind (&Editor::redo, this, true), gui_context());
804 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), boost::bind (&Editor::control_scroll, this, _1), gui_context());
805 ControlProtocol::StepTracksUp.connect (*this, invalidator (*this), boost::bind (&Editor::control_step_tracks_up, this), gui_context());
806 ControlProtocol::StepTracksDown.connect (*this, invalidator (*this), boost::bind (&Editor::control_step_tracks_down, this), gui_context());
807 ControlProtocol::GotoView.connect (*this, invalidator (*this), boost::bind (&Editor::control_view, this, _1), gui_context());
808 ControlProtocol::CloseDialog.connect (*this, invalidator (*this), Keyboard::close_current_dialog, gui_context());
809 ControlProtocol::VerticalZoomInAll.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_in_all, this), gui_context());
810 ControlProtocol::VerticalZoomOutAll.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_out_all, this), gui_context());
811 ControlProtocol::VerticalZoomInSelected.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_in_selected, this), gui_context());
812 ControlProtocol::VerticalZoomOutSelected.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_out_selected, this), gui_context());
814 ControlProtocol::AddStripableToSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Add), gui_context());
815 ControlProtocol::RemoveStripableFromSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Toggle), gui_context());
816 ControlProtocol::SetStripableSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Set), gui_context());
817 ControlProtocol::ToggleStripableSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Toggle), gui_context());
818 ControlProtocol::ClearStripableSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_unselect, this), gui_context());
820 BasicUI::AccessAction.connect (*this, invalidator (*this), boost::bind (&Editor::access_action, this, _1, _2), gui_context());
824 ARDOUR_UI::instance()->Escape.connect (*this, invalidator (*this), boost::bind (&Editor::escape, this), gui_context());
826 /* problematic: has to return a value and thus cannot be x-thread */
828 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
830 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&Editor::parameter_changed, this, _1), gui_context());
831 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &Editor::ui_parameter_changed));
833 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
835 _ignore_region_action = false;
836 _last_region_menu_was_main = false;
838 _show_marker_lines = false;
840 /* Button bindings */
842 button_bindings = new Bindings ("editor-mouse");
844 XMLNode* node = button_settings();
846 for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
847 button_bindings->load_operation (**i);
853 /* grab current parameter state */
854 boost::function<void (string)> pc (boost::bind (&Editor::ui_parameter_changed, this, _1));
855 UIConfiguration::instance().map_parameters (pc);
857 setup_fade_images ();
859 set_grid_to (GridTypeNone);
864 delete tempo_marker_menu;
865 delete meter_marker_menu;
867 delete range_marker_menu;
868 delete new_transport_marker_menu;
869 delete editor_ruler_menu;
870 delete _popup_region_menu_item;
872 delete button_bindings;
874 delete _route_groups;
875 delete _track_canvas_viewport;
878 delete _verbose_cursor;
879 delete quantize_dialog;
885 delete _playlist_selector;
886 delete _time_info_box;
891 LuaInstance::destroy_instance ();
893 for (list<XMLNode *>::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) {
896 for (std::map<ARDOUR::FadeShape, Gtk::Image*>::const_iterator i = _xfade_in_images.begin(); i != _xfade_in_images.end (); ++i) {
899 for (std::map<ARDOUR::FadeShape, Gtk::Image*>::const_iterator i = _xfade_out_images.begin(); i != _xfade_out_images.end (); ++i) {
905 Editor::button_settings () const
907 XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
908 XMLNode* node = find_named_node (*settings, X_("Buttons"));
911 node = new XMLNode (X_("Buttons"));
918 Editor::get_smart_mode () const
920 return ((current_mouse_mode() == MouseObject) && smart_mode_action->get_active());
924 Editor::catch_vanishing_regionview (RegionView *rv)
926 /* note: the selection will take care of the vanishing
927 audioregionview by itself.
930 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
934 if (clicked_regionview == rv) {
935 clicked_regionview = 0;
938 if (entered_regionview == rv) {
939 set_entered_regionview (0);
942 if (!_all_region_actions_sensitized) {
943 sensitize_all_region_actions (true);
948 Editor::set_entered_regionview (RegionView* rv)
950 if (rv == entered_regionview) {
954 if (entered_regionview) {
955 entered_regionview->exited ();
958 entered_regionview = rv;
960 if (entered_regionview != 0) {
961 entered_regionview->entered ();
964 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
965 /* This RegionView entry might have changed what region actions
966 are allowed, so sensitize them all in case a key is pressed.
968 sensitize_all_region_actions (true);
973 Editor::set_entered_track (TimeAxisView* tav)
976 entered_track->exited ();
982 entered_track->entered ();
987 Editor::instant_save ()
989 if (!constructed || !_session || no_save_instant) {
993 _session->add_instant_xml(get_state());
997 Editor::control_vertical_zoom_in_all ()
999 tav_zoom_smooth (false, true);
1003 Editor::control_vertical_zoom_out_all ()
1005 tav_zoom_smooth (true, true);
1009 Editor::control_vertical_zoom_in_selected ()
1011 tav_zoom_smooth (false, false);
1015 Editor::control_vertical_zoom_out_selected ()
1017 tav_zoom_smooth (true, false);
1021 Editor::control_view (uint32_t view)
1023 goto_visual_state (view);
1027 Editor::control_unselect ()
1029 selection->clear_tracks ();
1033 Editor::control_select (boost::shared_ptr<Stripable> s, Selection::Operation op)
1035 TimeAxisView* tav = time_axis_view_from_stripable (s);
1039 case Selection::Add:
1040 selection->add (tav);
1042 case Selection::Toggle:
1043 selection->toggle (tav);
1045 case Selection::Extend:
1047 case Selection::Set:
1048 selection->set (tav);
1052 selection->clear_tracks ();
1057 Editor::control_step_tracks_up ()
1059 scroll_tracks_up_line ();
1063 Editor::control_step_tracks_down ()
1065 scroll_tracks_down_line ();
1069 Editor::control_scroll (float fraction)
1071 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
1077 double step = fraction * current_page_samples();
1080 _control_scroll_target is an optional<T>
1082 it acts like a pointer to an samplepos_t, with
1083 a operator conversion to boolean to check
1084 that it has a value could possibly use
1085 playhead_cursor->current_sample to store the
1086 value and a boolean in the class to know
1087 when it's out of date
1090 if (!_control_scroll_target) {
1091 _control_scroll_target = _session->transport_sample();
1092 _dragging_playhead = true;
1095 if ((fraction < 0.0f) && (*_control_scroll_target <= (samplepos_t) fabs(step))) {
1096 *_control_scroll_target = 0;
1097 } else if ((fraction > 0.0f) && (max_samplepos - *_control_scroll_target < step)) {
1098 *_control_scroll_target = max_samplepos - (current_page_samples()*2); // allow room for slop in where the PH is on the screen
1100 *_control_scroll_target += (samplepos_t) trunc (step);
1103 /* move visuals, we'll catch up with it later */
1105 playhead_cursor->set_position (*_control_scroll_target);
1106 UpdateAllTransportClocks (*_control_scroll_target);
1108 if (*_control_scroll_target > (current_page_samples() / 2)) {
1109 /* try to center PH in window */
1110 reset_x_origin (*_control_scroll_target - (current_page_samples()/2));
1116 Now we do a timeout to actually bring the session to the right place
1117 according to the playhead. This is to avoid reading disk buffers on every
1118 call to control_scroll, which is driven by ScrollTimeline and therefore
1119 probably by a control surface wheel which can generate lots of events.
1121 /* cancel the existing timeout */
1123 control_scroll_connection.disconnect ();
1125 /* add the next timeout */
1127 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
1131 Editor::deferred_control_scroll (samplepos_t /*target*/)
1133 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
1134 /* reset for next stream */
1135 _control_scroll_target = boost::none;
1136 _dragging_playhead = false;
1141 Editor::access_action (const std::string& action_group, const std::string& action_item)
1147 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
1151 act = ActionManager::get_action (action_group.c_str(), action_item.c_str());
1155 } catch ( ActionManager::MissingActionException const& e) {
1156 cerr << "MissingActionException:" << e.what () << endl;
1161 Editor::set_toggleaction (const std::string& action_group, const std::string& action_item, bool s)
1163 ActionManager::set_toggleaction_state (action_group.c_str(), action_item.c_str(), s);
1167 Editor::on_realize ()
1171 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
1172 start_lock_event_timing ();
1177 Editor::start_lock_event_timing ()
1179 /* check if we should lock the GUI every 30 seconds */
1181 Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::lock_timeout_callback), 30 * 1000);
1185 Editor::generic_event_handler (GdkEvent* ev)
1188 case GDK_BUTTON_PRESS:
1189 case GDK_BUTTON_RELEASE:
1190 case GDK_MOTION_NOTIFY:
1192 case GDK_KEY_RELEASE:
1193 if (contents().is_mapped()) {
1194 gettimeofday (&last_event_time, 0);
1198 case GDK_LEAVE_NOTIFY:
1199 switch (ev->crossing.detail) {
1200 case GDK_NOTIFY_UNKNOWN:
1201 case GDK_NOTIFY_INFERIOR:
1202 case GDK_NOTIFY_ANCESTOR:
1204 case GDK_NOTIFY_VIRTUAL:
1205 case GDK_NOTIFY_NONLINEAR:
1206 case GDK_NOTIFY_NONLINEAR_VIRTUAL:
1207 /* leaving window, so reset focus, thus ending any and
1208 all text entry operations.
1210 ARDOUR_UI::instance()->reset_focus (&contents());
1223 Editor::lock_timeout_callback ()
1225 struct timeval now, delta;
1227 gettimeofday (&now, 0);
1229 timersub (&now, &last_event_time, &delta);
1231 if (delta.tv_sec > (time_t) UIConfiguration::instance().get_lock_gui_after_seconds()) {
1233 /* don't call again. Returning false will effectively
1234 disconnect us from the timer callback.
1236 unlock() will call start_lock_event_timing() to get things
1246 Editor::map_position_change (samplepos_t sample)
1248 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, sample)
1250 if (_session == 0) {
1254 if (_follow_playhead) {
1255 center_screen (sample);
1258 playhead_cursor->set_position (sample);
1262 Editor::center_screen (samplepos_t sample)
1264 samplecnt_t const page = _visible_canvas_width * samples_per_pixel;
1266 /* if we're off the page, then scroll.
1269 if (sample < _leftmost_sample || sample >= _leftmost_sample + page) {
1270 center_screen_internal (sample, page);
1275 Editor::center_screen_internal (samplepos_t sample, float page)
1279 if (sample > page) {
1280 sample -= (samplepos_t) page;
1285 reset_x_origin (sample);
1290 Editor::update_title ()
1292 ENSURE_GUI_THREAD (*this, &Editor::update_title);
1294 if (!own_window()) {
1299 bool dirty = _session->dirty();
1301 string session_name;
1303 if (_session->snap_name() != _session->name()) {
1304 session_name = _session->snap_name();
1306 session_name = _session->name();
1310 session_name = "*" + session_name;
1313 WindowTitle title(session_name);
1314 title += S_("Window|Editor");
1315 title += Glib::get_application_name();
1316 own_window()->set_title (title.get_string());
1318 /* ::session_going_away() will have taken care of it */
1323 Editor::set_session (Session *t)
1325 SessionHandlePtr::set_session (t);
1331 /* initialize _leftmost_sample to the extents of the session
1332 * this prevents a bogus setting of leftmost = "0" if the summary view asks for the leftmost sample
1333 * before the visible state has been loaded from instant.xml */
1334 _leftmost_sample = session_gui_extents().first;
1336 _playlist_selector->set_session (_session);
1337 nudge_clock->set_session (_session);
1338 _summary->set_session (_session);
1339 _group_tabs->set_session (_session);
1340 _route_groups->set_session (_session);
1341 _regions->set_session (_session);
1342 _sources->set_session (_session);
1343 _snapshots->set_session (_session);
1344 _routes->set_session (_session);
1345 _locations->set_session (_session);
1346 _time_info_box->set_session (_session);
1348 if (rhythm_ferret) {
1349 rhythm_ferret->set_session (_session);
1352 if (analysis_window) {
1353 analysis_window->set_session (_session);
1357 sfbrowser->set_session (_session);
1360 compute_fixed_ruler_scale ();
1362 /* Make sure we have auto loop and auto punch ranges */
1364 Location* loc = _session->locations()->auto_loop_location();
1366 loc->set_name (_("Loop"));
1369 loc = _session->locations()->auto_punch_location();
1372 loc->set_name (_("Punch"));
1375 refresh_location_display ();
1377 /* This must happen after refresh_location_display(), as (amongst other things) we restore
1378 * the selected Marker; this needs the LocationMarker list to be available.
1380 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1381 set_state (*node, Stateful::loading_state_version);
1383 /* catch up on selection state, etc. */
1386 sc.add (Properties::selected);
1387 presentation_info_changed (sc);
1389 /* catch up with the playhead */
1391 _session->request_locate (playhead_cursor->current_sample ());
1392 _pending_initial_locate = true;
1396 /* These signals can all be emitted by a non-GUI thread. Therefore the
1397 handlers for them must not attempt to directly interact with the GUI,
1398 but use PBD::Signal<T>::connect() which accepts an event loop
1399 ("context") where the handler will be asked to run.
1402 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::step_edit_status_change, this, _1), gui_context());
1403 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1404 _session->TransportLooped.connect (_session_connections, invalidator (*this), boost::bind (&Editor::transport_looped, this), gui_context());
1405 _session->PositionChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_position_change, this, _1), gui_context());
1406 _session->vca_manager().VCAAdded.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_vcas, this, _1), gui_context());
1407 _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_routes, this, _1), gui_context());
1408 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1409 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::tempo_map_changed, this, _1), gui_context());
1410 _session->tempo_map().MetricPositionChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::tempometric_position_changed, this, _1), gui_context());
1411 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1412 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::parameter_changed, this, _1), gui_context());
1413 _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Editor::session_state_saved, this, _1), gui_context());
1414 _session->locations()->added.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_new_location, this, _1), gui_context());
1415 _session->locations()->removed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::location_gone, this, _1), gui_context());
1416 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1417 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1419 playhead_cursor->track_canvas_item().reparent ((ArdourCanvas::Item*) get_cursor_scroll_group());
1420 playhead_cursor->show ();
1422 snapped_cursor->track_canvas_item().reparent ((ArdourCanvas::Item*) get_cursor_scroll_group());
1423 snapped_cursor->set_color (UIConfiguration::instance().color ("edit point"));
1424 snapped_cursor->show ();
1426 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1427 Config->map_parameters (pc);
1428 _session->config.map_parameters (pc);
1430 restore_ruler_visibility ();
1431 //tempo_map_changed (PropertyChange (0));
1432 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1434 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1435 (static_cast<TimeAxisView*>(*i))->set_samples_per_pixel (samples_per_pixel);
1438 super_rapid_screen_update_connection = Timers::super_rapid_connect (
1439 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1442 /* register for undo history */
1443 _session->register_with_memento_command_factory(id(), this);
1444 _session->register_with_memento_command_factory(_selection_memento->id(), _selection_memento);
1446 LuaInstance::instance()->set_session(_session);
1448 start_updating_meters ();
1452 Editor::fill_xfade_menu (Menu_Helpers::MenuList& items, bool start)
1454 using namespace Menu_Helpers;
1456 void (Editor::*emf)(FadeShape);
1457 std::map<ARDOUR::FadeShape,Gtk::Image*>* images;
1460 images = &_xfade_in_images;
1461 emf = &Editor::set_fade_in_shape;
1463 images = &_xfade_out_images;
1464 emf = &Editor::set_fade_out_shape;
1469 _("Linear (for highly correlated material)"),
1470 *(*images)[FadeLinear],
1471 sigc::bind (sigc::mem_fun (*this, emf), FadeLinear)
1475 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1479 _("Constant power"),
1480 *(*images)[FadeConstantPower],
1481 sigc::bind (sigc::mem_fun (*this, emf), FadeConstantPower)
1484 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1489 *(*images)[FadeSymmetric],
1490 sigc::bind (sigc::mem_fun (*this, emf), FadeSymmetric)
1494 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1499 *(*images)[FadeSlow],
1500 sigc::bind (sigc::mem_fun (*this, emf), FadeSlow)
1503 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1508 *(*images)[FadeFast],
1509 sigc::bind (sigc::mem_fun (*this, emf), FadeFast)
1512 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1515 /** Pop up a context menu for when the user clicks on a start crossfade */
1517 Editor::popup_xfade_in_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType /*item_type*/)
1519 using namespace Menu_Helpers;
1520 AudioRegionView* arv = dynamic_cast<AudioRegionView*> ((RegionView*)item->get_data ("regionview"));
1525 MenuList& items (xfade_in_context_menu.items());
1528 if (arv->audio_region()->fade_in_active()) {
1529 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1531 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1534 items.push_back (SeparatorElem());
1535 fill_xfade_menu (items, true);
1537 xfade_in_context_menu.popup (button, time);
1540 /** Pop up a context menu for when the user clicks on an end crossfade */
1542 Editor::popup_xfade_out_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType /*item_type*/)
1544 using namespace Menu_Helpers;
1545 AudioRegionView* arv = dynamic_cast<AudioRegionView*> ((RegionView*)item->get_data ("regionview"));
1550 MenuList& items (xfade_out_context_menu.items());
1553 if (arv->audio_region()->fade_out_active()) {
1554 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1556 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1559 items.push_back (SeparatorElem());
1560 fill_xfade_menu (items, false);
1562 xfade_out_context_menu.popup (button, time);
1566 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1568 using namespace Menu_Helpers;
1569 Menu* (Editor::*build_menu_function)();
1572 switch (item_type) {
1574 case RegionViewName:
1575 case RegionViewNameHighlight:
1576 case LeftFrameHandle:
1577 case RightFrameHandle:
1578 if (with_selection) {
1579 build_menu_function = &Editor::build_track_selection_context_menu;
1581 build_menu_function = &Editor::build_track_region_context_menu;
1586 if (with_selection) {
1587 build_menu_function = &Editor::build_track_selection_context_menu;
1589 build_menu_function = &Editor::build_track_context_menu;
1594 if (clicked_routeview->track()) {
1595 build_menu_function = &Editor::build_track_context_menu;
1597 build_menu_function = &Editor::build_track_bus_context_menu;
1602 /* probably shouldn't happen but if it does, we don't care */
1606 menu = (this->*build_menu_function)();
1607 menu->set_name ("ArdourContextMenu");
1609 /* now handle specific situations */
1611 switch (item_type) {
1613 case RegionViewName:
1614 case RegionViewNameHighlight:
1615 case LeftFrameHandle:
1616 case RightFrameHandle:
1626 /* probably shouldn't happen but if it does, we don't care */
1630 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1632 /* Bounce to disk */
1634 using namespace Menu_Helpers;
1635 MenuList& edit_items = menu->items();
1637 edit_items.push_back (SeparatorElem());
1639 switch (clicked_routeview->audio_track()->freeze_state()) {
1640 case AudioTrack::NoFreeze:
1641 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1644 case AudioTrack::Frozen:
1645 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1648 case AudioTrack::UnFrozen:
1649 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1655 if (item_type == StreamItem && clicked_routeview) {
1656 clicked_routeview->build_underlay_menu(menu);
1659 /* When the region menu is opened, we setup the actions so that they look right
1662 sensitize_the_right_region_actions (false);
1663 _last_region_menu_was_main = false;
1665 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1666 menu->popup (button, time);
1670 Editor::build_track_context_menu ()
1672 using namespace Menu_Helpers;
1674 MenuList& edit_items = track_context_menu.items();
1677 add_dstream_context_items (edit_items);
1678 return &track_context_menu;
1682 Editor::build_track_bus_context_menu ()
1684 using namespace Menu_Helpers;
1686 MenuList& edit_items = track_context_menu.items();
1689 add_bus_context_items (edit_items);
1690 return &track_context_menu;
1694 Editor::build_track_region_context_menu ()
1696 using namespace Menu_Helpers;
1697 MenuList& edit_items = track_region_context_menu.items();
1700 /* we've just cleared the track region context menu, so the menu that these
1701 two items were on will have disappeared; stop them dangling.
1703 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1706 boost::shared_ptr<Track> tr;
1707 boost::shared_ptr<Playlist> pl;
1709 if ((tr = rtv->track())) {
1710 add_region_context_items (edit_items, tr);
1714 add_dstream_context_items (edit_items);
1716 return &track_region_context_menu;
1720 Editor::loudness_analyze_region_selection ()
1725 Selection& s (PublicEditor::instance ().get_selection ());
1726 RegionSelection ars = s.regions;
1727 ARDOUR::AnalysisGraph ag (_session);
1728 samplecnt_t total_work = 0;
1730 for (RegionSelection::iterator j = ars.begin (); j != ars.end (); ++j) {
1731 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*j);
1735 if (!boost::dynamic_pointer_cast<AudioRegion> (arv->region ())) {
1738 assert (dynamic_cast<RouteTimeAxisView *> (&arv->get_time_axis_view ()));
1739 total_work += arv->region ()->length ();
1742 SimpleProgressDialog spd (_("Region Loudness Analysis"), sigc::mem_fun (ag, &AnalysisGraph::cancel));
1744 ag.set_total_samples (total_work);
1745 ag.Progress.connect_same_thread (c, boost::bind (&SimpleProgressDialog::update_progress, &spd, _1, _2));
1748 for (RegionSelection::iterator j = ars.begin (); j != ars.end (); ++j) {
1749 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*j);
1753 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (arv->region ());
1757 ag.analyze_region (ar);
1760 if (!ag.canceled ()) {
1761 ExportReport er (_("Audio Report/Analysis"), ag.results ());
1767 Editor::loudness_analyze_range_selection ()
1772 Selection& s (PublicEditor::instance ().get_selection ());
1773 TimeSelection ts = s.time;
1774 ARDOUR::AnalysisGraph ag (_session);
1775 samplecnt_t total_work = 0;
1777 for (TrackSelection::iterator i = s.tracks.begin (); i != s.tracks.end (); ++i) {
1778 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist> ((*i)->playlist ());
1782 RouteUI *rui = dynamic_cast<RouteUI *> (*i);
1786 for (std::list<AudioRange>::iterator j = ts.begin (); j != ts.end (); ++j) {
1787 total_work += j->length ();
1791 SimpleProgressDialog spd (_("Range Loudness Analysis"), sigc::mem_fun (ag, &AnalysisGraph::cancel));
1793 ag.set_total_samples (total_work);
1794 ag.Progress.connect_same_thread (c, boost::bind (&SimpleProgressDialog::update_progress, &spd, _1, _2));
1797 for (TrackSelection::iterator i = s.tracks.begin (); i != s.tracks.end (); ++i) {
1798 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist> ((*i)->playlist ());
1802 RouteUI *rui = dynamic_cast<RouteUI *> (*i);
1806 ag.analyze_range (rui->route (), pl, ts);
1809 if (!ag.canceled ()) {
1810 ExportReport er (_("Audio Report/Analysis"), ag.results ());
1816 Editor::spectral_analyze_region_selection ()
1818 if (analysis_window == 0) {
1819 analysis_window = new AnalysisWindow();
1822 analysis_window->set_session(_session);
1824 analysis_window->show_all();
1827 analysis_window->set_regionmode();
1828 analysis_window->analyze();
1830 analysis_window->present();
1834 Editor::spectral_analyze_range_selection()
1836 if (analysis_window == 0) {
1837 analysis_window = new AnalysisWindow();
1840 analysis_window->set_session(_session);
1842 analysis_window->show_all();
1845 analysis_window->set_rangemode();
1846 analysis_window->analyze();
1848 analysis_window->present();
1852 Editor::build_track_selection_context_menu ()
1854 using namespace Menu_Helpers;
1855 MenuList& edit_items = track_selection_context_menu.items();
1856 edit_items.clear ();
1858 add_selection_context_items (edit_items);
1859 // edit_items.push_back (SeparatorElem());
1860 // add_dstream_context_items (edit_items);
1862 return &track_selection_context_menu;
1866 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr<Track> track)
1868 using namespace Menu_Helpers;
1870 /* OK, stick the region submenu at the top of the list, and then add
1874 RegionSelection rs = get_regions_from_selection_and_entered ();
1876 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1878 if (_popup_region_menu_item == 0) {
1879 _popup_region_menu_item = new MenuItem (menu_item_name, false);
1880 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1881 _popup_region_menu_item->show ();
1883 _popup_region_menu_item->set_label (menu_item_name);
1886 /* No layering allowed in later is higher layering model */
1887 RefPtr<Action> act = ActionManager::get_action (X_("EditorMenu"), X_("RegionMenuLayering"));
1888 if (act && Config->get_layer_model() == LaterHigher) {
1889 act->set_sensitive (false);
1891 act->set_sensitive (true);
1894 const samplepos_t position = get_preferred_edit_position (EDIT_IGNORE_NONE, true);
1896 edit_items.push_back (*_popup_region_menu_item);
1897 if (Config->get_layer_model() == Manual && track->playlist()->count_regions_at (position) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1898 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region-context-menu")->create_menu_item ()));
1900 edit_items.push_back (SeparatorElem());
1903 /** Add context menu items relevant to selection ranges.
1904 * @param edit_items List to add the items to.
1907 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1909 using namespace Menu_Helpers;
1911 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1912 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1914 edit_items.push_back (SeparatorElem());
1915 edit_items.push_back (MenuElem (_("Zoom to Range"), sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_selection), Horizontal)));
1917 edit_items.push_back (SeparatorElem());
1918 edit_items.push_back (MenuElem (_("Loudness Analysis"), sigc::mem_fun(*this, &Editor::loudness_analyze_range_selection)));
1919 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::spectral_analyze_range_selection)));
1921 edit_items.push_back (SeparatorElem());
1923 edit_items.push_back (
1925 _("Move Range Start to Previous Region Boundary"),
1926 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, false)
1930 edit_items.push_back (
1932 _("Move Range Start to Next Region Boundary"),
1933 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, true)
1937 edit_items.push_back (
1939 _("Move Range End to Previous Region Boundary"),
1940 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, false)
1944 edit_items.push_back (
1946 _("Move Range End to Next Region Boundary"),
1947 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, true)
1951 edit_items.push_back (SeparatorElem());
1952 edit_items.push_back (MenuElem (_("Separate"), mem_fun(*this, &Editor::separate_region_from_selection)));
1953 // edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1955 edit_items.push_back (SeparatorElem());
1956 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1958 edit_items.push_back (SeparatorElem());
1959 edit_items.push_back (MenuElem (_("Set Loop from Selection"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1960 edit_items.push_back (MenuElem (_("Set Punch from Selection"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1961 edit_items.push_back (MenuElem (_("Set Session Start/End from Selection"), sigc::mem_fun(*this, &Editor::set_session_extents_from_selection)));
1963 edit_items.push_back (SeparatorElem());
1964 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1966 edit_items.push_back (SeparatorElem());
1967 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1968 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_range), false)));
1970 edit_items.push_back (SeparatorElem());
1971 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1972 edit_items.push_back (MenuElem (_("Consolidate Range with Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1973 edit_items.push_back (MenuElem (_("Bounce Range to Source List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1974 edit_items.push_back (MenuElem (_("Bounce Range to Source List with Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1975 edit_items.push_back (MenuElem (_("Export Range..."), sigc::mem_fun(*this, &Editor::export_selection)));
1976 if (ARDOUR_UI::instance()->video_timeline->get_duration() > 0) {
1977 edit_items.push_back (MenuElem (_("Export Video Range..."), sigc::bind (sigc::mem_fun(*(ARDOUR_UI::instance()), &ARDOUR_UI::export_video), true)));
1983 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1985 using namespace Menu_Helpers;
1989 Menu *play_menu = manage (new Menu);
1990 MenuList& play_items = play_menu->items();
1991 play_menu->set_name ("ArdourContextMenu");
1993 play_items.push_back (MenuElem (_("Play from Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1994 play_items.push_back (MenuElem (_("Play from Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1995 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1996 play_items.push_back (SeparatorElem());
1997 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1999 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2003 Menu *select_menu = manage (new Menu);
2004 MenuList& select_items = select_menu->items();
2005 select_menu->set_name ("ArdourContextMenu");
2007 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2008 select_items.push_back (MenuElem (_("Select All Objects"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_objects), Selection::Set)));
2009 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2010 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2011 select_items.push_back (SeparatorElem());
2012 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
2013 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
2014 select_items.push_back (MenuElem (_("Set Range to Selected Regions"), sigc::mem_fun(*this, &Editor::set_selection_from_region)));
2015 select_items.push_back (SeparatorElem());
2016 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true, true)));
2017 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false, true)));
2018 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2019 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2020 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
2021 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
2022 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
2024 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2028 Menu *cutnpaste_menu = manage (new Menu);
2029 MenuList& cutnpaste_items = cutnpaste_menu->items();
2030 cutnpaste_menu->set_name ("ArdourContextMenu");
2032 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2033 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2034 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f, true)));
2036 cutnpaste_items.push_back (SeparatorElem());
2038 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
2039 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
2041 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
2043 /* Adding new material */
2045 edit_items.push_back (SeparatorElem());
2046 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_source_list_selection), 1.0f)));
2047 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
2051 Menu *nudge_menu = manage (new Menu());
2052 MenuList& nudge_items = nudge_menu->items();
2053 nudge_menu->set_name ("ArdourContextMenu");
2055 edit_items.push_back (SeparatorElem());
2056 nudge_items.push_back (MenuElem (_("Nudge Entire Track Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2057 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2058 nudge_items.push_back (MenuElem (_("Nudge Entire Track Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2059 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2061 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2065 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
2067 using namespace Menu_Helpers;
2071 Menu *play_menu = manage (new Menu);
2072 MenuList& play_items = play_menu->items();
2073 play_menu->set_name ("ArdourContextMenu");
2075 play_items.push_back (MenuElem (_("Play from Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
2076 play_items.push_back (MenuElem (_("Play from Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
2077 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2081 Menu *select_menu = manage (new Menu);
2082 MenuList& select_items = select_menu->items();
2083 select_menu->set_name ("ArdourContextMenu");
2085 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2086 select_items.push_back (MenuElem (_("Select All Objects"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_objects), Selection::Set)));
2087 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2088 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2089 select_items.push_back (SeparatorElem());
2090 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true, true)));
2091 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false, true)));
2092 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2093 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2095 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2098 #if 0 // unused, why?
2099 Menu *cutnpaste_menu = manage (new Menu);
2100 MenuList& cutnpaste_items = cutnpaste_menu->items();
2101 cutnpaste_menu->set_name ("ArdourContextMenu");
2103 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2104 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2105 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f, true)));
2108 Menu *nudge_menu = manage (new Menu());
2109 MenuList& nudge_items = nudge_menu->items();
2110 nudge_menu->set_name ("ArdourContextMenu");
2112 edit_items.push_back (SeparatorElem());
2113 nudge_items.push_back (MenuElem (_("Nudge Entire Track Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2114 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2115 nudge_items.push_back (MenuElem (_("Nudge Entire Track Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2116 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2118 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2122 Editor::grid_type() const
2128 Editor::grid_musical() const
2130 switch (_grid_type) {
2131 case GridTypeBeatDiv32:
2132 case GridTypeBeatDiv28:
2133 case GridTypeBeatDiv24:
2134 case GridTypeBeatDiv20:
2135 case GridTypeBeatDiv16:
2136 case GridTypeBeatDiv14:
2137 case GridTypeBeatDiv12:
2138 case GridTypeBeatDiv10:
2139 case GridTypeBeatDiv8:
2140 case GridTypeBeatDiv7:
2141 case GridTypeBeatDiv6:
2142 case GridTypeBeatDiv5:
2143 case GridTypeBeatDiv4:
2144 case GridTypeBeatDiv3:
2145 case GridTypeBeatDiv2:
2150 case GridTypeTimecode:
2151 case GridTypeMinSec:
2152 case GridTypeCDFrame:
2159 Editor::grid_nonmusical() const
2161 switch (_grid_type) {
2162 case GridTypeTimecode:
2163 case GridTypeMinSec:
2164 case GridTypeCDFrame:
2166 case GridTypeBeatDiv32:
2167 case GridTypeBeatDiv28:
2168 case GridTypeBeatDiv24:
2169 case GridTypeBeatDiv20:
2170 case GridTypeBeatDiv16:
2171 case GridTypeBeatDiv14:
2172 case GridTypeBeatDiv12:
2173 case GridTypeBeatDiv10:
2174 case GridTypeBeatDiv8:
2175 case GridTypeBeatDiv7:
2176 case GridTypeBeatDiv6:
2177 case GridTypeBeatDiv5:
2178 case GridTypeBeatDiv4:
2179 case GridTypeBeatDiv3:
2180 case GridTypeBeatDiv2:
2189 Editor::snap_mode() const
2195 Editor::show_rulers_for_grid ()
2197 /* show appropriate rulers for this grid setting. */
2198 if (grid_musical()) {
2199 ruler_tempo_action->set_active(true);
2200 ruler_meter_action->set_active(true);
2201 ruler_bbt_action->set_active(true);
2203 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2204 ruler_timecode_action->set_active(false);
2205 ruler_minsec_action->set_active(false);
2206 ruler_samples_action->set_active(false);
2208 } else if (_grid_type == GridTypeTimecode) {
2209 ruler_timecode_action->set_active(true);
2211 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2212 ruler_tempo_action->set_active(false);
2213 ruler_meter_action->set_active(false);
2214 ruler_bbt_action->set_active(false);
2215 ruler_minsec_action->set_active(false);
2216 ruler_samples_action->set_active(false);
2218 } else if (_grid_type == GridTypeMinSec) {
2219 ruler_minsec_action->set_active(true);
2221 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2222 ruler_tempo_action->set_active(false);
2223 ruler_meter_action->set_active(false);
2224 ruler_bbt_action->set_active(false);
2225 ruler_timecode_action->set_active(false);
2226 ruler_samples_action->set_active(false);
2228 } else if (_grid_type == GridTypeCDFrame) {
2229 ruler_cd_marker_action->set_active(true);
2230 ruler_minsec_action->set_active(true);
2232 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2233 ruler_tempo_action->set_active(false);
2234 ruler_meter_action->set_active(false);
2235 ruler_bbt_action->set_active(false);
2236 ruler_timecode_action->set_active(false);
2237 ruler_samples_action->set_active(false);
2243 Editor::set_grid_to (GridType gt)
2245 if (_grid_type == gt) { // already set
2249 unsigned int grid_ind = (unsigned int)gt;
2251 if (internal_editing() && UIConfiguration::instance().get_grid_follows_internal()) {
2252 internal_grid_type = gt;
2254 pre_internal_grid_type = gt;
2259 if (grid_ind > grid_type_strings.size() - 1) {
2261 _grid_type = (GridType)grid_ind;
2264 string str = grid_type_strings[grid_ind];
2266 if (str != grid_type_selector.get_text()) {
2267 grid_type_selector.set_text (str);
2270 if (UIConfiguration::instance().get_show_grids_ruler()) {
2271 show_rulers_for_grid ();
2276 if (grid_musical()) {
2277 compute_bbt_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
2278 update_tempo_based_rulers ();
2281 mark_region_boundary_cache_dirty ();
2283 redisplay_grid (false);
2285 SnapChanged (); /* EMIT SIGNAL */
2289 Editor::set_snap_mode (SnapMode mode)
2291 if (internal_editing()) {
2292 internal_snap_mode = mode;
2294 pre_internal_snap_mode = mode;
2299 if (_snap_mode == SnapOff) {
2300 snap_mode_button.set_active_state (Gtkmm2ext::Off);
2302 snap_mode_button.set_active_state (Gtkmm2ext::ExplicitActive);
2309 Editor::set_edit_point_preference (EditPoint ep, bool force)
2311 if (Profile->get_mixbus()) {
2312 if (ep == EditAtSelectedMarker) {
2313 ep = EditAtPlayhead;
2317 bool changed = (_edit_point != ep);
2321 string str = edit_point_strings[(int)ep];
2322 if (str != edit_point_selector.get_text ()) {
2323 edit_point_selector.set_text (str);
2326 update_all_enter_cursors();
2328 if (!force && !changed) {
2332 const char* action=NULL;
2334 switch (_edit_point) {
2335 case EditAtPlayhead:
2336 action = "edit-at-playhead";
2338 case EditAtSelectedMarker:
2339 action = "edit-at-selected-marker";
2342 action = "edit-at-mouse";
2346 Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action ("Editor", action);
2347 tact->set_active (true);
2350 bool in_track_canvas;
2352 if (!mouse_sample (foo, in_track_canvas)) {
2353 in_track_canvas = false;
2356 reset_canvas_action_sensitivity (in_track_canvas);
2357 sensitize_the_right_region_actions (false);
2363 Editor::set_state (const XMLNode& node, int version)
2366 PBD::Unwinder<bool> nsi (no_save_instant, true);
2369 Tabbable::set_state (node, version);
2372 if (_session && node.get_property ("playhead", ph_pos)) {
2374 playhead_cursor->set_position (ph_pos);
2376 warning << _("Playhead position stored with a negative value - ignored (use zero instead)") << endmsg;
2377 playhead_cursor->set_position (0);
2380 playhead_cursor->set_position (0);
2383 node.get_property ("mixer-width", editor_mixer_strip_width);
2385 node.get_property ("zoom-focus", zoom_focus);
2386 zoom_focus_selection_done (zoom_focus);
2389 if (node.get_property ("zoom", z)) {
2390 /* older versions of ardour used floating point samples_per_pixel */
2391 reset_zoom (llrintf (z));
2393 reset_zoom (samples_per_pixel);
2397 if (node.get_property ("visible-track-count", cnt)) {
2398 set_visible_track_count (cnt);
2402 if (!node.get_property ("grid-type", grid_type)) {
2403 grid_type = _grid_type;
2405 set_grid_to (grid_type);
2408 if (node.get_property ("snap-mode", sm)) {
2409 snap_mode_selection_done(sm);
2410 /* set text of Dropdown. in case _snap_mode == SnapOff (default)
2411 * snap_mode_selection_done() will only mark an already active item as active
2412 * which does not trigger set_text().
2416 set_snap_mode (_snap_mode);
2419 node.get_property ("internal-grid-type", internal_grid_type);
2420 node.get_property ("internal-snap-mode", internal_snap_mode);
2421 node.get_property ("pre-internal-grid-type", pre_internal_grid_type);
2422 node.get_property ("pre-internal-snap-mode", pre_internal_snap_mode);
2425 if (node.get_property ("mouse-mode", mm_str)) {
2426 MouseMode m = str2mousemode(mm_str);
2427 set_mouse_mode (m, true);
2429 set_mouse_mode (MouseObject, true);
2433 if (node.get_property ("left-frame", lf_pos)) {
2437 reset_x_origin (lf_pos);
2441 if (node.get_property ("y-origin", y_origin)) {
2442 reset_y_origin (y_origin);
2446 node.get_property ("join-object-range", yn);
2448 RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("MouseMode"), X_("set-mouse-mode-object-range"));
2449 /* do it twice to force the change */
2450 tact->set_active (!yn);
2451 tact->set_active (yn);
2452 set_mouse_mode(mouse_mode, true);
2456 if (node.get_property ("edit-point", ep)) {
2457 set_edit_point_preference (ep, true);
2459 set_edit_point_preference (_edit_point);
2462 if (node.get_property ("follow-playhead", yn)) {
2463 set_follow_playhead (yn);
2466 if (node.get_property ("stationary-playhead", yn)) {
2467 set_stationary_playhead (yn);
2470 if (node.get_property ("show-editor-mixer", yn)) {
2472 Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Editor"), X_("show-editor-mixer"));
2473 /* do it twice to force the change */
2474 tact->set_active (!yn);
2475 tact->set_active (yn);
2479 node.get_property ("show-editor-list", yn);
2481 Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Editor"), X_("show-editor-list"));
2482 /* do it twice to force the change */
2483 tact->set_active (!yn);
2484 tact->set_active (yn);
2488 if (node.get_property (X_("editor-list-page"), el_page)) {
2489 _the_notebook.set_current_page (el_page);
2493 node.get_property (X_("show-marker-lines"), yn);
2495 Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Editor"), X_("show-marker-lines"));
2496 /* do it twice to force the change */
2497 tact->set_active (!yn);
2498 tact->set_active (yn);
2501 XMLNodeList children = node.children ();
2502 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2503 selection->set_state (**i, Stateful::current_state_version);
2504 _regions->set_state (**i);
2505 _locations->set_state (**i);
2508 if (node.get_property ("maximised", yn)) {
2509 Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Common"), X_("ToggleMaximalEditor"));
2510 bool fs = tact->get_active();
2512 ActionManager::do_action ("Common", "ToggleMaximalEditor");
2516 samplepos_t nudge_clock_value;
2517 if (node.get_property ("nudge-clock-value", nudge_clock_value)) {
2518 nudge_clock->set (nudge_clock_value);
2520 nudge_clock->set_mode (AudioClock::Timecode);
2521 nudge_clock->set (_session->sample_rate() * 5, true);
2526 * Not all properties may have been in XML, but
2527 * those that are linked to a private variable may need changing
2529 RefPtr<ToggleAction> tact;
2531 tact = ActionManager::get_toggle_action (X_("Editor"), X_("toggle-follow-playhead"));
2532 yn = _follow_playhead;
2533 if (tact->get_active() != yn) {
2534 tact->set_active (yn);
2537 tact = ActionManager::get_toggle_action (X_("Editor"), X_("toggle-stationary-playhead"));
2538 yn = _stationary_playhead;
2539 if (tact->get_active() != yn) {
2540 tact->set_active (yn);
2548 Editor::get_state ()
2550 XMLNode* node = new XMLNode (X_("Editor"));
2552 node->set_property ("id", id().to_s ());
2554 node->add_child_nocopy (Tabbable::get_state());
2556 node->set_property("edit-horizontal-pane-pos", edit_pane.get_divider ());
2557 node->set_property("notebook-shrunk", _notebook_shrunk);
2558 node->set_property("edit-vertical-pane-pos", editor_summary_pane.get_divider());
2560 maybe_add_mixer_strip_width (*node);
2562 node->set_property ("zoom-focus", zoom_focus);
2564 node->set_property ("zoom", samples_per_pixel);
2565 node->set_property ("grid-type", _grid_type);
2566 node->set_property ("snap-mode", _snap_mode);
2567 node->set_property ("internal-grid-type", internal_grid_type);
2568 node->set_property ("internal-snap-mode", internal_snap_mode);
2569 node->set_property ("pre-internal-grid-type", pre_internal_grid_type);
2570 node->set_property ("pre-internal-snap-mode", pre_internal_snap_mode);
2571 node->set_property ("edit-point", _edit_point);
2572 node->set_property ("visible-track-count", _visible_track_count);
2574 node->set_property ("playhead", playhead_cursor->current_sample ());
2575 node->set_property ("left-frame", _leftmost_sample);
2576 node->set_property ("y-origin", vertical_adjustment.get_value ());
2578 node->set_property ("maximised", _maximised);
2579 node->set_property ("follow-playhead", _follow_playhead);
2580 node->set_property ("stationary-playhead", _stationary_playhead);
2581 node->set_property ("mouse-mode", mouse_mode);
2582 node->set_property ("join-object-range", smart_mode_action->get_active ());
2584 Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Editor"), X_("show-editor-mixer"));
2585 node->set_property (X_("show-editor-mixer"), tact->get_active());
2587 tact = ActionManager::get_toggle_action (X_("Editor"), X_("show-editor-list"));
2588 node->set_property (X_("show-editor-list"), tact->get_active());
2590 node->set_property (X_("editor-list-page"), _the_notebook.get_current_page ());
2592 if (button_bindings) {
2593 XMLNode* bb = new XMLNode (X_("Buttons"));
2594 button_bindings->save (*bb);
2595 node->add_child_nocopy (*bb);
2598 node->set_property (X_("show-marker-lines"), _show_marker_lines);
2600 node->add_child_nocopy (selection->get_state ());
2601 node->add_child_nocopy (_regions->get_state ());
2603 node->set_property ("nudge-clock-value", nudge_clock->current_duration());
2605 node->add_child_nocopy (_locations->get_state ());
2610 /** if @param trackview_relative_offset is true, @param y y is an offset into the trackview area, in pixel units
2611 * if @param trackview_relative_offset is false, @param y y is a global canvas * coordinate, in pixel units
2613 * @return pair: TimeAxisView that y is over, layer index.
2615 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2616 * in stacked or expanded region display mode, otherwise 0.
2618 std::pair<TimeAxisView *, double>
2619 Editor::trackview_by_y_position (double y, bool trackview_relative_offset) const
2621 if (!trackview_relative_offset) {
2622 y -= _trackview_group->canvas_origin().y;
2626 return std::make_pair ((TimeAxisView *) 0, 0);
2629 for (TrackViewList::const_iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2631 std::pair<TimeAxisView*, double> const r = (*iter)->covers_y_position (y);
2638 return std::make_pair ((TimeAxisView *) 0, 0);
2642 Editor::set_snapped_cursor_position (samplepos_t pos)
2644 if (_edit_point == EditAtMouse) {
2645 snapped_cursor->set_position(pos);
2650 /** Snap a position to the grid, if appropriate, taking into account current
2651 * grid settings and also the state of any snap modifier keys that may be pressed.
2652 * @param start Position to snap.
2653 * @param event Event to get current key modifier information from, or 0.
2656 Editor::snap_to_with_modifier (MusicSample& start, GdkEvent const * event, RoundMode direction, SnapPref pref)
2658 if (!_session || !event) {
2662 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2663 if (_snap_mode == SnapOff) {
2664 snap_to_internal (start, direction, pref);
2666 start.set (start.sample, 0);
2669 if (_snap_mode != SnapOff) {
2670 snap_to_internal (start, direction, pref);
2671 } else if (ArdourKeyboard::indicates_snap_delta (event->button.state)) {
2672 /* SnapOff, but we pressed the snap_delta modifier */
2673 snap_to_internal (start, direction, pref);
2675 start.set (start.sample, 0);
2681 Editor::snap_to (MusicSample& start, RoundMode direction, SnapPref pref, bool ensure_snap)
2683 if (!_session || (_snap_mode == SnapOff && !ensure_snap)) {
2684 start.set (start.sample, 0);
2688 snap_to_internal (start, direction, pref, ensure_snap);
2692 check_best_snap (samplepos_t presnap, samplepos_t &test, samplepos_t &dist, samplepos_t &best)
2694 samplepos_t diff = abs (test - presnap);
2700 test = max_samplepos; // reset this so it doesn't get accidentally reused
2704 Editor::snap_to_timecode (MusicSample presnap, RoundMode direction, SnapPref gpref)
2706 samplepos_t start = presnap.sample;
2707 const samplepos_t one_timecode_second = (samplepos_t)(rint(_session->timecode_frames_per_second()) * _session->samples_per_timecode_frame());
2708 samplepos_t one_timecode_minute = (samplepos_t)(rint(_session->timecode_frames_per_second()) * _session->samples_per_timecode_frame() * 60);
2710 TimecodeRulerScale scale = (gpref != SnapToGrid_Unscaled) ? timecode_ruler_scale : timecode_show_samples;
2713 case timecode_show_bits:
2714 case timecode_show_samples:
2715 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2716 fmod((double)start, (double)_session->samples_per_timecode_frame()) == 0) {
2717 /* start is already on a whole timecode frame, do nothing */
2718 } else if (((direction == 0) && (fmod((double)start, (double)_session->samples_per_timecode_frame()) > (_session->samples_per_timecode_frame() / 2))) || (direction > 0)) {
2719 start = (samplepos_t) (ceil ((double) start / _session->samples_per_timecode_frame()) * _session->samples_per_timecode_frame());
2721 start = (samplepos_t) (floor ((double) start / _session->samples_per_timecode_frame()) * _session->samples_per_timecode_frame());
2725 case timecode_show_seconds:
2726 if (_session->config.get_timecode_offset_negative()) {
2727 start += _session->config.get_timecode_offset ();
2729 start -= _session->config.get_timecode_offset ();
2731 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2732 (start % one_timecode_second == 0)) {
2733 /* start is already on a whole second, do nothing */
2734 } else if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2735 start = (samplepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2737 start = (samplepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2740 if (_session->config.get_timecode_offset_negative()) {
2741 start -= _session->config.get_timecode_offset ();
2743 start += _session->config.get_timecode_offset ();
2747 case timecode_show_minutes:
2748 case timecode_show_hours:
2749 case timecode_show_many_hours:
2750 if (_session->config.get_timecode_offset_negative()) {
2751 start += _session->config.get_timecode_offset ();
2753 start -= _session->config.get_timecode_offset ();
2755 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2756 (start % one_timecode_minute == 0)) {
2757 /* start is already on a whole minute, do nothing */
2758 } else if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2759 start = (samplepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2761 start = (samplepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2763 if (_session->config.get_timecode_offset_negative()) {
2764 start -= _session->config.get_timecode_offset ();
2766 start += _session->config.get_timecode_offset ();
2770 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2773 MusicSample ret(start,0);
2778 Editor::snap_to_minsec (MusicSample presnap, RoundMode direction, SnapPref gpref)
2780 MusicSample ret(presnap);
2782 const samplepos_t one_second = _session->sample_rate();
2783 const samplepos_t one_minute = one_second * 60;
2784 const samplepos_t one_hour = one_minute * 60;
2786 MinsecRulerScale scale = (gpref != SnapToGrid_Unscaled) ? minsec_ruler_scale : minsec_show_seconds;
2789 case minsec_show_msecs:
2790 case minsec_show_seconds: {
2791 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2792 presnap.sample % one_second == 0) {
2793 /* start is already on a whole second, do nothing */
2794 } else if (((direction == 0) && (presnap.sample % one_second > one_second / 2)) || (direction > 0)) {
2795 ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_second) * one_second;
2797 ret.sample = (samplepos_t) floor ((double) presnap.sample / one_second) * one_second;
2801 case minsec_show_minutes: {
2802 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2803 presnap.sample % one_minute == 0) {
2804 /* start is already on a whole minute, do nothing */
2805 } else if (((direction == 0) && (presnap.sample % one_minute > one_minute / 2)) || (direction > 0)) {
2806 ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_minute) * one_minute;
2808 ret.sample = (samplepos_t) floor ((double) presnap.sample / one_minute) * one_minute;
2813 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2814 presnap.sample % one_hour == 0) {
2815 /* start is already on a whole hour, do nothing */
2816 } else if (((direction == 0) && (presnap.sample % one_hour > one_hour / 2)) || (direction > 0)) {
2817 ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_hour) * one_hour;
2819 ret.sample = (samplepos_t) floor ((double) presnap.sample / one_hour) * one_hour;
2828 Editor::snap_to_cd_frames (MusicSample presnap, RoundMode direction, SnapPref gpref)
2830 if ((gpref != SnapToGrid_Unscaled) && (minsec_ruler_scale != minsec_show_msecs)) {
2831 return snap_to_minsec (presnap, direction, gpref);
2834 const samplepos_t one_second = _session->sample_rate();
2836 MusicSample ret(presnap);
2838 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2839 presnap.sample % (one_second/75) == 0) {
2840 /* start is already on a whole CD sample, do nothing */
2841 } else if (((direction == 0) && (presnap.sample % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2842 ret.sample = (samplepos_t) ceil ((double) presnap.sample / (one_second / 75)) * (one_second / 75);
2844 ret.sample = (samplepos_t) floor ((double) presnap.sample / (one_second / 75)) * (one_second / 75);
2851 Editor::snap_to_bbt (MusicSample presnap, RoundMode direction, SnapPref gpref)
2853 MusicSample ret(presnap);
2855 if (gpref != SnapToGrid_Unscaled) { // use the visual grid lines which are limited by the zoom scale that the user selected
2858 switch (_grid_type) {
2859 case GridTypeBeatDiv3:
2860 case GridTypeBeatDiv6:
2861 case GridTypeBeatDiv12:
2862 case GridTypeBeatDiv24:
2865 case GridTypeBeatDiv5:
2866 case GridTypeBeatDiv10:
2867 case GridTypeBeatDiv20:
2870 case GridTypeBeatDiv7:
2871 case GridTypeBeatDiv14:
2872 case GridTypeBeatDiv28:
2879 BBTRulerScale scale = bbt_ruler_scale;
2886 ret = _session->tempo_map().round_to_bar (presnap.sample, direction);
2888 case bbt_show_quarters:
2889 ret = _session->tempo_map().round_to_beat (presnap.sample, direction);
2891 case bbt_show_eighths:
2892 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 1 * divisor, direction);
2894 case bbt_show_sixteenths:
2895 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 2 * divisor, direction);
2897 case bbt_show_thirtyseconds:
2898 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 4 * divisor, direction);
2902 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, get_grid_beat_divisions(_grid_type), direction);
2909 Editor::snap_to_grid (MusicSample presnap, RoundMode direction, SnapPref gpref)
2911 MusicSample ret(presnap);
2913 if (grid_musical()) {
2914 ret = snap_to_bbt (presnap, direction, gpref);
2917 switch (_grid_type) {
2918 case GridTypeTimecode:
2919 ret = snap_to_timecode(presnap, direction, gpref);
2921 case GridTypeMinSec:
2922 ret = snap_to_minsec(presnap, direction, gpref);
2924 case GridTypeCDFrame:
2925 ret = snap_to_cd_frames(presnap, direction, gpref);
2935 Editor::snap_to_marker (samplepos_t presnap, RoundMode direction)
2941 _session->locations()->marks_either_side (presnap, before, after);
2943 if (before == max_samplepos && after == max_samplepos) {
2944 /* No marks to snap to, so just don't snap */
2946 } else if (before == max_samplepos) {
2948 } else if (after == max_samplepos) {
2951 if ((direction == RoundUpMaybe || direction == RoundUpAlways)) {
2953 } else if ((direction == RoundDownMaybe || direction == RoundDownAlways)) {
2955 } else if (direction == 0) {
2956 if ((presnap - before) < (after - presnap)) {
2968 Editor::snap_to_internal (MusicSample& start, RoundMode direction, SnapPref pref, bool ensure_snap)
2970 const samplepos_t presnap = start.sample;
2972 samplepos_t test = max_samplepos; // for each snap, we'll use this value
2973 samplepos_t dist = max_samplepos; // this records the distance of the best snap result we've found so far
2974 samplepos_t best = max_samplepos; // this records the best snap-result we've found so far
2976 /* check snap-to-marker */
2977 if ((pref == SnapToAny_Visual) && UIConfiguration::instance().get_snap_to_marks()) {
2978 test = snap_to_marker (presnap, direction);
2979 check_best_snap(presnap, test, dist, best);
2982 /* check snap-to-region-{start/end/sync} */
2984 (pref == SnapToAny_Visual) &&
2985 (UIConfiguration::instance().get_snap_to_region_start() || UIConfiguration::instance().get_snap_to_region_end() || UIConfiguration::instance().get_snap_to_region_sync())
2987 if (!region_boundary_cache.empty()) {
2989 vector<samplepos_t>::iterator prev = region_boundary_cache.begin();
2990 vector<samplepos_t>::iterator next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), presnap);
2991 if (next != region_boundary_cache.begin ()) {
2996 if ((direction == RoundUpMaybe || direction == RoundUpAlways))
2998 else if ((direction == RoundDownMaybe || direction == RoundDownAlways))
3000 else if (direction == 0) {
3001 if ((presnap - *prev) < (*next - presnap)) {
3010 check_best_snap(presnap, test, dist, best);
3014 if (UIConfiguration::instance().get_snap_to_grid() && (_grid_type != GridTypeNone)) {
3015 MusicSample pre(presnap, 0);
3016 MusicSample post = snap_to_grid (pre, direction, pref);
3017 check_best_snap(presnap, post.sample, dist, best);
3020 /* now check "magnetic" state: is the grid within reasonable on-screen distance to trigger a snap?
3021 * this also helps to avoid snapping to somewhere the user can't see. (i.e.: I clicked on a region and it disappeared!!)
3022 * ToDo: Perhaps this should only occur if EditPointMouse?
3024 int snap_threshold_s = pixel_to_sample(UIConfiguration::instance().get_snap_threshold());
3026 start.set (best, 0);
3028 } else if (presnap > best) {
3029 if (presnap > (best+ snap_threshold_s)) {
3032 } else if (presnap < best) {
3033 if (presnap < (best - snap_threshold_s)) {
3038 start.set (best, 0);
3043 Editor::setup_toolbar ()
3045 HBox* mode_box = manage(new HBox);
3046 mode_box->set_border_width (2);
3047 mode_box->set_spacing(2);
3049 HBox* mouse_mode_box = manage (new HBox);
3050 HBox* mouse_mode_hbox = manage (new HBox);
3051 VBox* mouse_mode_vbox = manage (new VBox);
3052 Alignment* mouse_mode_align = manage (new Alignment);
3054 Glib::RefPtr<SizeGroup> mouse_mode_size_group = SizeGroup::create (SIZE_GROUP_VERTICAL);
3055 mouse_mode_size_group->add_widget (smart_mode_button);
3056 mouse_mode_size_group->add_widget (mouse_move_button);
3057 mouse_mode_size_group->add_widget (mouse_cut_button);
3058 mouse_mode_size_group->add_widget (mouse_select_button);
3059 mouse_mode_size_group->add_widget (mouse_timefx_button);
3060 if (!Profile->get_mixbus()) {
3061 mouse_mode_size_group->add_widget (mouse_audition_button);
3063 mouse_mode_size_group->add_widget (mouse_draw_button);
3064 mouse_mode_size_group->add_widget (mouse_content_button);
3066 if (!Profile->get_mixbus()) {
3067 mouse_mode_size_group->add_widget (zoom_in_button);
3068 mouse_mode_size_group->add_widget (zoom_out_button);
3069 mouse_mode_size_group->add_widget (zoom_out_full_button);
3070 mouse_mode_size_group->add_widget (zoom_focus_selector);
3071 mouse_mode_size_group->add_widget (tav_shrink_button);
3072 mouse_mode_size_group->add_widget (tav_expand_button);
3074 mouse_mode_size_group->add_widget (zoom_preset_selector);
3075 mouse_mode_size_group->add_widget (visible_tracks_selector);
3078 mouse_mode_size_group->add_widget (grid_type_selector);
3079 mouse_mode_size_group->add_widget (snap_mode_button);
3081 mouse_mode_size_group->add_widget (edit_point_selector);
3082 mouse_mode_size_group->add_widget (edit_mode_selector);
3084 mouse_mode_size_group->add_widget (*nudge_clock);
3085 mouse_mode_size_group->add_widget (nudge_forward_button);
3086 mouse_mode_size_group->add_widget (nudge_backward_button);
3088 mouse_mode_hbox->set_spacing (2);
3089 mouse_mode_hbox->pack_start (smart_mode_button, false, false);
3091 mouse_mode_hbox->pack_start (mouse_move_button, false, false);
3092 mouse_mode_hbox->pack_start (mouse_select_button, false, false);
3094 mouse_mode_hbox->pack_start (mouse_cut_button, false, false);
3096 if (!ARDOUR::Profile->get_mixbus()) {
3097 mouse_mode_hbox->pack_start (mouse_audition_button, false, false);
3100 mouse_mode_hbox->pack_start (mouse_timefx_button, false, false);
3101 mouse_mode_hbox->pack_start (mouse_draw_button, false, false);
3102 mouse_mode_hbox->pack_start (mouse_content_button, false, false);
3104 mouse_mode_vbox->pack_start (*mouse_mode_hbox);
3106 mouse_mode_align->add (*mouse_mode_vbox);
3107 mouse_mode_align->set (0.5, 1.0, 0.0, 0.0);
3109 mouse_mode_box->pack_start (*mouse_mode_align, false, false);
3111 edit_mode_selector.set_name ("mouse mode button");
3113 mode_box->pack_start (edit_mode_selector, false, false);
3114 mode_box->pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3115 mode_box->pack_start (edit_point_selector, false, false);
3116 mode_box->pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3118 mode_box->pack_start (*mouse_mode_box, false, false);
3122 _zoom_box.set_spacing (2);
3123 _zoom_box.set_border_width (2);
3127 zoom_preset_selector.set_name ("zoom button");
3128 zoom_preset_selector.set_icon (ArdourIcon::ZoomExpand);
3130 zoom_in_button.set_name ("zoom button");
3131 zoom_in_button.set_icon (ArdourIcon::ZoomIn);
3132 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-in"));
3133 zoom_in_button.set_related_action (act);
3135 zoom_out_button.set_name ("zoom button");
3136 zoom_out_button.set_icon (ArdourIcon::ZoomOut);
3137 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-out"));
3138 zoom_out_button.set_related_action (act);
3140 zoom_out_full_button.set_name ("zoom button");
3141 zoom_out_full_button.set_icon (ArdourIcon::ZoomFull);
3142 act = ActionManager::get_action (X_("Editor"), X_("zoom-to-session"));
3143 zoom_out_full_button.set_related_action (act);
3145 zoom_focus_selector.set_name ("zoom button");
3147 if (ARDOUR::Profile->get_mixbus()) {
3148 _zoom_box.pack_start (zoom_preset_selector, false, false);
3150 _zoom_box.pack_start (zoom_out_button, false, false);
3151 _zoom_box.pack_start (zoom_in_button, false, false);
3152 _zoom_box.pack_start (zoom_out_full_button, false, false);
3153 _zoom_box.pack_start (zoom_focus_selector, false, false);
3156 /* Track zoom buttons */
3157 _track_box.set_spacing (2);
3158 _track_box.set_border_width (2);
3160 visible_tracks_selector.set_name ("zoom button");
3161 if (Profile->get_mixbus()) {
3162 visible_tracks_selector.set_icon (ArdourIcon::TimeAxisExpand);
3164 set_size_request_to_display_given_text (visible_tracks_selector, _("All"), 30, 2);
3167 tav_expand_button.set_name ("zoom button");
3168 tav_expand_button.set_icon (ArdourIcon::TimeAxisExpand);
3169 act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
3170 tav_expand_button.set_related_action (act);
3172 tav_shrink_button.set_name ("zoom button");
3173 tav_shrink_button.set_icon (ArdourIcon::TimeAxisShrink);
3174 act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
3175 tav_shrink_button.set_related_action (act);
3177 if (ARDOUR::Profile->get_mixbus()) {
3178 _track_box.pack_start (visible_tracks_selector);
3180 _track_box.pack_start (visible_tracks_selector);
3181 _track_box.pack_start (tav_shrink_button);
3182 _track_box.pack_start (tav_expand_button);
3185 snap_box.set_spacing (2);
3186 snap_box.set_border_width (2);
3188 grid_type_selector.set_name ("mouse mode button");
3190 snap_mode_button.set_name ("mouse mode button");
3192 edit_point_selector.set_name ("mouse mode button");
3194 snap_box.pack_start (snap_mode_button, false, false);
3195 snap_box.pack_start (grid_type_selector, false, false);
3199 HBox *nudge_box = manage (new HBox);
3200 nudge_box->set_spacing (2);
3201 nudge_box->set_border_width (2);
3203 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
3204 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
3206 nudge_box->pack_start (nudge_backward_button, false, false);
3207 nudge_box->pack_start (nudge_forward_button, false, false);
3208 nudge_box->pack_start (*nudge_clock, false, false);
3211 /* Pack everything in... */
3213 toolbar_hbox.set_spacing (2);
3214 toolbar_hbox.set_border_width (2);
3216 ArdourWidgets::ArdourDropShadow *tool_shadow = manage (new (ArdourWidgets::ArdourDropShadow));
3217 tool_shadow->set_size_request (4, -1);
3218 tool_shadow->show();
3220 ebox_hpacker.pack_start (*tool_shadow, false, false);
3221 ebox_hpacker.pack_start(ebox_vpacker, true, true);
3223 Gtk::EventBox* spacer = manage (new Gtk::EventBox); // extra space under the mouse toolbar, for aesthetics
3224 spacer->set_name("EditorWindow");
3225 spacer->set_size_request(-1,4);
3228 ebox_vpacker.pack_start(toolbar_hbox, false, false);
3229 ebox_vpacker.pack_start(*spacer, false, false);
3230 ebox_vpacker.show();
3232 toolbar_hbox.pack_start (*mode_box, false, false);
3233 toolbar_hbox.pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3234 toolbar_hbox.pack_start (snap_box, false, false);
3235 toolbar_hbox.pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3236 toolbar_hbox.pack_start (*nudge_box, false, false);
3237 toolbar_hbox.pack_end (_zoom_box, false, false, 2);
3238 toolbar_hbox.pack_end (*(manage (new ArdourVSpacer ())), false, false, 3);
3239 toolbar_hbox.pack_end (_track_box, false, false);
3241 toolbar_hbox.show_all ();
3245 Editor::build_edit_point_menu ()
3247 using namespace Menu_Helpers;
3249 edit_point_selector.AddMenuElem (MenuElem (edit_point_strings[(int)EditAtPlayhead], sigc::bind (sigc::mem_fun(*this, &Editor::edit_point_selection_done), (EditPoint) EditAtPlayhead)));
3250 if(!Profile->get_mixbus())
3251 edit_point_selector.AddMenuElem (MenuElem (edit_point_strings[(int)EditAtSelectedMarker], sigc::bind (sigc::mem_fun(*this, &Editor::edit_point_selection_done), (EditPoint) EditAtSelectedMarker)));
3252 edit_point_selector.AddMenuElem (MenuElem (edit_point_strings[(int)EditAtMouse], sigc::bind (sigc::mem_fun(*this, &Editor::edit_point_selection_done), (EditPoint) EditAtMouse)));
3254 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_TRIANGLE_WIDTH, 2);
3258 Editor::build_edit_mode_menu ()
3260 using namespace Menu_Helpers;
3262 edit_mode_selector.AddMenuElem (MenuElem (edit_mode_strings[(int)Slide], sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Slide)));
3263 edit_mode_selector.AddMenuElem (MenuElem (edit_mode_strings[(int)Ripple], sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Ripple)));
3264 edit_mode_selector.AddMenuElem (MenuElem (edit_mode_strings[(int)Lock], sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Lock)));
3265 /* Note: Splice was removed */
3267 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_TRIANGLE_WIDTH, 2);
3271 Editor::build_grid_type_menu ()
3273 using namespace Menu_Helpers;
3275 /* main grid: bars, quarter-notes, etc */
3276 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeNone], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeNone)));
3277 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBar], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBar)));
3278 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeat], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeat)));
3279 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv2], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv2)));
3280 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv4], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv4)));
3281 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv8], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv8)));
3282 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv16], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv16)));
3283 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv32], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv32)));
3286 grid_type_selector.AddMenuElem(SeparatorElem());
3287 Gtk::Menu *_triplet_menu = manage (new Menu);
3288 MenuList& triplet_items (_triplet_menu->items());
3290 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv3], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv3)));
3291 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv6], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv6)));
3292 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv12], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv12)));
3293 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv24], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv24)));
3295 grid_type_selector.AddMenuElem (Menu_Helpers::MenuElem (_("Triplets"), *_triplet_menu));
3297 /* quintuplet grid */
3298 Gtk::Menu *_quintuplet_menu = manage (new Menu);
3299 MenuList& quintuplet_items (_quintuplet_menu->items());
3301 quintuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv5], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv5)));
3302 quintuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv10], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv10)));
3303 quintuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv20], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv20)));
3305 grid_type_selector.AddMenuElem (Menu_Helpers::MenuElem (_("Quintuplets"), *_quintuplet_menu));
3307 /* septuplet grid */
3308 Gtk::Menu *_septuplet_menu = manage (new Menu);
3309 MenuList& septuplet_items (_septuplet_menu->items());
3311 septuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv7], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv7)));
3312 septuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv14], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv14)));
3313 septuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv28], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv28)));
3315 grid_type_selector.AddMenuElem (Menu_Helpers::MenuElem (_("Septuplets"), *_septuplet_menu));
3317 grid_type_selector.AddMenuElem(SeparatorElem());
3318 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeTimecode], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeTimecode)));
3319 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeMinSec], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeMinSec)));
3320 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeCDFrame], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeCDFrame)));
3324 Editor::setup_tooltips ()
3326 set_tooltip (smart_mode_button, _("Smart Mode (add range functions to Grab Mode)"));
3327 set_tooltip (mouse_move_button, _("Grab Mode (select/move objects)"));
3328 set_tooltip (mouse_cut_button, _("Cut Mode (split regions)"));
3329 set_tooltip (mouse_select_button, _("Range Mode (select time ranges)"));
3330 set_tooltip (mouse_draw_button, _("Draw Mode (draw and edit gain/notes/automation)"));
3331 set_tooltip (mouse_timefx_button, _("Stretch Mode (time-stretch audio and midi regions, preserving pitch)"));
3332 set_tooltip (mouse_audition_button, _("Audition Mode (listen to regions)"));
3333 set_tooltip (mouse_content_button, _("Internal Edit Mode (edit notes and automation points)"));
3334 set_tooltip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
3335 set_tooltip (nudge_forward_button, _("Nudge Region/Selection Later"));
3336 set_tooltip (nudge_backward_button, _("Nudge Region/Selection Earlier"));
3337 set_tooltip (zoom_in_button, _("Zoom In"));
3338 set_tooltip (zoom_out_button, _("Zoom Out"));
3339 set_tooltip (zoom_preset_selector, _("Zoom to Time Scale"));
3340 set_tooltip (zoom_out_full_button, _("Zoom to Session"));
3341 set_tooltip (zoom_focus_selector, _("Zoom Focus"));
3342 set_tooltip (tav_expand_button, _("Expand Tracks"));
3343 set_tooltip (tav_shrink_button, _("Shrink Tracks"));
3344 set_tooltip (visible_tracks_selector, _("Number of visible tracks"));
3345 set_tooltip (grid_type_selector, _("Grid Mode"));
3346 set_tooltip (snap_mode_button, _("Snap Mode\n\nRight-click to visit Snap preferences."));
3347 set_tooltip (edit_point_selector, _("Edit Point"));
3348 set_tooltip (edit_mode_selector, _("Edit Mode"));
3349 set_tooltip (nudge_clock, _("Nudge Clock\n(controls distance used to nudge regions and selections)"));
3353 Editor::convert_drop_to_paths (
3354 vector<string>& paths,
3355 const RefPtr<Gdk::DragContext>& /*context*/,
3358 const SelectionData& data,
3362 if (_session == 0) {
3366 vector<string> uris = data.get_uris();
3370 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3371 are actually URI lists. So do it by hand.
3374 if (data.get_target() != "text/plain") {
3378 /* Parse the "uri-list" format that Nautilus provides,
3379 where each pathname is delimited by \r\n.
3381 THERE MAY BE NO NULL TERMINATING CHAR!!!
3384 string txt = data.get_text();
3388 p = (char *) malloc (txt.length() + 1);
3389 txt.copy (p, txt.length(), 0);
3390 p[txt.length()] = '\0';
3396 while (g_ascii_isspace (*p))
3400 while (*q && (*q != '\n') && (*q != '\r')) {
3407 while (q > p && g_ascii_isspace (*q))
3412 uris.push_back (string (p, q - p + 1));
3416 p = strchr (p, '\n');
3428 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3429 if ((*i).substr (0,7) == "file://") {
3430 paths.push_back (Glib::filename_from_uri (*i));
3438 Editor::new_tempo_section ()
3443 Editor::map_transport_state ()
3445 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state);
3447 if (_session && _session->transport_stopped()) {
3448 have_pending_keyboard_selection = false;
3451 update_loop_range_view ();
3455 Editor::transport_looped ()
3457 /* reset Playhead position interpolation.
3458 * see Editor::super_rapid_screen_update
3460 _last_update_time = 0;
3466 Editor::begin_selection_op_history ()
3468 selection_op_cmd_depth = 0;
3469 selection_op_history_it = 0;
3471 while(!selection_op_history.empty()) {
3472 delete selection_op_history.front();
3473 selection_op_history.pop_front();
3476 selection_undo_action->set_sensitive (false);
3477 selection_redo_action->set_sensitive (false);
3478 selection_op_history.push_front (&_selection_memento->get_state ());
3482 Editor::begin_reversible_selection_op (string name)
3485 //cerr << name << endl;
3486 /* begin/commit pairs can be nested */
3487 selection_op_cmd_depth++;
3492 Editor::commit_reversible_selection_op ()
3495 if (selection_op_cmd_depth == 1) {
3497 if (selection_op_history_it > 0 && selection_op_history_it < selection_op_history.size()) {
3498 /* The user has undone some selection ops and then made a new one,
3499 * making anything earlier in the list invalid.
3502 list<XMLNode *>::iterator it = selection_op_history.begin();
3503 list<XMLNode *>::iterator e_it = it;
3504 advance (e_it, selection_op_history_it);
3506 for (; it != e_it; ++it) {
3509 selection_op_history.erase (selection_op_history.begin(), e_it);
3512 selection_op_history.push_front (&_selection_memento->get_state ());
3513 selection_op_history_it = 0;
3515 selection_undo_action->set_sensitive (true);
3516 selection_redo_action->set_sensitive (false);
3519 if (selection_op_cmd_depth > 0) {
3520 selection_op_cmd_depth--;
3526 Editor::undo_selection_op ()
3529 selection_op_history_it++;
3531 for (std::list<XMLNode *>::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) {
3532 if (n == selection_op_history_it) {
3533 _selection_memento->set_state (*(*i), Stateful::current_state_version);
3534 selection_redo_action->set_sensitive (true);
3538 /* is there an earlier entry? */
3539 if ((selection_op_history_it + 1) >= selection_op_history.size()) {
3540 selection_undo_action->set_sensitive (false);
3546 Editor::redo_selection_op ()
3549 if (selection_op_history_it > 0) {
3550 selection_op_history_it--;
3553 for (std::list<XMLNode *>::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) {
3554 if (n == selection_op_history_it) {
3555 _selection_memento->set_state (*(*i), Stateful::current_state_version);
3556 selection_undo_action->set_sensitive (true);
3561 if (selection_op_history_it == 0) {
3562 selection_redo_action->set_sensitive (false);
3568 Editor::begin_reversible_command (string name)
3571 before.push_back (&_selection_memento->get_state ());
3572 _session->begin_reversible_command (name);
3577 Editor::begin_reversible_command (GQuark q)
3580 before.push_back (&_selection_memento->get_state ());
3581 _session->begin_reversible_command (q);
3586 Editor::abort_reversible_command ()
3589 while(!before.empty()) {
3590 delete before.front();
3593 _session->abort_reversible_command ();
3598 Editor::commit_reversible_command ()
3601 if (before.size() == 1) {
3602 _session->add_command (new MementoCommand<SelectionMemento>(*(_selection_memento), before.front(), &_selection_memento->get_state ()));
3603 redo_action->set_sensitive(false);
3604 undo_action->set_sensitive(true);
3605 begin_selection_op_history ();
3608 if (before.empty()) {
3609 cerr << "Please call begin_reversible_command() before commit_reversible_command()." << endl;
3614 _session->commit_reversible_command ();
3619 Editor::history_changed ()
3623 if (undo_action && _session) {
3624 if (_session->undo_depth() == 0) {
3625 label = S_("Command|Undo");
3627 label = string_compose(S_("Command|Undo (%1)"), _session->next_undo());
3629 undo_action->property_label() = label;
3632 if (redo_action && _session) {
3633 if (_session->redo_depth() == 0) {
3635 redo_action->set_sensitive (false);
3637 label = string_compose(_("Redo (%1)"), _session->next_redo());
3638 redo_action->set_sensitive (true);
3640 redo_action->property_label() = label;
3645 Editor::duplicate_range (bool with_dialog)
3649 RegionSelection rs = get_regions_from_selection_and_entered ();
3651 if (selection->time.length() == 0 && rs.empty()) {
3657 ArdourDialog win (_("Duplicate"));
3658 Label label (_("Number of duplications:"));
3659 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3660 SpinButton spinner (adjustment, 0.0, 1);
3663 win.get_vbox()->set_spacing (12);
3664 win.get_vbox()->pack_start (hbox);
3665 hbox.set_border_width (6);
3666 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3668 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3669 place, visually. so do this by hand.
3672 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3673 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3674 spinner.grab_focus();
3680 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3681 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3682 win.set_default_response (RESPONSE_ACCEPT);
3684 spinner.grab_focus ();
3686 switch (win.run ()) {
3687 case RESPONSE_ACCEPT:
3693 times = adjustment.get_value();
3696 if ((current_mouse_mode() == MouseRange)) {
3697 if (selection->time.length()) {
3698 duplicate_selection (times);
3700 } else if (get_smart_mode()) {
3701 if (selection->time.length()) {
3702 duplicate_selection (times);
3704 duplicate_some_regions (rs, times);
3706 duplicate_some_regions (rs, times);
3711 Editor::set_edit_mode (EditMode m)
3713 Config->set_edit_mode (m);
3717 Editor::cycle_edit_mode ()
3719 switch (Config->get_edit_mode()) {
3721 Config->set_edit_mode (Ripple);
3725 Config->set_edit_mode (Lock);
3728 Config->set_edit_mode (Slide);
3734 Editor::edit_mode_selection_done (EditMode m)
3736 Config->set_edit_mode (m);
3740 Editor::grid_type_selection_done (GridType gridtype)
3742 RefPtr<RadioAction> ract = grid_type_action (gridtype);
3744 ract->set_active ();
3749 Editor::snap_mode_selection_done (SnapMode mode)
3751 RefPtr<RadioAction> ract = snap_mode_action (mode);
3754 ract->set_active (true);
3759 Editor::cycle_edit_point (bool with_marker)
3761 if(Profile->get_mixbus())
3762 with_marker = false;
3764 switch (_edit_point) {
3766 set_edit_point_preference (EditAtPlayhead);
3768 case EditAtPlayhead:
3770 set_edit_point_preference (EditAtSelectedMarker);
3772 set_edit_point_preference (EditAtMouse);
3775 case EditAtSelectedMarker:
3776 set_edit_point_preference (EditAtMouse);
3782 Editor::edit_point_selection_done (EditPoint ep)
3784 set_edit_point_preference (ep);
3788 Editor::build_zoom_focus_menu ()
3790 using namespace Menu_Helpers;
3792 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusLeft], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusLeft)));
3793 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusRight], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusRight)));
3794 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusCenter], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusCenter)));
3795 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusPlayhead], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusPlayhead)));
3796 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusMouse], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusMouse)));
3797 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusEdit], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusEdit)));
3799 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_TRIANGLE_WIDTH, 2);
3803 Editor::zoom_focus_selection_done (ZoomFocus f)
3805 RefPtr<RadioAction> ract = zoom_focus_action (f);
3807 ract->set_active ();
3812 Editor::build_track_count_menu ()
3814 using namespace Menu_Helpers;
3816 if (!Profile->get_mixbus()) {
3817 visible_tracks_selector.AddMenuElem (MenuElem (X_("1"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 1)));
3818 visible_tracks_selector.AddMenuElem (MenuElem (X_("2"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 2)));
3819 visible_tracks_selector.AddMenuElem (MenuElem (X_("3"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 3)));
3820 visible_tracks_selector.AddMenuElem (MenuElem (X_("4"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 4)));
3821 visible_tracks_selector.AddMenuElem (MenuElem (X_("8"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 8)));
3822 visible_tracks_selector.AddMenuElem (MenuElem (X_("12"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 12)));
3823 visible_tracks_selector.AddMenuElem (MenuElem (X_("16"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 16)));
3824 visible_tracks_selector.AddMenuElem (MenuElem (X_("20"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 20)));
3825 visible_tracks_selector.AddMenuElem (MenuElem (X_("24"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 24)));
3826 visible_tracks_selector.AddMenuElem (MenuElem (X_("32"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 32)));
3827 visible_tracks_selector.AddMenuElem (MenuElem (X_("64"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 64)));
3828 visible_tracks_selector.AddMenuElem (MenuElem (_("Selection"), sigc::mem_fun(*this, &Editor::fit_selection)));
3829 visible_tracks_selector.AddMenuElem (MenuElem (_("All"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 0)));
3831 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 1 track"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 1)));
3832 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 2 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 2)));
3833 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 4 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 4)));
3834 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 8 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 8)));
3835 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 16 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 16)));
3836 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 24 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 24)));
3837 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 32 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 32)));
3838 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 48 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 48)));
3839 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit All tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 0)));
3840 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit Selection"), sigc::mem_fun(*this, &Editor::fit_selection)));
3842 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 10 ms"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 10)));
3843 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 100 ms"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 100)));
3844 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 1 sec"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 1 * 1000)));
3845 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 10 sec"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 10 * 1000)));
3846 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 1 min"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 60 * 1000)));
3847 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 10 min"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 10 * 60 * 1000)));
3848 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 1 hour"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 60 * 60 * 1000)));
3849 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 8 hours"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 8 * 60 * 60 * 1000)));
3850 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 24 hours"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 24 * 60 * 60 * 1000)));
3851 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Session"), sigc::mem_fun(*this, &Editor::temporal_zoom_session)));
3852 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Extents"), sigc::mem_fun(*this, &Editor::temporal_zoom_extents)));
3853 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Range/Region Selection"), sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_selection), Horizontal)));
3858 Editor::set_zoom_preset (int64_t ms)
3861 temporal_zoom_session();
3865 ARDOUR::samplecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
3866 temporal_zoom ((sample_rate * ms / 1000) / _visible_canvas_width);
3870 Editor::set_visible_track_count (int32_t n)
3872 _visible_track_count = n;
3874 /* if the canvas hasn't really been allocated any size yet, just
3875 record the desired number of visible tracks and return. when canvas
3876 allocation happens, we will get called again and then we can do the
3880 if (_visible_canvas_height <= 1) {
3886 DisplaySuspender ds;
3888 if (_visible_track_count > 0) {
3889 h = trackviews_height() / _visible_track_count;
3890 std::ostringstream s;
3891 s << _visible_track_count;
3893 } else if (_visible_track_count == 0) {
3895 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
3896 if ((*i)->marked_for_display()) {
3898 TimeAxisView::Children cl ((*i)->get_child_list ());
3899 for (TimeAxisView::Children::const_iterator j = cl.begin(); j != cl.end(); ++j) {
3900 if ((*j)->marked_for_display()) {
3907 visible_tracks_selector.set_text (X_("*"));
3910 h = trackviews_height() / n;
3913 /* negative value means that the visible track count has
3914 been overridden by explicit track height changes.
3916 visible_tracks_selector.set_text (X_("*"));
3920 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3921 (*i)->set_height (h, TimeAxisView::HeightPerLane);
3924 if (str != visible_tracks_selector.get_text()) {
3925 visible_tracks_selector.set_text (str);
3930 Editor::override_visible_track_count ()
3932 _visible_track_count = -1;
3933 visible_tracks_selector.set_text (_("*"));
3937 Editor::edit_controls_button_release (GdkEventButton* ev)
3939 if (Keyboard::is_context_menu_event (ev)) {
3940 ARDOUR_UI::instance()->add_route ();
3941 } else if (ev->button == 1) {
3942 selection->clear_tracks ();
3949 Editor::mouse_select_button_release (GdkEventButton* ev)
3951 /* this handles just right-clicks */
3953 if (ev->button != 3) {
3961 Editor::set_zoom_focus (ZoomFocus f)
3963 string str = zoom_focus_strings[(int)f];
3965 if (str != zoom_focus_selector.get_text()) {
3966 zoom_focus_selector.set_text (str);
3969 if (zoom_focus != f) {
3976 Editor::cycle_zoom_focus ()
3978 switch (zoom_focus) {
3980 set_zoom_focus (ZoomFocusRight);
3982 case ZoomFocusRight:
3983 set_zoom_focus (ZoomFocusCenter);
3985 case ZoomFocusCenter:
3986 set_zoom_focus (ZoomFocusPlayhead);
3988 case ZoomFocusPlayhead:
3989 set_zoom_focus (ZoomFocusMouse);
3991 case ZoomFocusMouse:
3992 set_zoom_focus (ZoomFocusEdit);
3995 set_zoom_focus (ZoomFocusLeft);
4001 Editor::update_grid ()
4003 if (grid_musical()) {
4004 std::vector<TempoMap::BBTPoint> grid;
4005 if (bbt_ruler_scale != bbt_show_many) {
4006 compute_current_bbt_points (grid, _leftmost_sample, _leftmost_sample + current_page_samples());
4008 maybe_draw_grid_lines ();
4009 } else if (grid_nonmusical()) {
4010 maybe_draw_grid_lines ();
4017 Editor::toggle_follow_playhead ()
4019 RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Editor"), X_("toggle-follow-playhead"));
4020 set_follow_playhead (tact->get_active());
4023 /** @param yn true to follow playhead, otherwise false.
4024 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
4027 Editor::set_follow_playhead (bool yn, bool catch_up)
4029 if (_follow_playhead != yn) {
4030 if ((_follow_playhead = yn) == true && catch_up) {
4032 reset_x_origin_to_follow_playhead ();
4039 Editor::toggle_stationary_playhead ()
4041 RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Editor"), X_("toggle-stationary-playhead"));
4042 set_stationary_playhead (tact->get_active());
4046 Editor::set_stationary_playhead (bool yn)
4048 if (_stationary_playhead != yn) {
4049 if ((_stationary_playhead = yn) == true) {
4050 /* catch up -- FIXME need a 3.0 equivalent of this 2.X call */
4051 // update_current_screen ();
4058 Editor::playlist_selector () const
4060 return *_playlist_selector;
4064 Editor::get_paste_offset (samplepos_t pos, unsigned paste_count, samplecnt_t duration)
4066 if (paste_count == 0) {
4067 /* don't bother calculating an offset that will be zero anyway */
4071 /* calculate basic unsnapped multi-paste offset */
4072 samplecnt_t offset = paste_count * duration;
4074 /* snap offset so pos + offset is aligned to the grid */
4075 MusicSample offset_pos (pos + offset, 0);
4076 snap_to(offset_pos, RoundUpMaybe);
4077 offset = offset_pos.sample - pos;
4083 Editor::get_grid_beat_divisions(samplepos_t position)
4085 switch (_grid_type) {
4086 case GridTypeBeatDiv32: return 32;
4087 case GridTypeBeatDiv28: return 28;
4088 case GridTypeBeatDiv24: return 24;
4089 case GridTypeBeatDiv20: return 20;
4090 case GridTypeBeatDiv16: return 16;
4091 case GridTypeBeatDiv14: return 14;
4092 case GridTypeBeatDiv12: return 12;
4093 case GridTypeBeatDiv10: return 10;
4094 case GridTypeBeatDiv8: return 8;
4095 case GridTypeBeatDiv7: return 7;
4096 case GridTypeBeatDiv6: return 6;
4097 case GridTypeBeatDiv5: return 5;
4098 case GridTypeBeatDiv4: return 4;
4099 case GridTypeBeatDiv3: return 3;
4100 case GridTypeBeatDiv2: return 2;
4101 case GridTypeBeat: return 1;
4102 case GridTypeBar: return 1;
4104 case GridTypeNone: return 0;
4105 case GridTypeTimecode: return 0;
4106 case GridTypeMinSec: return 0;
4107 case GridTypeCDFrame: return 0;
4113 /** returns the current musical grid divisiions using the supplied modifier mask from a GtkEvent.
4114 if the grid is non-musical, returns 0.
4115 if the grid is snapped to bars, returns -1.
4116 @param event_state the current keyboard modifier mask.
4119 Editor::get_grid_music_divisions (uint32_t event_state)
4121 if (snap_mode() == SnapOff && !ArdourKeyboard::indicates_snap (event_state)) {
4125 if (snap_mode() != SnapOff && ArdourKeyboard::indicates_snap (event_state)) {
4129 switch (_grid_type) {
4130 case GridTypeBeatDiv32: return 32;
4131 case GridTypeBeatDiv28: return 28;
4132 case GridTypeBeatDiv24: return 24;
4133 case GridTypeBeatDiv20: return 20;
4134 case GridTypeBeatDiv16: return 16;
4135 case GridTypeBeatDiv14: return 14;
4136 case GridTypeBeatDiv12: return 12;
4137 case GridTypeBeatDiv10: return 10;
4138 case GridTypeBeatDiv8: return 8;
4139 case GridTypeBeatDiv7: return 7;
4140 case GridTypeBeatDiv6: return 6;
4141 case GridTypeBeatDiv5: return 5;
4142 case GridTypeBeatDiv4: return 4;
4143 case GridTypeBeatDiv3: return 3;
4144 case GridTypeBeatDiv2: return 2;
4145 case GridTypeBeat: return 1;
4146 case GridTypeBar : return -1;
4148 case GridTypeNone: return 0;
4149 case GridTypeTimecode: return 0;
4150 case GridTypeMinSec: return 0;
4151 case GridTypeCDFrame: return 0;
4157 Editor::get_grid_type_as_beats (bool& success, samplepos_t position)
4161 const unsigned divisions = get_grid_beat_divisions(position);
4163 return Temporal::Beats(1.0 / (double)get_grid_beat_divisions(position));
4166 switch (_grid_type) {
4168 return Temporal::Beats(4.0 / _session->tempo_map().meter_at_sample (position).note_divisor());
4171 const Meter& m = _session->tempo_map().meter_at_sample (position);
4172 return Temporal::Beats((4.0 * m.divisions_per_bar()) / m.note_divisor());
4180 return Temporal::Beats();
4184 Editor::get_nudge_distance (samplepos_t pos, samplecnt_t& next)
4188 ret = nudge_clock->current_duration (pos);
4189 next = ret + 1; /* XXXX fix me */
4195 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
4197 ArdourDialog dialog (_("Playlist Deletion"));
4198 Label label (string_compose (_("Playlist %1 is currently unused.\n"
4199 "If it is kept, its audio files will not be cleaned.\n"
4200 "If it is deleted, audio files used by it alone will be cleaned."),
4203 dialog.set_position (WIN_POS_CENTER);
4204 dialog.get_vbox()->pack_start (label);
4208 dialog.add_button (_("Delete All Unused"), RESPONSE_YES); // needs clarification. this and all remaining ones
4209 dialog.add_button (_("Delete Playlist"), RESPONSE_ACCEPT);
4210 Button* keep = dialog.add_button (_("Keep Playlist"), RESPONSE_REJECT);
4211 dialog.add_button (_("Keep Remaining"), RESPONSE_NO); // ditto
4212 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
4214 /* by default gtk uses the left most button */
4215 keep->grab_focus ();
4217 switch (dialog.run ()) {
4219 /* keep this and all remaining ones */
4224 /* delete this and all others */
4228 case RESPONSE_ACCEPT:
4229 /* delete the playlist */
4233 case RESPONSE_REJECT:
4234 /* keep the playlist */
4246 Editor::audio_region_selection_covers (samplepos_t where)
4248 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
4249 if ((*a)->region()->covers (where)) {
4258 Editor::cleanup_regions ()
4260 _regions->remove_unused_regions();
4265 Editor::prepare_for_cleanup ()
4267 cut_buffer->clear_regions ();
4268 cut_buffer->clear_playlists ();
4270 selection->clear_regions ();
4271 selection->clear_playlists ();
4273 _regions->suspend_redisplay ();
4277 Editor::finish_cleanup ()
4279 _regions->resume_redisplay ();
4283 Editor::transport_loop_location()
4286 return _session->locations()->auto_loop_location();
4293 Editor::transport_punch_location()
4296 return _session->locations()->auto_punch_location();
4303 Editor::control_layout_scroll (GdkEventScroll* ev)
4305 /* Just forward to the normal canvas scroll method. The coordinate
4306 systems are different but since the canvas is always larger than the
4307 track headers, and aligned with the trackview area, this will work.
4309 In the not too distant future this layout is going away anyway and
4310 headers will be on the canvas.
4312 return canvas_scroll_event (ev, false);
4316 Editor::session_state_saved (string)
4319 _snapshots->redisplay ();
4323 Editor::maximise_editing_space ()
4329 Gtk::Window* toplevel = current_toplevel();
4332 toplevel->fullscreen ();
4338 Editor::restore_editing_space ()
4344 Gtk::Window* toplevel = current_toplevel();
4347 toplevel->unfullscreen();
4353 * Make new playlists for a given track and also any others that belong
4354 * to the same active route group with the `select' property.
4359 Editor::new_playlists (TimeAxisView* v)
4361 begin_reversible_command (_("new playlists"));
4362 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4363 _session->playlists()->get (playlists);
4364 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::group_select.property_id);
4365 commit_reversible_command ();
4369 * Use a copy of the current playlist for a given track and also any others that belong
4370 * to the same active route group with the `select' property.
4375 Editor::copy_playlists (TimeAxisView* v)
4377 begin_reversible_command (_("copy playlists"));
4378 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4379 _session->playlists()->get (playlists);
4380 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::group_select.property_id);
4381 commit_reversible_command ();
4384 /** Clear the current playlist for a given track and also any others that belong
4385 * to the same active route group with the `select' property.
4390 Editor::clear_playlists (TimeAxisView* v)
4392 begin_reversible_command (_("clear playlists"));
4393 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4394 _session->playlists()->get (playlists);
4395 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::group_select.property_id);
4396 commit_reversible_command ();
4400 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4402 atv.use_new_playlist (sz > 1 ? false : true, playlists, false);
4406 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4408 atv.use_new_playlist (sz > 1 ? false : true, playlists, true);
4412 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4414 atv.clear_playlist ();
4418 Editor::get_y_origin () const
4420 return vertical_adjustment.get_value ();
4423 /** Queue up a change to the viewport x origin.
4424 * @param sample New x origin.
4427 Editor::reset_x_origin (samplepos_t sample)
4429 pending_visual_change.add (VisualChange::TimeOrigin);
4430 pending_visual_change.time_origin = sample;
4431 ensure_visual_change_idle_handler ();
4435 Editor::reset_y_origin (double y)
4437 pending_visual_change.add (VisualChange::YOrigin);
4438 pending_visual_change.y_origin = y;
4439 ensure_visual_change_idle_handler ();
4443 Editor::reset_zoom (samplecnt_t spp)
4445 if (spp == samples_per_pixel) {
4449 pending_visual_change.add (VisualChange::ZoomLevel);
4450 pending_visual_change.samples_per_pixel = spp;
4451 ensure_visual_change_idle_handler ();
4455 Editor::reposition_and_zoom (samplepos_t sample, double fpu)
4457 reset_x_origin (sample);
4460 if (!no_save_visual) {
4461 undo_visual_stack.push_back (current_visual_state(false));
4465 Editor::VisualState::VisualState (bool with_tracks)
4466 : gui_state (with_tracks ? new GUIObjectState : 0)
4470 Editor::VisualState::~VisualState ()
4475 Editor::VisualState*
4476 Editor::current_visual_state (bool with_tracks)
4478 VisualState* vs = new VisualState (with_tracks);
4479 vs->y_position = vertical_adjustment.get_value();
4480 vs->samples_per_pixel = samples_per_pixel;
4481 vs->_leftmost_sample = _leftmost_sample;
4482 vs->zoom_focus = zoom_focus;
4485 vs->gui_state->set_state (ARDOUR_UI::instance()->gui_object_state->get_state());
4492 Editor::undo_visual_state ()
4494 if (undo_visual_stack.empty()) {
4498 VisualState* vs = undo_visual_stack.back();
4499 undo_visual_stack.pop_back();
4502 redo_visual_stack.push_back (current_visual_state (vs ? vs->gui_state != 0 : false));
4505 use_visual_state (*vs);
4510 Editor::redo_visual_state ()
4512 if (redo_visual_stack.empty()) {
4516 VisualState* vs = redo_visual_stack.back();
4517 redo_visual_stack.pop_back();
4519 /* XXX: can 'vs' really be 0? Is there a place that puts NULL pointers onto the stack? */
4520 undo_visual_stack.push_back (current_visual_state (vs ? (vs->gui_state != 0) : false));
4523 use_visual_state (*vs);
4528 Editor::swap_visual_state ()
4530 if (undo_visual_stack.empty()) {
4531 redo_visual_state ();
4533 undo_visual_state ();
4538 Editor::use_visual_state (VisualState& vs)
4540 PBD::Unwinder<bool> nsv (no_save_visual, true);
4541 DisplaySuspender ds;
4543 vertical_adjustment.set_value (vs.y_position);
4545 set_zoom_focus (vs.zoom_focus);
4546 reposition_and_zoom (vs._leftmost_sample, vs.samples_per_pixel);
4549 ARDOUR_UI::instance()->gui_object_state->set_state (vs.gui_state->get_state());
4551 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4552 (*i)->clear_property_cache();
4553 (*i)->reset_visual_state ();
4557 _routes->update_visibility ();
4560 /** This is the core function that controls the zoom level of the canvas. It is called
4561 * whenever one or more calls are made to reset_zoom(). It executes in an idle handler.
4562 * @param spp new number of samples per pixel
4565 Editor::set_samples_per_pixel (samplecnt_t spp)
4571 const samplecnt_t three_days = 3 * 24 * 60 * 60 * (_session ? _session->sample_rate() : 48000);
4572 const samplecnt_t lots_of_pixels = 4000;
4574 /* if the zoom level is greater than what you'd get trying to display 3
4575 * days of audio on a really big screen, then it's too big.
4578 if (spp * lots_of_pixels > three_days) {
4582 samples_per_pixel = spp;
4586 Editor::on_samples_per_pixel_changed ()
4588 bool const showing_time_selection = selection->time.length() > 0;
4590 if (showing_time_selection && selection->time.start () != selection->time.end_sample ()) {
4591 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4592 (*i)->reshow_selection (selection->time);
4596 ZoomChanged (); /* EMIT_SIGNAL */
4598 ArdourCanvas::GtkCanvasViewport* c;
4600 c = get_track_canvas();
4602 c->canvas()->zoomed ();
4605 if (playhead_cursor) {
4606 playhead_cursor->set_position (playhead_cursor->current_sample ());
4609 refresh_location_display();
4610 _summary->set_overlays_dirty ();
4612 update_marker_labels ();
4618 Editor::playhead_cursor_sample () const
4620 return playhead_cursor->current_sample();
4624 Editor::queue_visual_videotimeline_update ()
4626 pending_visual_change.add (VisualChange::VideoTimeline);
4627 ensure_visual_change_idle_handler ();
4631 Editor::ensure_visual_change_idle_handler ()
4633 if (pending_visual_change.idle_handler_id < 0) {
4634 /* see comment in add_to_idle_resize above. */
4635 pending_visual_change.idle_handler_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, _idle_visual_changer, this, NULL);
4636 pending_visual_change.being_handled = false;
4641 Editor::_idle_visual_changer (void* arg)
4643 return static_cast<Editor*>(arg)->idle_visual_changer ();
4647 Editor::pre_render ()
4649 visual_change_queued = false;
4651 if (pending_visual_change.pending != 0) {
4652 ensure_visual_change_idle_handler();
4657 Editor::idle_visual_changer ()
4659 pending_visual_change.idle_handler_id = -1;
4661 if (pending_visual_change.pending == 0) {
4665 /* set_horizontal_position() below (and maybe other calls) call
4666 gtk_main_iteration(), so it's possible that a signal will be handled
4667 half-way through this method. If this signal wants an
4668 idle_visual_changer we must schedule another one after this one, so
4669 mark the idle_handler_id as -1 here to allow that. Also make a note
4670 that we are doing the visual change, so that changes in response to
4671 super-rapid-screen-update can be dropped if we are still processing
4675 if (visual_change_queued) {
4679 pending_visual_change.being_handled = true;
4681 VisualChange vc = pending_visual_change;
4683 pending_visual_change.pending = (VisualChange::Type) 0;
4685 visual_changer (vc);
4687 pending_visual_change.being_handled = false;
4689 visual_change_queued = true;
4691 return 0; /* this is always a one-shot call */
4695 Editor::visual_changer (const VisualChange& vc)
4698 * Changed first so the correct horizontal canvas position is calculated in
4699 * Editor::set_horizontal_position
4701 if (vc.pending & VisualChange::ZoomLevel) {
4702 set_samples_per_pixel (vc.samples_per_pixel);
4705 if (vc.pending & VisualChange::TimeOrigin) {
4706 double new_time_origin = sample_to_pixel_unrounded (vc.time_origin);
4707 set_horizontal_position (new_time_origin);
4710 if (vc.pending & VisualChange::YOrigin) {
4711 vertical_adjustment.set_value (vc.y_origin);
4715 * Now the canvas is in the final state before render the canvas items that
4716 * support the Item::prepare_for_render interface can calculate the correct
4717 * item to visible canvas intersection.
4719 if (vc.pending & VisualChange::ZoomLevel) {
4720 on_samples_per_pixel_changed ();
4722 compute_fixed_ruler_scale ();
4724 compute_bbt_ruler_scale (vc.time_origin, pending_visual_change.time_origin + current_page_samples());
4725 update_tempo_based_rulers ();
4728 if (!(vc.pending & VisualChange::ZoomLevel)) {
4729 /* If the canvas is not being zoomed then the canvas items will not change
4730 * and cause Item::prepare_for_render to be called so do it here manually.
4731 * Not ideal, but I can't think of a better solution atm.
4733 _track_canvas->prepare_for_render();
4736 /* If we are only scrolling vertically there is no need to update these */
4737 if (vc.pending != VisualChange::YOrigin) {
4738 update_fixed_rulers ();
4739 redisplay_grid (true);
4741 /* video frames & position need to be updated for zoom, horiz-scroll
4742 * and (explicitly) VisualChange::VideoTimeline.
4744 update_video_timeline();
4747 _summary->set_overlays_dirty ();
4750 struct EditorOrderTimeAxisSorter {
4751 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4752 return a->order () < b->order ();
4757 Editor::sort_track_selection (TrackViewList& sel)
4759 EditorOrderTimeAxisSorter cmp;
4764 Editor::get_preferred_edit_position (EditIgnoreOption ignore, bool from_context_menu, bool from_outside_canvas)
4767 samplepos_t where = 0;
4768 EditPoint ep = _edit_point;
4770 if (Profile->get_mixbus()) {
4771 if (ep == EditAtSelectedMarker) {
4772 ep = EditAtPlayhead;
4776 if (from_outside_canvas && (ep == EditAtMouse)) {
4777 ep = EditAtPlayhead;
4778 } else if (from_context_menu && (ep == EditAtMouse)) {
4779 return canvas_event_sample (&context_click_event, 0, 0);
4782 if (entered_marker) {
4783 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4784 return entered_marker->position();
4787 if ((ignore == EDIT_IGNORE_PHEAD) && ep == EditAtPlayhead) {
4788 ep = EditAtSelectedMarker;
4791 if ((ignore == EDIT_IGNORE_MOUSE) && ep == EditAtMouse) {
4792 ep = EditAtPlayhead;
4795 MusicSample snap_mf (0, 0);
4798 case EditAtPlayhead:
4799 if (_dragging_playhead) {
4800 /* NOTE: since the user is dragging with the mouse, this operation will implicitly be Snapped */
4801 where = playhead_cursor->current_sample();
4803 where = _session->audible_sample();
4805 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4808 case EditAtSelectedMarker:
4809 if (!selection->markers.empty()) {
4811 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4814 where = loc->start();
4818 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4826 if (!mouse_sample (where, ignored)) {
4827 /* XXX not right but what can we do ? */
4830 snap_mf.sample = where;
4832 where = snap_mf.sample;
4833 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4841 Editor::set_loop_range (samplepos_t start, samplepos_t end, string cmd)
4843 if (!_session) return;
4845 begin_reversible_command (cmd);
4849 if ((tll = transport_loop_location()) == 0) {
4850 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop, get_grid_music_divisions(0));
4851 XMLNode &before = _session->locations()->get_state();
4852 _session->locations()->add (loc, true);
4853 _session->set_auto_loop_location (loc);
4854 XMLNode &after = _session->locations()->get_state();
4855 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4857 XMLNode &before = tll->get_state();
4858 tll->set_hidden (false, this);
4859 tll->set (start, end);
4860 XMLNode &after = tll->get_state();
4861 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4864 commit_reversible_command ();
4868 Editor::set_punch_range (samplepos_t start, samplepos_t end, string cmd)
4870 if (!_session) return;
4872 begin_reversible_command (cmd);
4876 if ((tpl = transport_punch_location()) == 0) {
4877 Location* loc = new Location (*_session, start, end, _("Punch"), Location::IsAutoPunch, get_grid_music_divisions(0));
4878 XMLNode &before = _session->locations()->get_state();
4879 _session->locations()->add (loc, true);
4880 _session->set_auto_punch_location (loc);
4881 XMLNode &after = _session->locations()->get_state();
4882 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4884 XMLNode &before = tpl->get_state();
4885 tpl->set_hidden (false, this);
4886 tpl->set (start, end);
4887 XMLNode &after = tpl->get_state();
4888 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4891 commit_reversible_command ();
4894 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4895 * @param rs List to which found regions are added.
4896 * @param where Time to look at.
4897 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4900 Editor::get_regions_at (RegionSelection& rs, samplepos_t where, const TrackViewList& ts) const
4902 const TrackViewList* tracks;
4905 tracks = &track_views;
4910 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4912 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4915 boost::shared_ptr<Track> tr;
4916 boost::shared_ptr<Playlist> pl;
4918 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4920 boost::shared_ptr<RegionList> regions = pl->regions_at (where);
4922 for (RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4923 RegionView* rv = rtv->view()->find_view (*i);
4934 Editor::get_regions_after (RegionSelection& rs, samplepos_t where, const TrackViewList& ts) const
4936 const TrackViewList* tracks;
4939 tracks = &track_views;
4944 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4945 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4947 boost::shared_ptr<Track> tr;
4948 boost::shared_ptr<Playlist> pl;
4950 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4952 boost::shared_ptr<RegionList> regions = pl->regions_touched (where, max_samplepos);
4954 for (RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4956 RegionView* rv = rtv->view()->find_view (*i);
4967 /** Get regions using the following method:
4969 * Make a region list using:
4970 * (a) any selected regions
4971 * (b) the intersection of any selected tracks and the edit point(*)
4972 * (c) if neither exists, and edit_point == mouse, then whatever region is under the mouse
4974 * (*) NOTE: in this case, if 'No Selection = All Tracks' is active, search all tracks
4976 * Note that we have forced the rule that selected regions and selected tracks are mutually exclusive
4980 Editor::get_regions_from_selection_and_edit_point (EditIgnoreOption ignore, bool from_context_menu, bool from_outside_canvas)
4982 RegionSelection regions;
4984 if (_edit_point == EditAtMouse && entered_regionview && selection->tracks.empty() && selection->regions.empty()) {
4985 regions.add (entered_regionview);
4987 regions = selection->regions;
4990 if (regions.empty()) {
4991 TrackViewList tracks = selection->tracks;
4993 if (!tracks.empty()) {
4994 /* no region selected or entered, but some selected tracks:
4995 * act on all regions on the selected tracks at the edit point
4997 samplepos_t const where = get_preferred_edit_position (ignore, from_context_menu, from_outside_canvas);
4998 get_regions_at(regions, where, tracks);
5005 /** Get regions using the following method:
5007 * Make a region list using:
5008 * (a) any selected regions
5009 * (b) the intersection of any selected tracks and the edit point(*)
5010 * (c) if neither exists, then whatever region is under the mouse
5012 * (*) NOTE: in this case, if 'No Selection = All Tracks' is active, search all tracks
5014 * Note that we have forced the rule that selected regions and selected tracks are mutually exclusive
5017 Editor::get_regions_from_selection_and_mouse (samplepos_t pos)
5019 RegionSelection regions;
5021 if (entered_regionview && selection->tracks.empty() && selection->regions.empty()) {
5022 regions.add (entered_regionview);
5024 regions = selection->regions;
5027 if (regions.empty()) {
5028 TrackViewList tracks = selection->tracks;
5030 if (!tracks.empty()) {
5031 /* no region selected or entered, but some selected tracks:
5032 * act on all regions on the selected tracks at the edit point
5034 get_regions_at(regions, pos, tracks);
5041 /** Start with regions that are selected, or the entered regionview if none are selected.
5042 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
5043 * of the regions that we started with.
5047 Editor::get_regions_from_selection_and_entered () const
5049 RegionSelection regions = selection->regions;
5051 if (regions.empty() && entered_regionview) {
5052 regions.add (entered_regionview);
5059 Editor::get_regionviews_by_id (PBD::ID const id, RegionSelection & regions) const
5061 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5062 RouteTimeAxisView* rtav;
5064 if ((rtav = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5065 boost::shared_ptr<Playlist> pl;
5066 std::vector<boost::shared_ptr<Region> > results;
5067 boost::shared_ptr<Track> tr;
5069 if ((tr = rtav->track()) == 0) {
5074 if ((pl = (tr->playlist())) != 0) {
5075 boost::shared_ptr<Region> r = pl->region_by_id (id);
5077 RegionView* rv = rtav->view()->find_view (r);
5079 regions.push_back (rv);
5088 Editor::get_per_region_note_selection (list<pair<PBD::ID, set<boost::shared_ptr<Evoral::Note<Temporal::Beats> > > > > &selection) const
5091 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5092 MidiTimeAxisView* mtav;
5094 if ((mtav = dynamic_cast<MidiTimeAxisView*> (*i)) != 0) {
5096 mtav->get_per_region_note_selection (selection);
5103 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions, bool src_comparison)
5105 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5107 RouteTimeAxisView* tatv;
5109 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5111 boost::shared_ptr<Playlist> pl;
5112 vector<boost::shared_ptr<Region> > results;
5114 boost::shared_ptr<Track> tr;
5116 if ((tr = tatv->track()) == 0) {
5121 if ((pl = (tr->playlist())) != 0) {
5122 if (src_comparison) {
5123 pl->get_source_equivalent_regions (region, results);
5125 pl->get_region_list_equivalent_regions (region, results);
5129 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
5130 if ((marv = tatv->view()->find_view (*ir)) != 0) {
5131 regions.push_back (marv);
5140 Editor::regionview_from_region (boost::shared_ptr<Region> region) const
5142 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5143 RouteTimeAxisView* tatv;
5144 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5145 if (!tatv->track()) {
5148 RegionView* marv = tatv->view()->find_view (region);
5158 Editor::rtav_from_route (boost::shared_ptr<Route> route) const
5160 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5161 RouteTimeAxisView* rtav;
5162 if ((rtav = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5163 if (rtav->route() == route) {
5172 Editor::show_rhythm_ferret ()
5174 if (rhythm_ferret == 0) {
5175 rhythm_ferret = new RhythmFerret(*this);
5178 rhythm_ferret->set_session (_session);
5179 rhythm_ferret->show ();
5180 rhythm_ferret->present ();
5184 Editor::first_idle ()
5186 MessageDialog* dialog = 0;
5188 if (track_views.size() > 1) {
5189 Timers::TimerSuspender t;
5190 dialog = new MessageDialog (
5191 string_compose (_("Please wait while %1 loads visual data."), PROGRAM_NAME),
5195 ARDOUR_UI::instance()->flush_pending (60);
5198 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
5202 /* now that all regionviews should exist, setup region selection */
5206 for (list<PBD::ID>::iterator pr = selection->regions.pending.begin (); pr != selection->regions.pending.end (); ++pr) {
5207 /* this is cumulative: rs is NOT cleared each time */
5208 get_regionviews_by_id (*pr, rs);
5211 selection->set (rs);
5213 /* first idle adds route children (automation tracks), so we need to redisplay here */
5214 _routes->redisplay ();
5218 if (_session->undo_depth() == 0) {
5219 undo_action->set_sensitive(false);
5221 redo_action->set_sensitive(false);
5222 begin_selection_op_history ();
5228 Editor::_idle_resize (gpointer arg)
5230 return ((Editor*)arg)->idle_resize ();
5234 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
5236 if (resize_idle_id < 0) {
5237 /* https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#G-PRIORITY-HIGH-IDLE:CAPS
5238 * GTK+ uses G_PRIORITY_HIGH_IDLE + 10 for resizing operations, and G_PRIORITY_HIGH_IDLE + 20 for redrawing operations.
5239 * (This is done to ensure that any pending resizes are processed before any pending redraws, so that widgets are not redrawn twice unnecessarily.)
5241 resize_idle_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, _idle_resize, this, NULL);
5242 _pending_resize_amount = 0;
5245 /* make a note of the smallest resulting height, so that we can clamp the
5246 lower limit at TimeAxisView::hSmall */
5248 int32_t min_resulting = INT32_MAX;
5250 _pending_resize_amount += h;
5251 _pending_resize_view = view;
5253 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
5255 if (selection->tracks.contains (_pending_resize_view)) {
5256 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5257 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
5261 if (min_resulting < 0) {
5266 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
5267 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
5271 /** Handle pending resizing of tracks */
5273 Editor::idle_resize ()
5275 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
5277 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
5278 selection->tracks.contains (_pending_resize_view)) {
5280 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5281 if (*i != _pending_resize_view) {
5282 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
5287 _pending_resize_amount = 0;
5288 _group_tabs->set_dirty ();
5289 resize_idle_id = -1;
5297 ENSURE_GUI_THREAD (*this, &Editor::located);
5300 playhead_cursor->set_position (_session->audible_sample ());
5301 if (_follow_playhead && !_pending_initial_locate) {
5302 reset_x_origin_to_follow_playhead ();
5306 _pending_locate_request = false;
5307 _pending_initial_locate = false;
5308 _last_update_time = 0;
5312 Editor::region_view_added (RegionView * rv)
5314 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (rv);
5316 list<pair<PBD::ID const, list<Evoral::event_id_t> > >::iterator rnote;
5317 for (rnote = selection->pending_midi_note_selection.begin(); rnote != selection->pending_midi_note_selection.end(); ++rnote) {
5318 if (rv->region()->id () == (*rnote).first) {
5319 mrv->select_notes ((*rnote).second);
5320 selection->pending_midi_note_selection.erase(rnote);
5326 _summary->set_background_dirty ();
5328 mark_region_boundary_cache_dirty ();
5332 Editor::region_view_removed ()
5334 _summary->set_background_dirty ();
5336 mark_region_boundary_cache_dirty ();
5340 Editor::axis_view_by_stripable (boost::shared_ptr<Stripable> s) const
5342 for (TrackViewList::const_iterator j = track_views.begin (); j != track_views.end(); ++j) {
5343 if ((*j)->stripable() == s) {
5352 Editor::axis_view_by_control (boost::shared_ptr<AutomationControl> c) const
5354 for (TrackViewList::const_iterator j = track_views.begin (); j != track_views.end(); ++j) {
5355 if ((*j)->control() == c) {
5359 TimeAxisView::Children kids = (*j)->get_child_list ();
5361 for (TimeAxisView::Children::iterator k = kids.begin(); k != kids.end(); ++k) {
5362 if ((*k)->control() == c) {
5372 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
5376 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
5377 TimeAxisView* tv = time_axis_view_from_stripable (*i);
5387 Editor::suspend_route_redisplay ()
5390 _routes->suspend_redisplay();
5395 Editor::resume_route_redisplay ()
5398 _routes->redisplay(); // queue redisplay
5399 _routes->resume_redisplay();
5404 Editor::add_vcas (VCAList& vlist)
5408 for (VCAList::iterator v = vlist.begin(); v != vlist.end(); ++v) {
5409 sl.push_back (boost::dynamic_pointer_cast<Stripable> (*v));
5412 add_stripables (sl);
5416 Editor::add_routes (RouteList& rlist)
5420 for (RouteList::iterator r = rlist.begin(); r != rlist.end(); ++r) {
5424 add_stripables (sl);
5428 Editor::add_stripables (StripableList& sl)
5430 list<TimeAxisView*> new_views;
5431 boost::shared_ptr<VCA> v;
5432 boost::shared_ptr<Route> r;
5433 TrackViewList new_selection;
5434 bool from_scratch = (track_views.size() == 0);
5436 sl.sort (Stripable::Sorter());
5438 for (StripableList::iterator s = sl.begin(); s != sl.end(); ++s) {
5440 if ((*s)->is_foldbackbus()) {
5444 if ((v = boost::dynamic_pointer_cast<VCA> (*s)) != 0) {
5446 VCATimeAxisView* vtv = new VCATimeAxisView (*this, _session, *_track_canvas);
5448 new_views.push_back (vtv);
5450 } else if ((r = boost::dynamic_pointer_cast<Route> (*s)) != 0) {
5452 if (r->is_auditioner() || r->is_monitor()) {
5456 RouteTimeAxisView* rtv;
5457 DataType dt = r->input()->default_type();
5459 if (dt == ARDOUR::DataType::AUDIO) {
5460 rtv = new AudioTimeAxisView (*this, _session, *_track_canvas);
5462 } else if (dt == ARDOUR::DataType::MIDI) {
5463 rtv = new MidiTimeAxisView (*this, _session, *_track_canvas);
5466 throw unknown_type();
5469 new_views.push_back (rtv);
5470 track_views.push_back (rtv);
5471 new_selection.push_back (rtv);
5473 rtv->effective_gain_display ();
5475 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
5476 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
5480 if (new_views.size() > 0) {
5481 _routes->time_axis_views_added (new_views);
5482 //_summary->routes_added (new_selection); /* XXX requires RouteTimeAxisViewList */
5485 /* note: !new_selection.empty() means that we got some routes rather
5489 if (!from_scratch && !new_selection.empty()) {
5490 selection->set (new_selection);
5491 begin_selection_op_history();
5494 if (show_editor_mixer_when_tracks_arrive && !new_selection.empty()) {
5495 show_editor_mixer (true);
5498 editor_list_button.set_sensitive (true);
5502 Editor::timeaxisview_deleted (TimeAxisView *tv)
5504 if (tv == entered_track) {
5508 if (_session && _session->deletion_in_progress()) {
5509 /* the situation is under control */
5513 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
5515 if (dynamic_cast<AutomationTimeAxisView*> (tv)) {
5516 selection->remove (tv);
5520 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
5522 _routes->route_removed (tv);
5524 TimeAxisView::Children c = tv->get_child_list ();
5525 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
5526 if (entered_track == i->get()) {
5531 /* remove it from the list of track views */
5533 TrackViewList::iterator i;
5535 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
5536 i = track_views.erase (i);
5539 /* Update the route that is shown in the editor-mixer. */
5544 boost::shared_ptr<Route> route = rtav->route ();
5545 if (current_mixer_strip && current_mixer_strip->route() == route) {
5547 TimeAxisView* next_tv;
5549 if (track_views.empty()) {
5551 } else if (i == track_views.end()) {
5552 next_tv = track_views.front();
5557 // skip VCAs (cannot be selected, n/a in editor-mixer)
5558 if (dynamic_cast<VCATimeAxisView*> (next_tv)) {
5559 /* VCAs are sorted last in line -- route_sorter.h, jump to top */
5560 next_tv = track_views.front();
5562 if (dynamic_cast<VCATimeAxisView*> (next_tv)) {
5563 /* just in case: no master, only a VCA remains */
5569 set_selected_mixer_strip (*next_tv);
5571 /* make the editor mixer strip go away setting the
5572 * button to inactive (which also unticks the menu option)
5575 ActionManager::uncheck_toggleaction ("Editor/show-editor-mixer");
5581 Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection)
5587 DisplaySuspender ds;
5588 PresentationInfo::ChangeSuspender cs;
5590 if (apply_to_selection) {
5591 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end();) {
5593 TrackSelection::iterator j = i;
5596 hide_track_in_display (*i, false);
5601 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
5603 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
5604 /* this will hide the mixer strip */
5605 set_selected_mixer_strip (*tv);
5608 _routes->hide_track_in_display (*tv);
5613 Editor::show_track_in_display (TimeAxisView* tv, bool move_into_view)
5618 _routes->show_track_in_display (*tv);
5619 if (move_into_view) {
5620 ensure_time_axis_view_is_visible (*tv, false);
5625 Editor::sync_track_view_list_and_routes ()
5627 track_views = TrackViewList (_routes->views ());
5629 _summary->set_background_dirty();
5630 _group_tabs->set_dirty ();
5632 return false; // do not call again (until needed)
5636 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5638 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5643 /** Find a StripableTimeAxisView by the ID of its stripable */
5644 StripableTimeAxisView*
5645 Editor::get_stripable_time_axis_by_id (const PBD::ID& id) const
5647 StripableTimeAxisView* v;
5649 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5650 if((v = dynamic_cast<StripableTimeAxisView*>(*i)) != 0) {
5651 if(v->stripable()->id() == id) {
5661 Editor::fit_route_group (RouteGroup *g)
5663 TrackViewList ts = axis_views_from_routes (g->route_list ());
5668 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5670 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5673 _session->cancel_audition ();
5677 if (_session->is_auditioning()) {
5678 _session->cancel_audition ();
5679 if (r == last_audition_region) {
5684 _session->audition_region (r);
5685 last_audition_region = r;
5690 Editor::hide_a_region (boost::shared_ptr<Region> r)
5692 r->set_hidden (true);
5696 Editor::show_a_region (boost::shared_ptr<Region> r)
5698 r->set_hidden (false);
5702 Editor::audition_region_from_region_list ()
5704 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5708 Editor::step_edit_status_change (bool yn)
5711 start_step_editing ();
5713 stop_step_editing ();
5718 Editor::start_step_editing ()
5720 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5724 Editor::stop_step_editing ()
5726 step_edit_connection.disconnect ();
5730 Editor::check_step_edit ()
5732 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5733 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5735 mtv->check_step_edit ();
5739 return true; // do it again, till we stop
5743 Editor::scroll_press (Direction dir)
5745 ++_scroll_callbacks;
5747 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5748 /* delay the first auto-repeat */
5754 scroll_backward (1);
5762 scroll_up_one_track ();
5766 scroll_down_one_track ();
5770 /* do hacky auto-repeat */
5771 if (!_scroll_connection.connected ()) {
5773 _scroll_connection = Glib::signal_timeout().connect (
5774 sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100
5777 _scroll_callbacks = 0;
5784 Editor::scroll_release ()
5786 _scroll_connection.disconnect ();
5789 /** Queue a change for the Editor viewport x origin to follow the playhead */
5791 Editor::reset_x_origin_to_follow_playhead ()
5793 samplepos_t const sample = playhead_cursor->current_sample ();
5795 if (sample < _leftmost_sample || sample > _leftmost_sample + current_page_samples()) {
5797 if (_session->transport_speed() < 0) {
5799 if (sample > (current_page_samples() / 2)) {
5800 center_screen (sample-(current_page_samples()/2));
5802 center_screen (current_page_samples()/2);
5809 if (sample < _leftmost_sample) {
5811 if (_session->transport_rolling()) {
5812 /* rolling; end up with the playhead at the right of the page */
5813 l = sample - current_page_samples ();
5815 /* not rolling: end up with the playhead 1/4 of the way along the page */
5816 l = sample - current_page_samples() / 4;
5820 if (_session->transport_rolling()) {
5821 /* rolling: end up with the playhead on the left of the page */
5824 /* not rolling: end up with the playhead 3/4 of the way along the page */
5825 l = sample - 3 * current_page_samples() / 4;
5833 center_screen_internal (l + (current_page_samples() / 2), current_page_samples ());
5839 Editor::super_rapid_screen_update ()
5841 if (!_session || !_session->engine().running()) {
5845 /* METERING / MIXER STRIPS */
5847 /* update track meters, if required */
5848 if (contents().is_mapped() && meters_running) {
5849 RouteTimeAxisView* rtv;
5850 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5851 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5852 rtv->fast_update ();
5857 /* and any current mixer strip */
5858 if (current_mixer_strip) {
5859 current_mixer_strip->fast_update ();
5862 bool latent_locate = false;
5863 samplepos_t sample = _session->audible_sample (&latent_locate);
5864 const int64_t now = g_get_monotonic_time ();
5867 if (_session->exporting ()) {
5868 /* freewheel/export may be faster or slower than transport_speed() / SR.
5869 * Also exporting multiple ranges locates/jumps without a _pending_locate_request.
5871 _last_update_time = 0;
5874 if (!_session->transport_rolling () || _session->is_auditioning ()) {
5875 /* Do not interpolate the playhead position; just set it */
5876 _last_update_time = 0;
5879 if (_last_update_time > 0) {
5880 /* interpolate and smoothen playhead position */
5881 const double ds = (now - _last_update_time) * _session->transport_speed() * _session->nominal_sample_rate () * 1e-6;
5882 samplepos_t guess = playhead_cursor->current_sample () + rint (ds);
5883 err = sample - guess;
5885 guess += err * .12 + _err_screen_engine; // time-constant based on 25fps (super_rapid_screen_update)
5886 _err_screen_engine += .0144 * (err - _err_screen_engine); // tc^2
5889 printf ("eng: %ld gui:%ld (%+6.1f) diff: %6.1f (err: %7.2f)\n",
5891 err, _err_screen_engine);
5896 _err_screen_engine = 0;
5899 if (err > 8192 || latent_locate) {
5900 // in case of x-runs or freewheeling
5901 _last_update_time = 0;
5902 sample = _session->audible_sample ();
5904 _last_update_time = now;
5907 /* snapped cursor stuff (the snapped_cursor shows where an operation is going to occur) */
5909 MusicSample where (sample, 0);
5910 if (!UIConfiguration::instance().get_show_snapped_cursor()) {
5911 snapped_cursor->hide ();
5912 } else if (_edit_point == EditAtPlayhead && !_dragging_playhead) {
5913 /* EditAtPlayhead does not snap */
5914 } else if (_edit_point == EditAtSelectedMarker) {
5915 /* NOTE: I don't think EditAtSelectedMarker should snap. They are what they are.
5916 * however, the current editing code -does- snap so I'll draw it that way for now.
5918 if (!selection->markers.empty()) {
5919 MusicSample ms (selection->markers.front()->position(), 0);
5920 snap_to (ms); // should use snap_to_with_modifier?
5921 snapped_cursor->set_position (ms.sample);
5922 snapped_cursor->show ();
5924 } else if (mouse_sample (where.sample, ignored)) { // cursor is in the editing canvas. show it.
5925 snapped_cursor->show ();
5926 } else { // mouse is out of the editing canvas. hide the snapped_cursor
5927 snapped_cursor->hide ();
5930 /* There are a few reasons why we might not update the playhead / viewport stuff:
5932 * 1. we don't update things when there's a pending locate request, otherwise
5933 * when the editor requests a locate there is a chance that this method
5934 * will move the playhead before the locate request is processed, causing
5936 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5937 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5939 if (_pending_locate_request) {
5940 _last_update_time = 0;
5944 if (_dragging_playhead) {
5945 _last_update_time = 0;
5949 if (playhead_cursor->current_sample () == sample) {
5953 playhead_cursor->set_position (sample);
5955 if (_session->requested_return_sample() >= 0) {
5956 _last_update_time = 0;
5960 if (!_follow_playhead || pending_visual_change.being_handled) {
5961 /* We only do this if we aren't already
5962 * handling a visual change (ie if
5963 * pending_visual_change.being_handled is
5964 * false) so that these requests don't stack
5965 * up there are too many of them to handle in
5971 if (!_stationary_playhead) {
5972 reset_x_origin_to_follow_playhead ();
5974 samplepos_t const sample = playhead_cursor->current_sample ();
5975 double target = ((double)sample - (double)current_page_samples() / 2.0);
5976 if (target <= 0.0) {
5979 /* compare to EditorCursor::set_position() */
5980 double const old_pos = sample_to_pixel_unrounded (_leftmost_sample);
5981 double const new_pos = sample_to_pixel_unrounded (target);
5982 if (rint (new_pos) != rint (old_pos)) {
5983 reset_x_origin (pixel_to_sample (new_pos));
5990 Editor::session_going_away ()
5992 _have_idled = false;
5994 _session_connections.drop_connections ();
5996 super_rapid_screen_update_connection.disconnect ();
5998 selection->clear ();
5999 cut_buffer->clear ();
6001 clicked_regionview = 0;
6002 clicked_axisview = 0;
6003 clicked_routeview = 0;
6004 entered_regionview = 0;
6006 _last_update_time = 0;
6009 playhead_cursor->hide ();
6011 /* rip everything out of the list displays */
6016 _route_groups->clear ();
6018 /* do this first so that deleting a track doesn't reset cms to null
6019 and thus cause a leak.
6022 if (current_mixer_strip) {
6023 if (current_mixer_strip->get_parent() != 0) {
6024 global_hpacker.remove (*current_mixer_strip);
6026 delete current_mixer_strip;
6027 current_mixer_strip = 0;
6030 /* delete all trackviews */
6032 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6035 track_views.clear ();
6037 nudge_clock->set_session (0);
6039 editor_list_button.set_active(false);
6040 editor_list_button.set_sensitive(false);
6042 /* clear tempo/meter rulers */
6043 remove_metric_marks ();
6044 clear_marker_display ();
6050 stop_step_editing ();
6054 /* get rid of any existing editor mixer strip */
6056 WindowTitle title(Glib::get_application_name());
6057 title += _("Editor");
6059 own_window()->set_title (title.get_string());
6062 SessionHandlePtr::session_going_away ();
6066 Editor::trigger_script (int i)
6068 LuaInstance::instance()-> call_action (i);
6072 Editor::show_editor_list (bool yn)
6075 _editor_list_vbox.show ();
6077 _editor_list_vbox.hide ();
6082 Editor::change_region_layering_order (bool from_context_menu)
6084 const samplepos_t position = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context_menu);
6086 if (!clicked_routeview) {
6087 if (layering_order_editor) {
6088 layering_order_editor->hide ();
6093 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
6099 boost::shared_ptr<Playlist> pl = track->playlist();
6105 if (layering_order_editor == 0) {
6106 layering_order_editor = new RegionLayeringOrderEditor (*this);
6109 layering_order_editor->set_context (clicked_routeview->name(), _session, clicked_routeview, pl, position);
6110 layering_order_editor->maybe_present ();
6114 Editor::update_region_layering_order_editor ()
6116 if (layering_order_editor && layering_order_editor->is_visible ()) {
6117 change_region_layering_order (true);
6122 Editor::setup_fade_images ()
6124 _xfade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadein-linear")));
6125 _xfade_in_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadein-symmetric")));
6126 _xfade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadein-fast-cut")));
6127 _xfade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadein-slow-cut")));
6128 _xfade_in_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadein-constant-power")));
6130 _xfade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadeout-linear")));
6131 _xfade_out_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-symmetric")));
6132 _xfade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut")));
6133 _xfade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut")));
6134 _xfade_out_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-constant-power")));
6138 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
6140 Editor::action_menu_item (std::string const & name)
6142 Glib::RefPtr<Action> a = editor_actions->get_action (name);
6145 return *manage (a->create_menu_item ());
6149 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
6151 EventBox* b = manage (new EventBox);
6152 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
6153 Label* l = manage (new Label (name));
6157 _the_notebook.append_page (widget, *b);
6161 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
6163 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
6164 _the_notebook.set_current_page (_the_notebook.page_num (*page));
6167 if (ev->type == GDK_2BUTTON_PRESS) {
6169 /* double-click on a notebook tab shrinks or expands the notebook */
6171 if (_notebook_shrunk) {
6172 if (pre_notebook_shrink_pane_width) {
6173 edit_pane.set_divider (0, *pre_notebook_shrink_pane_width);
6175 _notebook_shrunk = false;
6177 pre_notebook_shrink_pane_width = edit_pane.get_divider();
6179 /* this expands the LHS of the edit pane to cover the notebook
6180 PAGE but leaves the tabs visible.
6182 edit_pane.set_divider (0, edit_pane.get_divider() + page->get_width());
6183 _notebook_shrunk = true;
6191 Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
6193 using namespace Menu_Helpers;
6195 MenuList& items = _control_point_context_menu.items ();
6198 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &Editor::edit_control_point), item)));
6199 items.push_back (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun (*this, &Editor::remove_control_point), item)));
6200 if (!can_remove_control_point (item)) {
6201 items.back().set_sensitive (false);
6204 _control_point_context_menu.popup (event->button.button, event->button.time);
6208 Editor::popup_note_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
6210 using namespace Menu_Helpers;
6212 NoteBase* note = reinterpret_cast<NoteBase*>(item->get_data("notebase"));
6217 /* We need to get the selection here and pass it to the operations, since
6218 popping up the menu will cause a region leave event which clears
6219 entered_regionview. */
6221 MidiRegionView& mrv = note->region_view();
6222 const RegionSelection rs = get_regions_from_selection_and_entered ();
6223 const uint32_t sel_size = mrv.selection_size ();
6225 MenuList& items = _note_context_menu.items();
6229 items.push_back(MenuElem(_("Delete"),
6230 sigc::mem_fun(mrv, &MidiRegionView::delete_selection)));
6233 items.push_back(MenuElem(_("Edit..."),
6234 sigc::bind(sigc::mem_fun(*this, &Editor::edit_notes), &mrv)));
6235 if (sel_size != 1) {
6236 items.back().set_sensitive (false);
6239 items.push_back(MenuElem(_("Transpose..."),
6240 sigc::bind(sigc::mem_fun(*this, &Editor::transpose_regions), rs)));
6243 items.push_back(MenuElem(_("Legatize"),
6244 sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, false)));
6246 items.back().set_sensitive (false);
6249 items.push_back(MenuElem(_("Quantize..."),
6250 sigc::bind(sigc::mem_fun(*this, &Editor::quantize_regions), rs)));
6252 items.push_back(MenuElem(_("Remove Overlap"),
6253 sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, true)));
6255 items.back().set_sensitive (false);
6258 items.push_back(MenuElem(_("Transform..."),
6259 sigc::bind(sigc::mem_fun(*this, &Editor::transform_regions), rs)));
6261 _note_context_menu.popup (event->button.button, event->button.time);
6265 Editor::zoom_vertical_modifier_released()
6267 _stepping_axis_view = 0;
6271 Editor::ui_parameter_changed (string parameter)
6273 if (parameter == "icon-set") {
6274 while (!_cursor_stack.empty()) {
6275 _cursor_stack.pop_back();
6277 _cursors->set_cursor_set (UIConfiguration::instance().get_icon_set());
6278 _cursor_stack.push_back(_cursors->grabber);
6279 edit_pane.set_drag_cursor (*_cursors->expand_left_right);
6280 editor_summary_pane.set_drag_cursor (*_cursors->expand_up_down);
6282 } else if (parameter == "draggable-playhead") {
6283 if (_verbose_cursor) {
6284 playhead_cursor->set_sensitive (UIConfiguration::instance().get_draggable_playhead());
6286 } else if (parameter == "use-note-bars-for-velocity") {
6287 ArdourCanvas::Note::set_show_velocity_bars (UIConfiguration::instance().get_use_note_bars_for_velocity());
6288 _track_canvas->request_redraw (_track_canvas->visible_area());
6289 } else if (parameter == "use-note-color-for-velocity") {
6290 /* handled individually by each MidiRegionView */
6295 Editor::use_own_window (bool and_fill_it)
6297 bool new_window = !own_window();
6299 Gtk::Window* win = Tabbable::use_own_window (and_fill_it);
6301 if (win && new_window) {
6302 win->set_name ("EditorWindow");
6304 ARDOUR_UI::instance()->setup_toplevel_window (*win, _("Editor"), this);
6306 // win->signal_realize().connect (*this, &Editor::on_realize);
6307 win->signal_event().connect (sigc::bind (sigc::ptr_fun (&Keyboard::catch_user_event_for_pre_dialog_focus), win));
6308 win->signal_event().connect (sigc::mem_fun (*this, &Editor::generic_event_handler));
6309 win->set_data ("ardour-bindings", bindings);
6314 DisplaySuspender ds;
6315 contents().show_all ();
6317 /* XXX: this is a bit unfortunate; it would probably
6318 be nicer if we could just call show () above rather
6319 than needing the show_all ()
6322 /* re-hide stuff if necessary */
6323 editor_list_button_toggled ();
6324 parameter_changed ("show-summary");
6325 parameter_changed ("show-group-tabs");
6326 parameter_changed ("show-zoom-tools");
6328 /* now reset all audio_time_axis heights, because widgets might need
6334 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6335 tv = (static_cast<TimeAxisView*>(*i));
6336 tv->reset_height ();
6339 if (current_mixer_strip) {
6340 current_mixer_strip->hide_things ();
6341 current_mixer_strip->parameter_changed ("mixer-element-visibility");