2 Copyright (C) 2000-2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /* Note: public Editor methods are documented in public_editor.h */
30 #include "ardour_ui.h"
32 * ardour_ui.h include was moved to the top of the list
33 * due to a conflicting definition of 'Style' between
34 * Apple's MacTypes.h and BarController.
37 #include <boost/none.hpp>
39 #include <sigc++/bind.h>
41 #include "pbd/convert.h"
42 #include "pbd/error.h"
43 #include "pbd/enumwriter.h"
44 #include "pbd/memento_command.h"
45 #include "pbd/unknown_type.h"
46 #include "pbd/unwind.h"
47 #include "pbd/stacktrace.h"
48 #include "pbd/timersub.h"
50 #include <glibmm/miscutils.h>
51 #include <glibmm/uriutils.h>
52 #include <gtkmm/image.h>
53 #include <gdkmm/color.h>
54 #include <gdkmm/bitmap.h>
56 #include <gtkmm/menu.h>
57 #include <gtkmm/menuitem.h>
59 #include "gtkmm2ext/bindings.h"
60 #include "gtkmm2ext/gtk_ui.h"
61 #include "gtkmm2ext/keyboard.h"
62 #include "gtkmm2ext/utils.h"
63 #include "gtkmm2ext/window_title.h"
64 #include "gtkmm2ext/cell_renderer_pixbuf_toggle.h"
66 #include "ardour/analysis_graph.h"
67 #include "ardour/audio_track.h"
68 #include "ardour/audioengine.h"
69 #include "ardour/audioregion.h"
70 #include "ardour/lmath.h"
71 #include "ardour/location.h"
72 #include "ardour/profile.h"
73 #include "ardour/route.h"
74 #include "ardour/route_group.h"
75 #include "ardour/session_playlists.h"
76 #include "ardour/tempo.h"
77 #include "ardour/utils.h"
78 #include "ardour/vca_manager.h"
79 #include "ardour/vca.h"
81 #include "canvas/debug.h"
82 #include "canvas/note.h"
83 #include "canvas/text.h"
85 #include "widgets/ardour_spacer.h"
86 #include "widgets/eventboxext.h"
87 #include "widgets/tooltips.h"
89 #include "control_protocol/control_protocol.h"
92 #include "analysis_window.h"
93 #include "audio_clock.h"
94 #include "audio_region_view.h"
95 #include "audio_streamview.h"
96 #include "audio_time_axis.h"
97 #include "automation_time_axis.h"
98 #include "bundle_manager.h"
99 #include "crossfade_edit.h"
102 #include "editing_convert.h"
104 #include "editor_cursors.h"
105 #include "editor_drag.h"
106 #include "editor_group_tabs.h"
107 #include "editor_locations.h"
108 #include "editor_regions.h"
109 #include "editor_route_groups.h"
110 #include "editor_routes.h"
111 #include "editor_snapshots.h"
112 #include "editor_summary.h"
113 #include "enums_convert.h"
114 #include "export_report.h"
115 #include "global_port_matrix.h"
116 #include "gui_object.h"
117 #include "gui_thread.h"
118 #include "keyboard.h"
119 #include "luainstance.h"
121 #include "midi_region_view.h"
122 #include "midi_time_axis.h"
123 #include "mixer_strip.h"
124 #include "mixer_ui.h"
125 #include "mouse_cursors.h"
126 #include "note_base.h"
127 #include "playlist_selector.h"
128 #include "public_editor.h"
129 #include "quantize_dialog.h"
130 #include "region_layering_order_editor.h"
131 #include "rgb_macros.h"
132 #include "rhythm_ferret.h"
133 #include "route_sorter.h"
134 #include "selection.h"
135 #include "simple_progress_dialog.h"
137 #include "grid_lines.h"
138 #include "time_axis_view.h"
139 #include "time_info_box.h"
141 #include "ui_config.h"
143 #include "vca_time_axis.h"
144 #include "verbose_cursor.h"
146 #include "pbd/i18n.h"
149 using namespace ARDOUR;
150 using namespace ArdourWidgets;
151 using namespace ARDOUR_UI_UTILS;
154 using namespace Glib;
155 using namespace Gtkmm2ext;
156 using namespace Editing;
158 using PBD::internationalize;
160 using Gtkmm2ext::Keyboard;
162 double Editor::timebar_height = 15.0;
164 static const gchar *_grid_type_strings[] = {
173 N_("1/3 (8th triplet)"), // or "1/12" ?
174 N_("1/6 (16th triplet)"),
175 N_("1/12 (32nd triplet)"),
176 N_("1/24 (64th triplet)"),
177 N_("1/5 (8th quintuplet)"),
178 N_("1/10 (16th quintuplet)"),
179 N_("1/20 (32nd quintuplet)"),
180 N_("1/7 (8th septuplet)"),
181 N_("1/14 (16th septuplet)"),
182 N_("1/28 (32nd septuplet)"),
189 static const gchar *_edit_point_strings[] = {
196 static const gchar *_edit_mode_strings[] = {
204 static const gchar *_zoom_focus_strings[] = {
214 #ifdef USE_RUBBERBAND
215 static const gchar *_rb_opt_strings[] = {
218 N_("Balanced multitimbral mixture"),
219 N_("Unpitched percussion with stable notes"),
220 N_("Crisp monophonic instrumental"),
221 N_("Unpitched solo percussion"),
222 N_("Resample without preserving pitch"),
227 /* Robin says: this should be odd to accomodate cairo drawing offset (width/2 rounds up to pixel boundary) */
229 #define COMBO_TRIANGLE_WIDTH 19 // ArdourButton _diameter (11) + 2 * arrow-padding (2*2) + 2 * text-padding (2*5)
231 #define COMBO_TRIANGLE_WIDTH 11 // as-measured for win/linux.
235 : PublicEditor (global_hpacker)
236 , editor_mixer_strip_width (Wide)
237 , constructed (false)
238 , _playlist_selector (0)
240 , no_save_visual (false)
241 , _leftmost_sample (0)
242 , samples_per_pixel (2048)
243 , zoom_focus (ZoomFocusPlayhead)
244 , mouse_mode (MouseObject)
245 , pre_internal_grid_type (GridTypeBeat)
246 , pre_internal_snap_mode (SnapOff)
247 , internal_grid_type (GridTypeBeat)
248 , internal_snap_mode (SnapOff)
249 , _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
250 , _notebook_shrunk (false)
251 , location_marker_color (0)
252 , location_range_color (0)
253 , location_loop_color (0)
254 , location_punch_color (0)
255 , location_cd_marker_color (0)
257 , _show_marker_lines (false)
258 , clicked_axisview (0)
259 , clicked_routeview (0)
260 , clicked_regionview (0)
261 , clicked_selection (0)
262 , clicked_control_point (0)
263 , button_release_can_deselect (true)
264 , _mouse_changed_selection (false)
265 , region_edit_menu_split_item (0)
266 , region_edit_menu_split_multichannel_item (0)
267 , track_region_edit_playlist_menu (0)
268 , track_edit_playlist_submenu (0)
269 , track_selection_edit_playlist_submenu (0)
270 , _popup_region_menu_item (0)
272 , _track_canvas_viewport (0)
273 , within_track_canvas (false)
274 , _verbose_cursor (0)
278 , range_marker_group (0)
279 , transport_marker_group (0)
280 , cd_marker_group (0)
281 , _time_markers_group (0)
282 , hv_scroll_group (0)
284 , cursor_scroll_group (0)
285 , no_scroll_group (0)
286 , _trackview_group (0)
287 , _drag_motion_group (0)
288 , _canvas_drop_zone (0)
289 , no_ruler_shown_update (false)
290 , ruler_grabbed_widget (0)
292 , minsec_mark_interval (0)
293 , minsec_mark_modulo (0)
295 , timecode_ruler_scale (timecode_show_many_hours)
296 , timecode_mark_modulo (0)
297 , timecode_nmarks (0)
298 , _samples_ruler_interval (0)
299 , bbt_ruler_scale (bbt_show_many)
302 , bbt_bar_helper_on (0)
303 , bbt_accent_modulo (0)
308 , visible_timebars (0)
309 , editor_ruler_menu (0)
313 , range_marker_bar (0)
314 , transport_marker_bar (0)
316 , minsec_label (_("Mins:Secs"))
317 , bbt_label (_("Bars:Beats"))
318 , timecode_label (_("Timecode"))
319 , samples_label (_("Samples"))
320 , tempo_label (_("Tempo"))
321 , meter_label (_("Meter"))
322 , mark_label (_("Location Markers"))
323 , range_mark_label (_("Range Markers"))
324 , transport_mark_label (_("Loop/Punch Ranges"))
325 , cd_mark_label (_("CD Markers"))
326 , videotl_label (_("Video Timeline"))
329 , playhead_cursor (0)
330 , _region_boundary_cache_dirty (true)
331 , edit_packer (4, 4, true)
332 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
333 , horizontal_adjustment (0.0, 0.0, 1e16)
334 , unused_adjustment (0.0, 0.0, 10.0, 400.0)
335 , controls_layout (unused_adjustment, vertical_adjustment)
336 , _scroll_callbacks (0)
337 , _visible_canvas_width (0)
338 , _visible_canvas_height (0)
339 , _full_canvas_height (0)
340 , edit_controls_left_menu (0)
341 , edit_controls_right_menu (0)
342 , visual_change_queued(false)
343 , _last_update_time (0)
344 , _err_screen_engine (0)
345 , cut_buffer_start (0)
346 , cut_buffer_length (0)
347 , button_bindings (0)
348 , last_paste_pos (-1)
351 , current_interthread_info (0)
352 , analysis_window (0)
353 , select_new_marker (false)
355 , scrubbing_direction (0)
356 , scrub_reversals (0)
357 , scrub_reverse_distance (0)
358 , have_pending_keyboard_selection (false)
359 , pending_keyboard_selection_start (0)
360 , _grid_type (GridTypeBeat)
361 , _snap_mode (SnapOff)
362 , ignore_gui_changes (false)
363 , _drags (new DragManager (this))
365 /* , last_event_time { 0, 0 } */ /* this initialization style requires C++11 */
366 , _dragging_playhead (false)
367 , _dragging_edit_point (false)
368 , _follow_playhead (true)
369 , _stationary_playhead (false)
372 , global_rect_group (0)
373 , time_line_group (0)
374 , tempo_marker_menu (0)
375 , meter_marker_menu (0)
377 , range_marker_menu (0)
378 , transport_marker_menu (0)
379 , new_transport_marker_menu (0)
381 , marker_menu_item (0)
382 , bbt_beat_subdivision (4)
383 , _visible_track_count (-1)
384 , toolbar_selection_clock_table (2,3)
385 , automation_mode_button (_("mode"))
386 , selection (new Selection (this, true))
387 , cut_buffer (new Selection (this, false))
388 , _selection_memento (new SelectionMemento())
389 , _all_region_actions_sensitized (false)
390 , _ignore_region_action (false)
391 , _last_region_menu_was_main (false)
392 , _track_selection_change_without_scroll (false)
393 , _editor_track_selection_change_without_scroll (false)
394 , cd_marker_bar_drag_rect (0)
395 , range_bar_drag_rect (0)
396 , transport_bar_drag_rect (0)
397 , transport_bar_range_rect (0)
398 , transport_bar_preroll_rect (0)
399 , transport_bar_postroll_rect (0)
400 , transport_loop_range_rect (0)
401 , transport_punch_range_rect (0)
402 , transport_punchin_line (0)
403 , transport_punchout_line (0)
404 , transport_preroll_rect (0)
405 , transport_postroll_rect (0)
407 , rubberband_rect (0)
413 , autoscroll_horizontal_allowed (false)
414 , autoscroll_vertical_allowed (false)
416 , autoscroll_widget (0)
417 , show_gain_after_trim (false)
418 , selection_op_cmd_depth (0)
419 , selection_op_history_it (0)
420 , no_save_instant (false)
422 , current_mixer_strip (0)
423 , show_editor_mixer_when_tracks_arrive (false)
424 , nudge_clock (new AudioClock (X_("nudge"), false, X_("nudge"), true, false, true))
425 , current_stepping_trackview (0)
426 , last_track_height_step_timestamp (0)
428 , entered_regionview (0)
429 , clear_entered_track (false)
430 , _edit_point (EditAtMouse)
431 , meters_running (false)
433 , _have_idled (false)
434 , resize_idle_id (-1)
435 , _pending_resize_amount (0)
436 , _pending_resize_view (0)
437 , _pending_locate_request (false)
438 , _pending_initial_locate (false)
442 , layering_order_editor (0)
443 , _last_cut_copy_source_track (0)
444 , _region_selection_change_updates_region_list (true)
446 , _following_mixer_selection (false)
447 , _control_point_toggled_on_press (false)
448 , _stepping_axis_view (0)
449 , quantize_dialog (0)
450 , _main_menu_disabler (0)
451 , myactions (X_("editor"))
453 /* we are a singleton */
455 PublicEditor::_instance = this;
459 last_event_time.tv_sec = 0;
460 last_event_time.tv_usec = 0;
462 selection_op_history.clear();
465 grid_type_strings = I18N (_grid_type_strings);
466 zoom_focus_strings = I18N (_zoom_focus_strings);
467 edit_mode_strings = I18N (_edit_mode_strings);
468 edit_point_strings = I18N (_edit_point_strings);
469 #ifdef USE_RUBBERBAND
470 rb_opt_strings = I18N (_rb_opt_strings);
474 build_edit_mode_menu();
475 build_zoom_focus_menu();
476 build_track_count_menu();
477 build_grid_type_menu();
478 build_edit_point_menu();
480 location_marker_color = UIConfiguration::instance().color ("location marker");
481 location_range_color = UIConfiguration::instance().color ("location range");
482 location_cd_marker_color = UIConfiguration::instance().color ("location cd marker");
483 location_loop_color = UIConfiguration::instance().color ("location loop");
484 location_punch_color = UIConfiguration::instance().color ("location punch");
486 timebar_height = std::max (12., ceil (15. * UIConfiguration::instance().get_ui_scale()));
488 TimeAxisView::setup_sizes ();
489 ArdourMarker::setup_sizes (timebar_height);
490 TempoCurve::setup_sizes (timebar_height);
492 bbt_label.set_name ("EditorRulerLabel");
493 bbt_label.set_size_request (-1, (int)timebar_height);
494 bbt_label.set_alignment (1.0, 0.5);
495 bbt_label.set_padding (5,0);
497 bbt_label.set_no_show_all();
498 minsec_label.set_name ("EditorRulerLabel");
499 minsec_label.set_size_request (-1, (int)timebar_height);
500 minsec_label.set_alignment (1.0, 0.5);
501 minsec_label.set_padding (5,0);
502 minsec_label.hide ();
503 minsec_label.set_no_show_all();
504 timecode_label.set_name ("EditorRulerLabel");
505 timecode_label.set_size_request (-1, (int)timebar_height);
506 timecode_label.set_alignment (1.0, 0.5);
507 timecode_label.set_padding (5,0);
508 timecode_label.hide ();
509 timecode_label.set_no_show_all();
510 samples_label.set_name ("EditorRulerLabel");
511 samples_label.set_size_request (-1, (int)timebar_height);
512 samples_label.set_alignment (1.0, 0.5);
513 samples_label.set_padding (5,0);
514 samples_label.hide ();
515 samples_label.set_no_show_all();
517 tempo_label.set_name ("EditorRulerLabel");
518 tempo_label.set_size_request (-1, (int)timebar_height);
519 tempo_label.set_alignment (1.0, 0.5);
520 tempo_label.set_padding (5,0);
522 tempo_label.set_no_show_all();
524 meter_label.set_name ("EditorRulerLabel");
525 meter_label.set_size_request (-1, (int)timebar_height);
526 meter_label.set_alignment (1.0, 0.5);
527 meter_label.set_padding (5,0);
529 meter_label.set_no_show_all();
531 if (Profile->get_trx()) {
532 mark_label.set_text (_("Markers"));
534 mark_label.set_name ("EditorRulerLabel");
535 mark_label.set_size_request (-1, (int)timebar_height);
536 mark_label.set_alignment (1.0, 0.5);
537 mark_label.set_padding (5,0);
539 mark_label.set_no_show_all();
541 cd_mark_label.set_name ("EditorRulerLabel");
542 cd_mark_label.set_size_request (-1, (int)timebar_height);
543 cd_mark_label.set_alignment (1.0, 0.5);
544 cd_mark_label.set_padding (5,0);
545 cd_mark_label.hide();
546 cd_mark_label.set_no_show_all();
548 videotl_bar_height = 4;
549 videotl_label.set_name ("EditorRulerLabel");
550 videotl_label.set_size_request (-1, (int)timebar_height * videotl_bar_height);
551 videotl_label.set_alignment (1.0, 0.5);
552 videotl_label.set_padding (5,0);
553 videotl_label.hide();
554 videotl_label.set_no_show_all();
556 range_mark_label.set_name ("EditorRulerLabel");
557 range_mark_label.set_size_request (-1, (int)timebar_height);
558 range_mark_label.set_alignment (1.0, 0.5);
559 range_mark_label.set_padding (5,0);
560 range_mark_label.hide();
561 range_mark_label.set_no_show_all();
563 transport_mark_label.set_name ("EditorRulerLabel");
564 transport_mark_label.set_size_request (-1, (int)timebar_height);
565 transport_mark_label.set_alignment (1.0, 0.5);
566 transport_mark_label.set_padding (5,0);
567 transport_mark_label.hide();
568 transport_mark_label.set_no_show_all();
570 initialize_canvas ();
572 CairoWidget::set_focus_handler (sigc::mem_fun (ARDOUR_UI::instance(), &ARDOUR_UI::reset_focus));
574 _summary = new EditorSummary (this);
576 selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
577 selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
579 editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
581 selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
582 selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
584 edit_controls_vbox.set_spacing (0);
585 vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
586 _track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
588 HBox* h = manage (new HBox);
589 _group_tabs = new EditorGroupTabs (this);
590 if (!ARDOUR::Profile->get_trx()) {
591 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 _snapshots = new EditorSnapshots (this);
651 _locations = new EditorLocations (this);
652 _time_info_box = new TimeInfoBox ("EditorTimeInfo", true);
654 /* these are static location signals */
656 Location::start_changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context());
657 Location::end_changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context());
658 Location::changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context());
660 add_notebook_page (_("Regions"), _regions->widget ());
661 add_notebook_page (_("Tracks & Busses"), _routes->widget ());
662 add_notebook_page (_("Snapshots"), _snapshots->widget ());
663 add_notebook_page (_("Track & Bus Groups"), _route_groups->widget ());
664 add_notebook_page (_("Ranges & Marks"), _locations->widget ());
666 _the_notebook.set_show_tabs (true);
667 _the_notebook.set_scrollable (true);
668 _the_notebook.popup_disable ();
669 _the_notebook.set_tab_pos (Gtk::POS_RIGHT);
670 _the_notebook.show_all ();
672 _notebook_shrunk = false;
675 /* Pick up some settings we need to cache, early */
677 XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
680 settings->get_property ("notebook-shrunk", _notebook_shrunk);
683 editor_summary_pane.set_check_divider_position (true);
684 editor_summary_pane.add (edit_packer);
686 Button* summary_arrow_left = manage (new Button);
687 summary_arrow_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
688 summary_arrow_left->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), LEFT)));
689 summary_arrow_left->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
691 Button* summary_arrow_right = manage (new Button);
692 summary_arrow_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
693 summary_arrow_right->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), RIGHT)));
694 summary_arrow_right->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
696 VBox* summary_arrows_left = manage (new VBox);
697 summary_arrows_left->pack_start (*summary_arrow_left);
699 VBox* summary_arrows_right = manage (new VBox);
700 summary_arrows_right->pack_start (*summary_arrow_right);
702 Frame* summary_frame = manage (new Frame);
703 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
705 summary_frame->add (*_summary);
706 summary_frame->show ();
708 _summary_hbox.pack_start (*summary_arrows_left, false, false);
709 _summary_hbox.pack_start (*summary_frame, true, true);
710 _summary_hbox.pack_start (*summary_arrows_right, false, false);
712 if (!ARDOUR::Profile->get_trx()) {
713 editor_summary_pane.add (_summary_hbox);
716 edit_pane.set_check_divider_position (true);
717 edit_pane.add (editor_summary_pane);
718 if (!ARDOUR::Profile->get_trx()) {
719 _editor_list_vbox.pack_start (*_time_info_box, false, false, 0);
720 _editor_list_vbox.pack_start (_the_notebook);
721 edit_pane.add (_editor_list_vbox);
722 edit_pane.set_child_minsize (_editor_list_vbox, 30); /* rough guess at width of notebook tabs */
725 edit_pane.set_drag_cursor (*_cursors->expand_left_right);
726 editor_summary_pane.set_drag_cursor (*_cursors->expand_up_down);
729 if (!settings || !settings->get_property ("edit-horizontal-pane-pos", fract) || fract > 1.0) {
730 /* initial allocation is 90% to canvas, 10% to notebook */
733 edit_pane.set_divider (0, fract);
735 if (!settings || !settings->get_property ("edit-vertical-pane-pos", fract) || fract > 1.0) {
736 /* initial allocation is 90% to canvas, 10% to summary */
739 editor_summary_pane.set_divider (0, fract);
741 global_vpacker.set_spacing (0);
742 global_vpacker.set_border_width (0);
744 /* the next three EventBoxes provide the ability for their child widgets to have a background color. That is all. */
746 Gtk::EventBox* ebox = manage (new Gtk::EventBox); // a themeable box
747 ebox->set_name("EditorWindow");
748 ebox->add (ebox_hpacker);
750 Gtk::EventBox* epane_box = manage (new EventBoxExt); // a themeable box
751 epane_box->set_name("EditorWindow");
752 epane_box->add (edit_pane);
754 Gtk::EventBox* epane_box2 = manage (new EventBoxExt); // a themeable box
755 epane_box2->set_name("EditorWindow");
756 epane_box2->add (global_vpacker);
758 ArdourWidgets::ArdourDropShadow *toolbar_shadow = manage (new (ArdourWidgets::ArdourDropShadow));
759 toolbar_shadow->set_size_request (-1, 4);
760 toolbar_shadow->set_mode(ArdourWidgets::ArdourDropShadow::DropShadowBoth);
761 toolbar_shadow->set_name("EditorWindow");
762 toolbar_shadow->show();
764 global_vpacker.pack_start (*toolbar_shadow, false, false);
765 global_vpacker.pack_start (*ebox, false, false);
766 global_vpacker.pack_start (*epane_box, true, true);
767 global_hpacker.pack_start (*epane_box2, true, true);
769 /* need to show the "contents" widget so that notebook will show if tab is switched to
772 global_hpacker.show ();
776 /* register actions now so that set_state() can find them and set toggles/checks etc */
783 _playlist_selector = new PlaylistSelector();
784 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
786 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), boost::bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
790 nudge_forward_button.set_name ("nudge button");
791 nudge_forward_button.set_icon(ArdourIcon::NudgeRight);
793 nudge_backward_button.set_name ("nudge button");
794 nudge_backward_button.set_icon(ArdourIcon::NudgeLeft);
796 fade_context_menu.set_name ("ArdourContextMenu");
798 Gtkmm2ext::Keyboard::the_keyboard().ZoomVerticalModifierReleased.connect (sigc::mem_fun (*this, &Editor::zoom_vertical_modifier_released));
800 /* allow external control surfaces/protocols to do various things */
802 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
803 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
804 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
805 ControlProtocol::Undo.connect (*this, invalidator (*this), boost::bind (&Editor::undo, this, true), gui_context());
806 ControlProtocol::Redo.connect (*this, invalidator (*this), boost::bind (&Editor::redo, this, true), gui_context());
807 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), boost::bind (&Editor::control_scroll, this, _1), gui_context());
808 ControlProtocol::StepTracksUp.connect (*this, invalidator (*this), boost::bind (&Editor::control_step_tracks_up, this), gui_context());
809 ControlProtocol::StepTracksDown.connect (*this, invalidator (*this), boost::bind (&Editor::control_step_tracks_down, this), gui_context());
810 ControlProtocol::GotoView.connect (*this, invalidator (*this), boost::bind (&Editor::control_view, this, _1), gui_context());
811 ControlProtocol::CloseDialog.connect (*this, invalidator (*this), Keyboard::close_current_dialog, gui_context());
812 ControlProtocol::VerticalZoomInAll.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_in_all, this), gui_context());
813 ControlProtocol::VerticalZoomOutAll.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_out_all, this), gui_context());
814 ControlProtocol::VerticalZoomInSelected.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_in_selected, this), gui_context());
815 ControlProtocol::VerticalZoomOutSelected.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_out_selected, this), gui_context());
817 ControlProtocol::AddStripableToSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Add), gui_context());
818 ControlProtocol::RemoveStripableFromSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Toggle), gui_context());
819 ControlProtocol::SetStripableSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Set), gui_context());
820 ControlProtocol::ToggleStripableSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Toggle), gui_context());
821 ControlProtocol::ClearStripableSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_unselect, this), gui_context());
823 BasicUI::AccessAction.connect (*this, invalidator (*this), boost::bind (&Editor::access_action, this, _1, _2), gui_context());
827 ARDOUR_UI::instance()->Escape.connect (*this, invalidator (*this), boost::bind (&Editor::escape, this), gui_context());
829 /* problematic: has to return a value and thus cannot be x-thread */
831 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
833 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&Editor::parameter_changed, this, _1), gui_context());
834 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &Editor::ui_parameter_changed));
836 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
838 _ignore_region_action = false;
839 _last_region_menu_was_main = false;
840 _popup_region_menu_item = 0;
842 _show_marker_lines = false;
844 /* Button bindings */
846 button_bindings = new Bindings ("editor-mouse");
848 XMLNode* node = button_settings();
850 for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
851 button_bindings->load_operation (**i);
857 /* grab current parameter state */
858 boost::function<void (string)> pc (boost::bind (&Editor::ui_parameter_changed, this, _1));
859 UIConfiguration::instance().map_parameters (pc);
861 setup_fade_images ();
863 set_grid_to (GridTypeNone);
870 delete button_bindings;
872 delete _route_groups;
873 delete _track_canvas_viewport;
876 delete _verbose_cursor;
877 delete quantize_dialog;
883 delete _playlist_selector;
884 delete _time_info_box;
889 LuaInstance::destroy_instance ();
891 for (list<XMLNode *>::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) {
894 for (std::map<ARDOUR::FadeShape, Gtk::Image*>::const_iterator i = _xfade_in_images.begin(); i != _xfade_in_images.end (); ++i) {
897 for (std::map<ARDOUR::FadeShape, Gtk::Image*>::const_iterator i = _xfade_out_images.begin(); i != _xfade_out_images.end (); ++i) {
903 Editor::button_settings () const
905 XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
906 XMLNode* node = find_named_node (*settings, X_("Buttons"));
909 node = new XMLNode (X_("Buttons"));
916 Editor::get_smart_mode () const
918 return ((current_mouse_mode() == MouseObject) && smart_mode_action->get_active());
922 Editor::catch_vanishing_regionview (RegionView *rv)
924 /* note: the selection will take care of the vanishing
925 audioregionview by itself.
928 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
932 if (clicked_regionview == rv) {
933 clicked_regionview = 0;
936 if (entered_regionview == rv) {
937 set_entered_regionview (0);
940 if (!_all_region_actions_sensitized) {
941 sensitize_all_region_actions (true);
946 Editor::set_entered_regionview (RegionView* rv)
948 if (rv == entered_regionview) {
952 if (entered_regionview) {
953 entered_regionview->exited ();
956 entered_regionview = rv;
958 if (entered_regionview != 0) {
959 entered_regionview->entered ();
962 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
963 /* This RegionView entry might have changed what region actions
964 are allowed, so sensitize them all in case a key is pressed.
966 sensitize_all_region_actions (true);
971 Editor::set_entered_track (TimeAxisView* tav)
974 entered_track->exited ();
980 entered_track->entered ();
985 Editor::instant_save ()
987 if (!constructed || !ARDOUR_UI::instance()->session_loaded || no_save_instant) {
992 _session->add_instant_xml(get_state());
994 Config->add_instant_xml(get_state());
999 Editor::control_vertical_zoom_in_all ()
1001 tav_zoom_smooth (false, true);
1005 Editor::control_vertical_zoom_out_all ()
1007 tav_zoom_smooth (true, true);
1011 Editor::control_vertical_zoom_in_selected ()
1013 tav_zoom_smooth (false, false);
1017 Editor::control_vertical_zoom_out_selected ()
1019 tav_zoom_smooth (true, false);
1023 Editor::control_view (uint32_t view)
1025 goto_visual_state (view);
1029 Editor::control_unselect ()
1031 selection->clear_tracks ();
1035 Editor::control_select (boost::shared_ptr<Stripable> s, Selection::Operation op)
1037 TimeAxisView* tav = time_axis_view_from_stripable (s);
1041 case Selection::Add:
1042 selection->add (tav);
1044 case Selection::Toggle:
1045 selection->toggle (tav);
1047 case Selection::Extend:
1049 case Selection::Set:
1050 selection->set (tav);
1054 selection->clear_tracks ();
1059 Editor::control_step_tracks_up ()
1061 scroll_tracks_up_line ();
1065 Editor::control_step_tracks_down ()
1067 scroll_tracks_down_line ();
1071 Editor::control_scroll (float fraction)
1073 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
1079 double step = fraction * current_page_samples();
1082 _control_scroll_target is an optional<T>
1084 it acts like a pointer to an samplepos_t, with
1085 a operator conversion to boolean to check
1086 that it has a value could possibly use
1087 playhead_cursor->current_sample to store the
1088 value and a boolean in the class to know
1089 when it's out of date
1092 if (!_control_scroll_target) {
1093 _control_scroll_target = _session->transport_sample();
1094 _dragging_playhead = true;
1097 if ((fraction < 0.0f) && (*_control_scroll_target <= (samplepos_t) fabs(step))) {
1098 *_control_scroll_target = 0;
1099 } else if ((fraction > 0.0f) && (max_samplepos - *_control_scroll_target < step)) {
1100 *_control_scroll_target = max_samplepos - (current_page_samples()*2); // allow room for slop in where the PH is on the screen
1102 *_control_scroll_target += (samplepos_t) trunc (step);
1105 /* move visuals, we'll catch up with it later */
1107 playhead_cursor->set_position (*_control_scroll_target);
1108 UpdateAllTransportClocks (*_control_scroll_target);
1110 if (*_control_scroll_target > (current_page_samples() / 2)) {
1111 /* try to center PH in window */
1112 reset_x_origin (*_control_scroll_target - (current_page_samples()/2));
1118 Now we do a timeout to actually bring the session to the right place
1119 according to the playhead. This is to avoid reading disk buffers on every
1120 call to control_scroll, which is driven by ScrollTimeline and therefore
1121 probably by a control surface wheel which can generate lots of events.
1123 /* cancel the existing timeout */
1125 control_scroll_connection.disconnect ();
1127 /* add the next timeout */
1129 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
1133 Editor::deferred_control_scroll (samplepos_t /*target*/)
1135 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
1136 /* reset for next stream */
1137 _control_scroll_target = boost::none;
1138 _dragging_playhead = false;
1143 Editor::access_action (const std::string& action_group, const std::string& action_item)
1149 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
1152 act = ActionManager::get_action (action_group.c_str(), action_item.c_str());
1160 Editor::set_toggleaction (const std::string& action_group, const std::string& action_item, bool s)
1162 ActionManager::set_toggleaction_state (action_group.c_str(), action_item.c_str(), s);
1166 Editor::on_realize ()
1170 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
1171 start_lock_event_timing ();
1176 Editor::start_lock_event_timing ()
1178 /* check if we should lock the GUI every 30 seconds */
1180 Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::lock_timeout_callback), 30 * 1000);
1184 Editor::generic_event_handler (GdkEvent* ev)
1187 case GDK_BUTTON_PRESS:
1188 case GDK_BUTTON_RELEASE:
1189 case GDK_MOTION_NOTIFY:
1191 case GDK_KEY_RELEASE:
1192 if (contents().is_mapped()) {
1193 gettimeofday (&last_event_time, 0);
1197 case GDK_LEAVE_NOTIFY:
1198 switch (ev->crossing.detail) {
1199 case GDK_NOTIFY_UNKNOWN:
1200 case GDK_NOTIFY_INFERIOR:
1201 case GDK_NOTIFY_ANCESTOR:
1203 case GDK_NOTIFY_VIRTUAL:
1204 case GDK_NOTIFY_NONLINEAR:
1205 case GDK_NOTIFY_NONLINEAR_VIRTUAL:
1206 /* leaving window, so reset focus, thus ending any and
1207 all text entry operations.
1209 ARDOUR_UI::instance()->reset_focus (&contents());
1222 Editor::lock_timeout_callback ()
1224 struct timeval now, delta;
1226 gettimeofday (&now, 0);
1228 timersub (&now, &last_event_time, &delta);
1230 if (delta.tv_sec > (time_t) UIConfiguration::instance().get_lock_gui_after_seconds()) {
1232 /* don't call again. Returning false will effectively
1233 disconnect us from the timer callback.
1235 unlock() will call start_lock_event_timing() to get things
1245 Editor::map_position_change (samplepos_t sample)
1247 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, sample)
1249 if (_session == 0) {
1253 if (_follow_playhead) {
1254 center_screen (sample);
1257 playhead_cursor->set_position (sample);
1261 Editor::center_screen (samplepos_t sample)
1263 samplecnt_t const page = _visible_canvas_width * samples_per_pixel;
1265 /* if we're off the page, then scroll.
1268 if (sample < _leftmost_sample || sample >= _leftmost_sample + page) {
1269 center_screen_internal (sample, page);
1274 Editor::center_screen_internal (samplepos_t sample, float page)
1278 if (sample > page) {
1279 sample -= (samplepos_t) page;
1284 reset_x_origin (sample);
1289 Editor::update_title ()
1291 ENSURE_GUI_THREAD (*this, &Editor::update_title);
1293 if (!own_window()) {
1298 bool dirty = _session->dirty();
1300 string session_name;
1302 if (_session->snap_name() != _session->name()) {
1303 session_name = _session->snap_name();
1305 session_name = _session->name();
1309 session_name = "*" + session_name;
1312 WindowTitle title(session_name);
1313 title += S_("Window|Editor");
1314 title += Glib::get_application_name();
1315 own_window()->set_title (title.get_string());
1317 /* ::session_going_away() will have taken care of it */
1322 Editor::set_session (Session *t)
1324 SessionHandlePtr::set_session (t);
1330 /* initialize _leftmost_sample to the extents of the session
1331 * this prevents a bogus setting of leftmost = "0" if the summary view asks for the leftmost sample
1332 * before the visible state has been loaded from instant.xml */
1333 _leftmost_sample = session_gui_extents().first;
1335 _playlist_selector->set_session (_session);
1336 nudge_clock->set_session (_session);
1337 _summary->set_session (_session);
1338 _group_tabs->set_session (_session);
1339 _route_groups->set_session (_session);
1340 _regions->set_session (_session);
1341 _snapshots->set_session (_session);
1342 _routes->set_session (_session);
1343 _locations->set_session (_session);
1344 _time_info_box->set_session (_session);
1346 if (rhythm_ferret) {
1347 rhythm_ferret->set_session (_session);
1350 if (analysis_window) {
1351 analysis_window->set_session (_session);
1355 sfbrowser->set_session (_session);
1358 compute_fixed_ruler_scale ();
1360 /* Make sure we have auto loop and auto punch ranges */
1362 Location* loc = _session->locations()->auto_loop_location();
1364 loc->set_name (_("Loop"));
1367 loc = _session->locations()->auto_punch_location();
1370 loc->set_name (_("Punch"));
1373 refresh_location_display ();
1375 /* This must happen after refresh_location_display(), as (amongst other things) we restore
1376 * the selected Marker; this needs the LocationMarker list to be available.
1378 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1379 set_state (*node, Stateful::loading_state_version);
1381 /* catch up on selection state, etc. */
1384 sc.add (Properties::selected);
1385 presentation_info_changed (sc);
1387 /* catch up with the playhead */
1389 _session->request_locate (playhead_cursor->current_sample ());
1390 _pending_initial_locate = true;
1394 /* These signals can all be emitted by a non-GUI thread. Therefore the
1395 handlers for them must not attempt to directly interact with the GUI,
1396 but use PBD::Signal<T>::connect() which accepts an event loop
1397 ("context") where the handler will be asked to run.
1400 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::step_edit_status_change, this, _1), gui_context());
1401 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1402 _session->TransportLooped.connect (_session_connections, invalidator (*this), boost::bind (&Editor::transport_looped, this), gui_context());
1403 _session->PositionChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_position_change, this, _1), gui_context());
1404 _session->vca_manager().VCAAdded.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_vcas, this, _1), gui_context());
1405 _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_routes, this, _1), gui_context());
1406 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1407 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::tempo_map_changed, this, _1), gui_context());
1408 _session->tempo_map().MetricPositionChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::tempometric_position_changed, this, _1), gui_context());
1409 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1410 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::parameter_changed, this, _1), gui_context());
1411 _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Editor::session_state_saved, this, _1), gui_context());
1412 _session->locations()->added.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_new_location, this, _1), gui_context());
1413 _session->locations()->removed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::location_gone, this, _1), gui_context());
1414 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1415 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1417 playhead_cursor->track_canvas_item().reparent ((ArdourCanvas::Item*) get_cursor_scroll_group());
1418 playhead_cursor->show ();
1420 snapped_cursor->track_canvas_item().reparent ((ArdourCanvas::Item*) get_cursor_scroll_group());
1421 snapped_cursor->set_color (UIConfiguration::instance().color ("edit point"));
1422 snapped_cursor->show ();
1424 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1425 Config->map_parameters (pc);
1426 _session->config.map_parameters (pc);
1428 restore_ruler_visibility ();
1429 //tempo_map_changed (PropertyChange (0));
1430 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1432 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1433 (static_cast<TimeAxisView*>(*i))->set_samples_per_pixel (samples_per_pixel);
1436 super_rapid_screen_update_connection = Timers::super_rapid_connect (
1437 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1440 /* register for undo history */
1441 _session->register_with_memento_command_factory(id(), this);
1442 _session->register_with_memento_command_factory(_selection_memento->id(), _selection_memento);
1444 LuaInstance::instance()->set_session(_session);
1446 start_updating_meters ();
1450 Editor::fill_xfade_menu (Menu_Helpers::MenuList& items, bool start)
1452 using namespace Menu_Helpers;
1454 void (Editor::*emf)(FadeShape);
1455 std::map<ARDOUR::FadeShape,Gtk::Image*>* images;
1458 images = &_xfade_in_images;
1459 emf = &Editor::set_fade_in_shape;
1461 images = &_xfade_out_images;
1462 emf = &Editor::set_fade_out_shape;
1467 _("Linear (for highly correlated material)"),
1468 *(*images)[FadeLinear],
1469 sigc::bind (sigc::mem_fun (*this, emf), FadeLinear)
1473 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1477 _("Constant power"),
1478 *(*images)[FadeConstantPower],
1479 sigc::bind (sigc::mem_fun (*this, emf), FadeConstantPower)
1482 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1487 *(*images)[FadeSymmetric],
1488 sigc::bind (sigc::mem_fun (*this, emf), FadeSymmetric)
1492 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1497 *(*images)[FadeSlow],
1498 sigc::bind (sigc::mem_fun (*this, emf), FadeSlow)
1501 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1506 *(*images)[FadeFast],
1507 sigc::bind (sigc::mem_fun (*this, emf), FadeFast)
1510 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1513 /** Pop up a context menu for when the user clicks on a start crossfade */
1515 Editor::popup_xfade_in_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType /*item_type*/)
1517 using namespace Menu_Helpers;
1518 AudioRegionView* arv = dynamic_cast<AudioRegionView*> ((RegionView*)item->get_data ("regionview"));
1523 MenuList& items (xfade_in_context_menu.items());
1526 if (arv->audio_region()->fade_in_active()) {
1527 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1529 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1532 items.push_back (SeparatorElem());
1533 fill_xfade_menu (items, true);
1535 xfade_in_context_menu.popup (button, time);
1538 /** Pop up a context menu for when the user clicks on an end crossfade */
1540 Editor::popup_xfade_out_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType /*item_type*/)
1542 using namespace Menu_Helpers;
1543 AudioRegionView* arv = dynamic_cast<AudioRegionView*> ((RegionView*)item->get_data ("regionview"));
1548 MenuList& items (xfade_out_context_menu.items());
1551 if (arv->audio_region()->fade_out_active()) {
1552 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1554 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1557 items.push_back (SeparatorElem());
1558 fill_xfade_menu (items, false);
1560 xfade_out_context_menu.popup (button, time);
1564 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1566 using namespace Menu_Helpers;
1567 Menu* (Editor::*build_menu_function)();
1570 switch (item_type) {
1572 case RegionViewName:
1573 case RegionViewNameHighlight:
1574 case LeftFrameHandle:
1575 case RightFrameHandle:
1576 if (with_selection) {
1577 build_menu_function = &Editor::build_track_selection_context_menu;
1579 build_menu_function = &Editor::build_track_region_context_menu;
1584 if (with_selection) {
1585 build_menu_function = &Editor::build_track_selection_context_menu;
1587 build_menu_function = &Editor::build_track_context_menu;
1592 if (clicked_routeview->track()) {
1593 build_menu_function = &Editor::build_track_context_menu;
1595 build_menu_function = &Editor::build_track_bus_context_menu;
1600 /* probably shouldn't happen but if it does, we don't care */
1604 menu = (this->*build_menu_function)();
1605 menu->set_name ("ArdourContextMenu");
1607 /* now handle specific situations */
1609 switch (item_type) {
1611 case RegionViewName:
1612 case RegionViewNameHighlight:
1613 case LeftFrameHandle:
1614 case RightFrameHandle:
1615 if (!with_selection) {
1616 if (region_edit_menu_split_item) {
1617 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1618 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1620 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1623 if (region_edit_menu_split_multichannel_item) {
1624 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1625 region_edit_menu_split_multichannel_item->set_sensitive (true);
1627 region_edit_menu_split_multichannel_item->set_sensitive (false);
1640 /* probably shouldn't happen but if it does, we don't care */
1644 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1646 /* Bounce to disk */
1648 using namespace Menu_Helpers;
1649 MenuList& edit_items = menu->items();
1651 edit_items.push_back (SeparatorElem());
1653 switch (clicked_routeview->audio_track()->freeze_state()) {
1654 case AudioTrack::NoFreeze:
1655 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1658 case AudioTrack::Frozen:
1659 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1662 case AudioTrack::UnFrozen:
1663 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1669 if (item_type == StreamItem && clicked_routeview) {
1670 clicked_routeview->build_underlay_menu(menu);
1673 /* When the region menu is opened, we setup the actions so that they look right
1676 sensitize_the_right_region_actions (false);
1677 _last_region_menu_was_main = false;
1679 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1680 menu->popup (button, time);
1684 Editor::build_track_context_menu ()
1686 using namespace Menu_Helpers;
1688 MenuList& edit_items = track_context_menu.items();
1691 add_dstream_context_items (edit_items);
1692 return &track_context_menu;
1696 Editor::build_track_bus_context_menu ()
1698 using namespace Menu_Helpers;
1700 MenuList& edit_items = track_context_menu.items();
1703 add_bus_context_items (edit_items);
1704 return &track_context_menu;
1708 Editor::build_track_region_context_menu ()
1710 using namespace Menu_Helpers;
1711 MenuList& edit_items = track_region_context_menu.items();
1714 /* we've just cleared the track region context menu, so the menu that these
1715 two items were on will have disappeared; stop them dangling.
1717 region_edit_menu_split_item = 0;
1718 region_edit_menu_split_multichannel_item = 0;
1720 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1723 boost::shared_ptr<Track> tr;
1724 boost::shared_ptr<Playlist> pl;
1726 if ((tr = rtv->track())) {
1727 add_region_context_items (edit_items, tr);
1731 add_dstream_context_items (edit_items);
1733 return &track_region_context_menu;
1737 Editor::loudness_analyze_region_selection ()
1742 Selection& s (PublicEditor::instance ().get_selection ());
1743 RegionSelection ars = s.regions;
1744 ARDOUR::AnalysisGraph ag (_session);
1745 samplecnt_t total_work = 0;
1747 for (RegionSelection::iterator j = ars.begin (); j != ars.end (); ++j) {
1748 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*j);
1752 if (!boost::dynamic_pointer_cast<AudioRegion> (arv->region ())) {
1755 assert (dynamic_cast<RouteTimeAxisView *> (&arv->get_time_axis_view ()));
1756 total_work += arv->region ()->length ();
1759 SimpleProgressDialog spd (_("Region Loudness Analysis"), sigc::mem_fun (ag, &AnalysisGraph::cancel));
1761 ag.set_total_samples (total_work);
1762 ag.Progress.connect_same_thread (c, boost::bind (&SimpleProgressDialog::update_progress, &spd, _1, _2));
1765 for (RegionSelection::iterator j = ars.begin (); j != ars.end (); ++j) {
1766 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*j);
1770 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (arv->region ());
1774 ag.analyze_region (ar);
1777 if (!ag.canceled ()) {
1778 ExportReport er (_("Audio Report/Analysis"), ag.results ());
1784 Editor::loudness_analyze_range_selection ()
1789 Selection& s (PublicEditor::instance ().get_selection ());
1790 TimeSelection ts = s.time;
1791 ARDOUR::AnalysisGraph ag (_session);
1792 samplecnt_t total_work = 0;
1794 for (TrackSelection::iterator i = s.tracks.begin (); i != s.tracks.end (); ++i) {
1795 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist> ((*i)->playlist ());
1799 RouteUI *rui = dynamic_cast<RouteUI *> (*i);
1803 for (std::list<AudioRange>::iterator j = ts.begin (); j != ts.end (); ++j) {
1804 total_work += j->length ();
1808 SimpleProgressDialog spd (_("Range Loudness Analysis"), sigc::mem_fun (ag, &AnalysisGraph::cancel));
1810 ag.set_total_samples (total_work);
1811 ag.Progress.connect_same_thread (c, boost::bind (&SimpleProgressDialog::update_progress, &spd, _1, _2));
1814 for (TrackSelection::iterator i = s.tracks.begin (); i != s.tracks.end (); ++i) {
1815 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist> ((*i)->playlist ());
1819 RouteUI *rui = dynamic_cast<RouteUI *> (*i);
1823 ag.analyze_range (rui->route (), pl, ts);
1826 if (!ag.canceled ()) {
1827 ExportReport er (_("Audio Report/Analysis"), ag.results ());
1833 Editor::spectral_analyze_region_selection ()
1835 if (analysis_window == 0) {
1836 analysis_window = new AnalysisWindow();
1839 analysis_window->set_session(_session);
1841 analysis_window->show_all();
1844 analysis_window->set_regionmode();
1845 analysis_window->analyze();
1847 analysis_window->present();
1851 Editor::spectral_analyze_range_selection()
1853 if (analysis_window == 0) {
1854 analysis_window = new AnalysisWindow();
1857 analysis_window->set_session(_session);
1859 analysis_window->show_all();
1862 analysis_window->set_rangemode();
1863 analysis_window->analyze();
1865 analysis_window->present();
1869 Editor::build_track_selection_context_menu ()
1871 using namespace Menu_Helpers;
1872 MenuList& edit_items = track_selection_context_menu.items();
1873 edit_items.clear ();
1875 add_selection_context_items (edit_items);
1876 // edit_items.push_back (SeparatorElem());
1877 // add_dstream_context_items (edit_items);
1879 return &track_selection_context_menu;
1883 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr<Track> track)
1885 using namespace Menu_Helpers;
1887 /* OK, stick the region submenu at the top of the list, and then add
1891 RegionSelection rs = get_regions_from_selection_and_entered ();
1893 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1895 if (_popup_region_menu_item == 0) {
1896 _popup_region_menu_item = new MenuItem (menu_item_name, false);
1897 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1898 _popup_region_menu_item->show ();
1900 _popup_region_menu_item->set_label (menu_item_name);
1903 /* No layering allowed in later is higher layering model */
1904 RefPtr<Action> act = ActionManager::get_action (X_("EditorMenu"), X_("RegionMenuLayering"));
1905 if (act && Config->get_layer_model() == LaterHigher) {
1906 act->set_sensitive (false);
1908 act->set_sensitive (true);
1911 const samplepos_t position = get_preferred_edit_position (EDIT_IGNORE_NONE, true);
1913 edit_items.push_back (*_popup_region_menu_item);
1914 if (Config->get_layer_model() == Manual && track->playlist()->count_regions_at (position) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1915 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region-context-menu")->create_menu_item ()));
1917 edit_items.push_back (SeparatorElem());
1920 /** Add context menu items relevant to selection ranges.
1921 * @param edit_items List to add the items to.
1924 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1926 using namespace Menu_Helpers;
1928 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1929 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1931 edit_items.push_back (SeparatorElem());
1932 edit_items.push_back (MenuElem (_("Zoom to Range"), sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_selection), Horizontal)));
1934 edit_items.push_back (SeparatorElem());
1935 edit_items.push_back (MenuElem (_("Loudness Analysis"), sigc::mem_fun(*this, &Editor::loudness_analyze_range_selection)));
1936 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::spectral_analyze_range_selection)));
1938 edit_items.push_back (SeparatorElem());
1940 edit_items.push_back (
1942 _("Move Range Start to Previous Region Boundary"),
1943 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, false)
1947 edit_items.push_back (
1949 _("Move Range Start to Next Region Boundary"),
1950 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, true)
1954 edit_items.push_back (
1956 _("Move Range End to Previous Region Boundary"),
1957 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, false)
1961 edit_items.push_back (
1963 _("Move Range End to Next Region Boundary"),
1964 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, true)
1968 edit_items.push_back (SeparatorElem());
1969 edit_items.push_back (MenuElem (_("Separate"), mem_fun(*this, &Editor::separate_region_from_selection)));
1970 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1972 edit_items.push_back (SeparatorElem());
1973 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1975 edit_items.push_back (SeparatorElem());
1976 edit_items.push_back (MenuElem (_("Set Loop from Selection"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1977 edit_items.push_back (MenuElem (_("Set Punch from Selection"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1978 edit_items.push_back (MenuElem (_("Set Session Start/End from Selection"), sigc::mem_fun(*this, &Editor::set_session_extents_from_selection)));
1980 edit_items.push_back (SeparatorElem());
1981 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1983 edit_items.push_back (SeparatorElem());
1984 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1985 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_range), false)));
1987 edit_items.push_back (SeparatorElem());
1988 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1989 edit_items.push_back (MenuElem (_("Consolidate Range with Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1990 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1991 edit_items.push_back (MenuElem (_("Bounce Range to Region List with Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1992 edit_items.push_back (MenuElem (_("Export Range..."), sigc::mem_fun(*this, &Editor::export_selection)));
1993 if (ARDOUR_UI::instance()->video_timeline->get_duration() > 0) {
1994 edit_items.push_back (MenuElem (_("Export Video Range..."), sigc::bind (sigc::mem_fun(*(ARDOUR_UI::instance()), &ARDOUR_UI::export_video), true)));
2000 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
2002 using namespace Menu_Helpers;
2006 Menu *play_menu = manage (new Menu);
2007 MenuList& play_items = play_menu->items();
2008 play_menu->set_name ("ArdourContextMenu");
2010 play_items.push_back (MenuElem (_("Play from Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
2011 play_items.push_back (MenuElem (_("Play from Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
2012 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
2013 play_items.push_back (SeparatorElem());
2014 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
2016 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2020 Menu *select_menu = manage (new Menu);
2021 MenuList& select_items = select_menu->items();
2022 select_menu->set_name ("ArdourContextMenu");
2024 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2025 select_items.push_back (MenuElem (_("Select All Objects"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_objects), Selection::Set)));
2026 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2027 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2028 select_items.push_back (SeparatorElem());
2029 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
2030 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
2031 select_items.push_back (MenuElem (_("Set Range to Selected Regions"), sigc::mem_fun(*this, &Editor::set_selection_from_region)));
2032 select_items.push_back (SeparatorElem());
2033 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true, true)));
2034 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false, true)));
2035 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2036 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2037 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
2038 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
2039 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
2041 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2045 Menu *cutnpaste_menu = manage (new Menu);
2046 MenuList& cutnpaste_items = cutnpaste_menu->items();
2047 cutnpaste_menu->set_name ("ArdourContextMenu");
2049 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2050 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2051 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f, true)));
2053 cutnpaste_items.push_back (SeparatorElem());
2055 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
2056 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
2058 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
2060 /* Adding new material */
2062 edit_items.push_back (SeparatorElem());
2063 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
2064 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
2068 Menu *nudge_menu = manage (new Menu());
2069 MenuList& nudge_items = nudge_menu->items();
2070 nudge_menu->set_name ("ArdourContextMenu");
2072 edit_items.push_back (SeparatorElem());
2073 nudge_items.push_back (MenuElem (_("Nudge Entire Track Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2074 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2075 nudge_items.push_back (MenuElem (_("Nudge Entire Track Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2076 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2078 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2082 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
2084 using namespace Menu_Helpers;
2088 Menu *play_menu = manage (new Menu);
2089 MenuList& play_items = play_menu->items();
2090 play_menu->set_name ("ArdourContextMenu");
2092 play_items.push_back (MenuElem (_("Play from Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
2093 play_items.push_back (MenuElem (_("Play from Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
2094 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2098 Menu *select_menu = manage (new Menu);
2099 MenuList& select_items = select_menu->items();
2100 select_menu->set_name ("ArdourContextMenu");
2102 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2103 select_items.push_back (MenuElem (_("Select All Objects"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_objects), Selection::Set)));
2104 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2105 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2106 select_items.push_back (SeparatorElem());
2107 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true, true)));
2108 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false, true)));
2109 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2110 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2112 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2116 Menu *cutnpaste_menu = manage (new Menu);
2117 MenuList& cutnpaste_items = cutnpaste_menu->items();
2118 cutnpaste_menu->set_name ("ArdourContextMenu");
2120 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2121 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2122 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f, true)));
2124 Menu *nudge_menu = manage (new Menu());
2125 MenuList& nudge_items = nudge_menu->items();
2126 nudge_menu->set_name ("ArdourContextMenu");
2128 edit_items.push_back (SeparatorElem());
2129 nudge_items.push_back (MenuElem (_("Nudge Entire Track Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2130 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2131 nudge_items.push_back (MenuElem (_("Nudge Entire Track Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2132 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2134 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2138 Editor::grid_type() const
2144 Editor::grid_musical() const
2146 switch (_grid_type) {
2147 case GridTypeBeatDiv32:
2148 case GridTypeBeatDiv28:
2149 case GridTypeBeatDiv24:
2150 case GridTypeBeatDiv20:
2151 case GridTypeBeatDiv16:
2152 case GridTypeBeatDiv14:
2153 case GridTypeBeatDiv12:
2154 case GridTypeBeatDiv10:
2155 case GridTypeBeatDiv8:
2156 case GridTypeBeatDiv7:
2157 case GridTypeBeatDiv6:
2158 case GridTypeBeatDiv5:
2159 case GridTypeBeatDiv4:
2160 case GridTypeBeatDiv3:
2161 case GridTypeBeatDiv2:
2166 case GridTypeTimecode:
2167 case GridTypeMinSec:
2168 case GridTypeCDFrame:
2175 Editor::grid_nonmusical() const
2177 switch (_grid_type) {
2178 case GridTypeTimecode:
2179 case GridTypeMinSec:
2180 case GridTypeCDFrame:
2182 case GridTypeBeatDiv32:
2183 case GridTypeBeatDiv28:
2184 case GridTypeBeatDiv24:
2185 case GridTypeBeatDiv20:
2186 case GridTypeBeatDiv16:
2187 case GridTypeBeatDiv14:
2188 case GridTypeBeatDiv12:
2189 case GridTypeBeatDiv10:
2190 case GridTypeBeatDiv8:
2191 case GridTypeBeatDiv7:
2192 case GridTypeBeatDiv6:
2193 case GridTypeBeatDiv5:
2194 case GridTypeBeatDiv4:
2195 case GridTypeBeatDiv3:
2196 case GridTypeBeatDiv2:
2205 Editor::snap_mode() const
2211 Editor::show_rulers_for_grid ()
2213 /* show appropriate rulers for this grid setting. */
2214 if (grid_musical()) {
2215 ruler_tempo_action->set_active(true);
2216 ruler_meter_action->set_active(true);
2217 ruler_bbt_action->set_active(true);
2219 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2220 ruler_timecode_action->set_active(false);
2221 ruler_minsec_action->set_active(false);
2222 ruler_samples_action->set_active(false);
2224 } else if (_grid_type == GridTypeTimecode) {
2225 ruler_timecode_action->set_active(true);
2227 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2228 ruler_tempo_action->set_active(false);
2229 ruler_meter_action->set_active(false);
2230 ruler_bbt_action->set_active(false);
2231 ruler_minsec_action->set_active(false);
2232 ruler_samples_action->set_active(false);
2234 } else if (_grid_type == GridTypeMinSec) {
2235 ruler_minsec_action->set_active(true);
2237 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2238 ruler_tempo_action->set_active(false);
2239 ruler_meter_action->set_active(false);
2240 ruler_bbt_action->set_active(false);
2241 ruler_timecode_action->set_active(false);
2242 ruler_samples_action->set_active(false);
2244 } else if (_grid_type == GridTypeCDFrame) {
2245 ruler_cd_marker_action->set_active(true);
2246 ruler_minsec_action->set_active(true);
2248 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2249 ruler_tempo_action->set_active(false);
2250 ruler_meter_action->set_active(false);
2251 ruler_bbt_action->set_active(false);
2252 ruler_timecode_action->set_active(false);
2253 ruler_samples_action->set_active(false);
2259 Editor::set_grid_to (GridType gt)
2261 if (_grid_type == gt) { // already set
2265 unsigned int grid_ind = (unsigned int)gt;
2267 if (internal_editing() && UIConfiguration::instance().get_grid_follows_internal()) {
2268 internal_grid_type = gt;
2270 pre_internal_grid_type = gt;
2275 if (grid_ind > grid_type_strings.size() - 1) {
2277 _grid_type = (GridType)grid_ind;
2280 string str = grid_type_strings[grid_ind];
2282 if (str != grid_type_selector.get_text()) {
2283 grid_type_selector.set_text (str);
2286 if (UIConfiguration::instance().get_show_grids_ruler()) {
2287 show_rulers_for_grid ();
2292 if (grid_musical()) {
2293 compute_bbt_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
2294 update_tempo_based_rulers ();
2297 mark_region_boundary_cache_dirty ();
2299 redisplay_grid (false);
2301 SnapChanged (); /* EMIT SIGNAL */
2305 Editor::set_snap_mode (SnapMode mode)
2307 if (internal_editing()) {
2308 internal_snap_mode = mode;
2310 pre_internal_snap_mode = mode;
2315 if (_snap_mode == SnapOff) {
2316 snap_mode_button.set_active_state (Gtkmm2ext::Off);
2318 snap_mode_button.set_active_state (Gtkmm2ext::ExplicitActive);
2325 Editor::set_edit_point_preference (EditPoint ep, bool force)
2327 bool changed = (_edit_point != ep);
2330 if (Profile->get_mixbus())
2331 if (ep == EditAtSelectedMarker)
2332 ep = EditAtPlayhead;
2334 string str = edit_point_strings[(int)ep];
2335 if (str != edit_point_selector.get_text ()) {
2336 edit_point_selector.set_text (str);
2339 update_all_enter_cursors();
2341 if (!force && !changed) {
2345 const char* action=NULL;
2347 switch (_edit_point) {
2348 case EditAtPlayhead:
2349 action = "edit-at-playhead";
2351 case EditAtSelectedMarker:
2352 action = "edit-at-marker";
2355 action = "edit-at-mouse";
2359 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2361 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2365 bool in_track_canvas;
2367 if (!mouse_sample (foo, in_track_canvas)) {
2368 in_track_canvas = false;
2371 reset_canvas_action_sensitivity (in_track_canvas);
2372 sensitize_the_right_region_actions (false);
2378 Editor::set_state (const XMLNode& node, int version)
2381 PBD::Unwinder<bool> nsi (no_save_instant, true);
2384 Tabbable::set_state (node, version);
2387 if (_session && node.get_property ("playhead", ph_pos)) {
2389 playhead_cursor->set_position (ph_pos);
2391 warning << _("Playhead position stored with a negative value - ignored (use zero instead)") << endmsg;
2392 playhead_cursor->set_position (0);
2395 playhead_cursor->set_position (0);
2398 node.get_property ("mixer-width", editor_mixer_strip_width);
2400 node.get_property ("zoom-focus", zoom_focus);
2401 zoom_focus_selection_done (zoom_focus);
2404 if (node.get_property ("zoom", z)) {
2405 /* older versions of ardour used floating point samples_per_pixel */
2406 reset_zoom (llrintf (z));
2408 reset_zoom (samples_per_pixel);
2412 if (node.get_property ("visible-track-count", cnt)) {
2413 set_visible_track_count (cnt);
2417 if (!node.get_property ("grid-type", grid_type)) {
2418 grid_type = _grid_type;
2420 set_grid_to (grid_type);
2423 if (node.get_property ("snap-mode", sm)) {
2424 snap_mode_selection_done(sm);
2425 /* set text of Dropdown. in case _snap_mode == SnapOff (default)
2426 * snap_mode_selection_done() will only mark an already active item as active
2427 * which does not trigger set_text().
2431 set_snap_mode (_snap_mode);
2434 node.get_property ("internal-grid-type", internal_grid_type);
2435 node.get_property ("internal-snap-mode", internal_snap_mode);
2436 node.get_property ("pre-internal-grid-type", pre_internal_grid_type);
2437 node.get_property ("pre-internal-snap-mode", pre_internal_snap_mode);
2440 if (node.get_property ("mouse-mode", mm_str)) {
2441 MouseMode m = str2mousemode(mm_str);
2442 set_mouse_mode (m, true);
2444 set_mouse_mode (MouseObject, true);
2448 if (node.get_property ("left-frame", lf_pos)) {
2452 reset_x_origin (lf_pos);
2456 if (node.get_property ("y-origin", y_origin)) {
2457 reset_y_origin (y_origin);
2460 if (node.get_property ("join-object-range", yn)) {
2461 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object-range"));
2463 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2464 tact->set_active (!yn);
2465 tact->set_active (yn);
2467 set_mouse_mode(mouse_mode, true);
2471 if (node.get_property ("edit-point", ep)) {
2472 set_edit_point_preference (ep, true);
2474 set_edit_point_preference (_edit_point);
2477 if (node.get_property ("follow-playhead", yn)) {
2478 set_follow_playhead (yn);
2481 if (node.get_property ("stationary-playhead", yn)) {
2482 set_stationary_playhead (yn);
2485 RegionListSortType sort_type;
2486 if (node.get_property ("region-list-sort-type", sort_type)) {
2487 _regions->reset_sort_type (sort_type, true);
2490 if (node.get_property ("show-editor-mixer", yn)) {
2492 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2495 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2497 /* do it twice to force the change */
2499 tact->set_active (!yn);
2500 tact->set_active (yn);
2503 if (node.get_property ("show-editor-list", yn)) {
2505 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2508 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2510 /* do it twice to force the change */
2512 tact->set_active (!yn);
2513 tact->set_active (yn);
2517 if (node.get_property (X_("editor-list-page"), el_page)) {
2518 _the_notebook.set_current_page (el_page);
2521 if (node.get_property (X_("show-marker-lines"), yn)) {
2522 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2524 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2526 tact->set_active (!yn);
2527 tact->set_active (yn);
2530 XMLNodeList children = node.children ();
2531 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2532 selection->set_state (**i, Stateful::current_state_version);
2533 _regions->set_state (**i);
2534 _locations->set_state (**i);
2537 if (node.get_property ("maximised", yn)) {
2538 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMaximalEditor"));
2540 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2541 bool fs = tact && tact->get_active();
2543 ActionManager::do_action ("Common", "ToggleMaximalEditor");
2547 samplepos_t nudge_clock_value;
2548 if (node.get_property ("nudge-clock-value", nudge_clock_value)) {
2549 nudge_clock->set (nudge_clock_value);
2551 nudge_clock->set_mode (AudioClock::Timecode);
2552 nudge_clock->set (_session->sample_rate() * 5, true);
2557 * Not all properties may have been in XML, but
2558 * those that are linked to a private variable may need changing
2562 act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2563 yn = _follow_playhead;
2565 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2566 if (tact->get_active() != yn) {
2567 tact->set_active (yn);
2571 act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2572 yn = _stationary_playhead;
2574 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2575 if (tact->get_active() != yn) {
2576 tact->set_active (yn);
2585 Editor::get_state ()
2587 XMLNode* node = new XMLNode (X_("Editor"));
2589 node->set_property ("id", id().to_s ());
2591 node->add_child_nocopy (Tabbable::get_state());
2593 node->set_property("edit-horizontal-pane-pos", edit_pane.get_divider ());
2594 node->set_property("notebook-shrunk", _notebook_shrunk);
2595 node->set_property("edit-vertical-pane-pos", editor_summary_pane.get_divider());
2597 maybe_add_mixer_strip_width (*node);
2599 node->set_property ("zoom-focus", zoom_focus);
2601 node->set_property ("zoom", samples_per_pixel);
2602 node->set_property ("grid-type", _grid_type);
2603 node->set_property ("snap-mode", _snap_mode);
2604 node->set_property ("internal-grid-type", internal_grid_type);
2605 node->set_property ("internal-snap-mode", internal_snap_mode);
2606 node->set_property ("pre-internal-grid-type", pre_internal_grid_type);
2607 node->set_property ("pre-internal-snap-mode", pre_internal_snap_mode);
2608 node->set_property ("edit-point", _edit_point);
2609 node->set_property ("visible-track-count", _visible_track_count);
2611 node->set_property ("playhead", playhead_cursor->current_sample ());
2612 node->set_property ("left-frame", _leftmost_sample);
2613 node->set_property ("y-origin", vertical_adjustment.get_value ());
2615 node->set_property ("maximised", _maximised);
2616 node->set_property ("follow-playhead", _follow_playhead);
2617 node->set_property ("stationary-playhead", _stationary_playhead);
2618 node->set_property ("region-list-sort-type", _regions->sort_type ());
2619 node->set_property ("mouse-mode", mouse_mode);
2620 node->set_property ("join-object-range", smart_mode_action->get_active ());
2622 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2624 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2625 node->set_property (X_("show-editor-mixer"), tact->get_active());
2628 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2630 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2631 node->set_property (X_("show-editor-list"), tact->get_active());
2634 node->set_property (X_("editor-list-page"), _the_notebook.get_current_page ());
2636 if (button_bindings) {
2637 XMLNode* bb = new XMLNode (X_("Buttons"));
2638 button_bindings->save (*bb);
2639 node->add_child_nocopy (*bb);
2642 node->set_property (X_("show-marker-lines"), _show_marker_lines);
2644 node->add_child_nocopy (selection->get_state ());
2645 node->add_child_nocopy (_regions->get_state ());
2647 node->set_property ("nudge-clock-value", nudge_clock->current_duration());
2649 node->add_child_nocopy (_locations->get_state ());
2654 /** if @param trackview_relative_offset is true, @param y y is an offset into the trackview area, in pixel units
2655 * if @param trackview_relative_offset is false, @param y y is a global canvas * coordinate, in pixel units
2657 * @return pair: TimeAxisView that y is over, layer index.
2659 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2660 * in stacked or expanded region display mode, otherwise 0.
2662 std::pair<TimeAxisView *, double>
2663 Editor::trackview_by_y_position (double y, bool trackview_relative_offset) const
2665 if (!trackview_relative_offset) {
2666 y -= _trackview_group->canvas_origin().y;
2670 return std::make_pair ((TimeAxisView *) 0, 0);
2673 for (TrackViewList::const_iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2675 std::pair<TimeAxisView*, double> const r = (*iter)->covers_y_position (y);
2682 return std::make_pair ((TimeAxisView *) 0, 0);
2686 Editor::set_snapped_cursor_position (samplepos_t pos)
2688 if (_edit_point == EditAtMouse) {
2689 snapped_cursor->set_position(pos);
2694 /** Snap a position to the grid, if appropriate, taking into account current
2695 * grid settings and also the state of any snap modifier keys that may be pressed.
2696 * @param start Position to snap.
2697 * @param event Event to get current key modifier information from, or 0.
2700 Editor::snap_to_with_modifier (MusicSample& start, GdkEvent const * event, RoundMode direction, SnapPref pref)
2702 if (!_session || !event) {
2706 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2707 if (_snap_mode == SnapOff) {
2708 snap_to_internal (start, direction, pref);
2710 start.set (start.sample, 0);
2713 if (_snap_mode != SnapOff) {
2714 snap_to_internal (start, direction, pref);
2715 } else if (ArdourKeyboard::indicates_snap_delta (event->button.state)) {
2716 /* SnapOff, but we pressed the snap_delta modifier */
2717 snap_to_internal (start, direction, pref);
2719 start.set (start.sample, 0);
2725 Editor::snap_to (MusicSample& start, RoundMode direction, SnapPref pref, bool ensure_snap)
2727 if (!_session || (_snap_mode == SnapOff && !ensure_snap)) {
2728 start.set (start.sample, 0);
2732 snap_to_internal (start, direction, pref, ensure_snap);
2736 check_best_snap (samplepos_t presnap, samplepos_t &test, samplepos_t &dist, samplepos_t &best)
2738 samplepos_t diff = abs (test - presnap);
2744 test = max_samplepos; // reset this so it doesn't get accidentally reused
2748 Editor::snap_to_timecode (MusicSample presnap, RoundMode direction, SnapPref gpref)
2750 samplepos_t start = presnap.sample;
2751 const samplepos_t one_timecode_second = (samplepos_t)(rint(_session->timecode_frames_per_second()) * _session->samples_per_timecode_frame());
2752 samplepos_t one_timecode_minute = (samplepos_t)(rint(_session->timecode_frames_per_second()) * _session->samples_per_timecode_frame() * 60);
2754 TimecodeRulerScale scale = (gpref != SnapToGrid_Unscaled) ? timecode_ruler_scale : timecode_show_samples;
2757 case timecode_show_bits:
2758 case timecode_show_samples:
2759 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2760 fmod((double)start, (double)_session->samples_per_timecode_frame()) == 0) {
2761 /* start is already on a whole timecode frame, do nothing */
2762 } else if (((direction == 0) && (fmod((double)start, (double)_session->samples_per_timecode_frame()) > (_session->samples_per_timecode_frame() / 2))) || (direction > 0)) {
2763 start = (samplepos_t) (ceil ((double) start / _session->samples_per_timecode_frame()) * _session->samples_per_timecode_frame());
2765 start = (samplepos_t) (floor ((double) start / _session->samples_per_timecode_frame()) * _session->samples_per_timecode_frame());
2769 case timecode_show_seconds:
2770 if (_session->config.get_timecode_offset_negative()) {
2771 start += _session->config.get_timecode_offset ();
2773 start -= _session->config.get_timecode_offset ();
2775 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2776 (start % one_timecode_second == 0)) {
2777 /* start is already on a whole second, do nothing */
2778 } else if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2779 start = (samplepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2781 start = (samplepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2784 if (_session->config.get_timecode_offset_negative()) {
2785 start -= _session->config.get_timecode_offset ();
2787 start += _session->config.get_timecode_offset ();
2791 case timecode_show_minutes:
2792 case timecode_show_hours:
2793 case timecode_show_many_hours:
2794 if (_session->config.get_timecode_offset_negative()) {
2795 start += _session->config.get_timecode_offset ();
2797 start -= _session->config.get_timecode_offset ();
2799 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2800 (start % one_timecode_minute == 0)) {
2801 /* start is already on a whole minute, do nothing */
2802 } else if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2803 start = (samplepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2805 start = (samplepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2807 if (_session->config.get_timecode_offset_negative()) {
2808 start -= _session->config.get_timecode_offset ();
2810 start += _session->config.get_timecode_offset ();
2814 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2817 MusicSample ret(start,0);
2822 Editor::snap_to_minsec (MusicSample presnap, RoundMode direction, SnapPref gpref)
2824 MusicSample ret(presnap);
2826 const samplepos_t one_second = _session->sample_rate();
2827 const samplepos_t one_minute = one_second * 60;
2828 const samplepos_t one_hour = one_minute * 60;
2830 MinsecRulerScale scale = (gpref != SnapToGrid_Unscaled) ? minsec_ruler_scale : minsec_show_seconds;
2833 case minsec_show_msecs:
2834 case minsec_show_seconds: {
2835 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2836 presnap.sample % one_second == 0) {
2837 /* start is already on a whole second, do nothing */
2838 } else if (((direction == 0) && (presnap.sample % one_second > one_second / 2)) || (direction > 0)) {
2839 ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_second) * one_second;
2841 ret.sample = (samplepos_t) floor ((double) presnap.sample / one_second) * one_second;
2845 case minsec_show_minutes: {
2846 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2847 presnap.sample % one_minute == 0) {
2848 /* start is already on a whole minute, do nothing */
2849 } else if (((direction == 0) && (presnap.sample % one_minute > one_minute / 2)) || (direction > 0)) {
2850 ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_minute) * one_minute;
2852 ret.sample = (samplepos_t) floor ((double) presnap.sample / one_minute) * one_minute;
2857 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2858 presnap.sample % one_hour == 0) {
2859 /* start is already on a whole hour, do nothing */
2860 } else if (((direction == 0) && (presnap.sample % one_hour > one_hour / 2)) || (direction > 0)) {
2861 ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_hour) * one_hour;
2863 ret.sample = (samplepos_t) floor ((double) presnap.sample / one_hour) * one_hour;
2872 Editor::snap_to_cd_frames (MusicSample presnap, RoundMode direction, SnapPref gpref)
2874 if ((gpref != SnapToGrid_Unscaled) && (minsec_ruler_scale != minsec_show_msecs)) {
2875 return snap_to_minsec (presnap, direction, gpref);
2878 const samplepos_t one_second = _session->sample_rate();
2880 MusicSample ret(presnap);
2882 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2883 presnap.sample % (one_second/75) == 0) {
2884 /* start is already on a whole CD sample, do nothing */
2885 } else if (((direction == 0) && (presnap.sample % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2886 ret.sample = (samplepos_t) ceil ((double) presnap.sample / (one_second / 75)) * (one_second / 75);
2888 ret.sample = (samplepos_t) floor ((double) presnap.sample / (one_second / 75)) * (one_second / 75);
2895 Editor::snap_to_bbt (MusicSample presnap, RoundMode direction, SnapPref gpref)
2897 MusicSample ret(presnap);
2899 if (gpref != SnapToGrid_Unscaled) { // use the visual grid lines which are limited by the zoom scale that the user selected
2902 switch (_grid_type) {
2903 case GridTypeBeatDiv3:
2904 case GridTypeBeatDiv6:
2905 case GridTypeBeatDiv12:
2906 case GridTypeBeatDiv24:
2909 case GridTypeBeatDiv5:
2910 case GridTypeBeatDiv10:
2911 case GridTypeBeatDiv20:
2914 case GridTypeBeatDiv7:
2915 case GridTypeBeatDiv14:
2916 case GridTypeBeatDiv28:
2923 BBTRulerScale scale = bbt_ruler_scale;
2930 ret = _session->tempo_map().round_to_bar (presnap.sample, direction);
2932 case bbt_show_quarters:
2933 ret = _session->tempo_map().round_to_beat (presnap.sample, direction);
2935 case bbt_show_eighths:
2936 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 1 * divisor, direction);
2938 case bbt_show_sixteenths:
2939 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 2 * divisor, direction);
2941 case bbt_show_thirtyseconds:
2942 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 4 * divisor, direction);
2946 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, get_grid_beat_divisions(_grid_type), direction);
2953 Editor::snap_to_grid (MusicSample presnap, RoundMode direction, SnapPref gpref)
2955 MusicSample ret(presnap);
2957 if (grid_musical()) {
2958 ret = snap_to_bbt (presnap, direction, gpref);
2961 switch (_grid_type) {
2962 case GridTypeTimecode:
2963 ret = snap_to_timecode(presnap, direction, gpref);
2965 case GridTypeMinSec:
2966 ret = snap_to_minsec(presnap, direction, gpref);
2968 case GridTypeCDFrame:
2969 ret = snap_to_cd_frames(presnap, direction, gpref);
2979 Editor::snap_to_marker (samplepos_t presnap, RoundMode direction)
2985 _session->locations()->marks_either_side (presnap, before, after);
2987 if (before == max_samplepos && after == max_samplepos) {
2988 /* No marks to snap to, so just don't snap */
2990 } else if (before == max_samplepos) {
2992 } else if (after == max_samplepos) {
2995 if ((direction == RoundUpMaybe || direction == RoundUpAlways)) {
2997 } else if ((direction == RoundDownMaybe || direction == RoundDownAlways)) {
2999 } else if (direction == 0) {
3000 if ((presnap - before) < (after - presnap)) {
3012 Editor::snap_to_internal (MusicSample& start, RoundMode direction, SnapPref pref, bool ensure_snap)
3014 const samplepos_t presnap = start.sample;
3016 samplepos_t test = max_samplepos; // for each snap, we'll use this value
3017 samplepos_t dist = max_samplepos; // this records the distance of the best snap result we've found so far
3018 samplepos_t best = max_samplepos; // this records the best snap-result we've found so far
3020 /* check snap-to-marker */
3021 if ((pref == SnapToAny_Visual) && UIConfiguration::instance().get_snap_to_marks()) {
3022 test = snap_to_marker (presnap, direction);
3023 check_best_snap(presnap, test, dist, best);
3026 /* check snap-to-region-{start/end/sync} */
3028 (pref == SnapToAny_Visual) &&
3029 (UIConfiguration::instance().get_snap_to_region_start() || UIConfiguration::instance().get_snap_to_region_end() || UIConfiguration::instance().get_snap_to_region_sync())
3031 if (!region_boundary_cache.empty()) {
3033 vector<samplepos_t>::iterator prev = region_boundary_cache.begin();
3034 vector<samplepos_t>::iterator next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), presnap);
3035 if (next != region_boundary_cache.begin ()) {
3040 if ((direction == RoundUpMaybe || direction == RoundUpAlways))
3042 else if ((direction == RoundDownMaybe || direction == RoundDownAlways))
3044 else if (direction == 0) {
3045 if ((presnap - *prev) < (*next - presnap)) {
3054 check_best_snap(presnap, test, dist, best);
3058 if (UIConfiguration::instance().get_snap_to_grid() && (_grid_type != GridTypeNone)) {
3059 MusicSample pre(presnap, 0);
3060 MusicSample post = snap_to_grid (pre, direction, pref);
3061 check_best_snap(presnap, post.sample, dist, best);
3064 /* now check "magnetic" state: is the grid within reasonable on-screen distance to trigger a snap?
3065 * this also helps to avoid snapping to somewhere the user can't see. (i.e.: I clicked on a region and it disappeared!!)
3066 * ToDo: Perhaps this should only occur if EditPointMouse?
3068 int snap_threshold_s = pixel_to_sample(UIConfiguration::instance().get_snap_threshold());
3070 start.set (best, 0);
3072 } else if (presnap > best) {
3073 if (presnap > (best+ snap_threshold_s)) {
3076 } else if (presnap < best) {
3077 if (presnap < (best - snap_threshold_s)) {
3082 start.set (best, 0);
3087 Editor::setup_toolbar ()
3089 HBox* mode_box = manage(new HBox);
3090 mode_box->set_border_width (2);
3091 mode_box->set_spacing(2);
3093 HBox* mouse_mode_box = manage (new HBox);
3094 HBox* mouse_mode_hbox = manage (new HBox);
3095 VBox* mouse_mode_vbox = manage (new VBox);
3096 Alignment* mouse_mode_align = manage (new Alignment);
3098 Glib::RefPtr<SizeGroup> mouse_mode_size_group = SizeGroup::create (SIZE_GROUP_VERTICAL);
3099 mouse_mode_size_group->add_widget (smart_mode_button);
3100 mouse_mode_size_group->add_widget (mouse_move_button);
3101 mouse_mode_size_group->add_widget (mouse_cut_button);
3102 mouse_mode_size_group->add_widget (mouse_select_button);
3103 mouse_mode_size_group->add_widget (mouse_timefx_button);
3104 mouse_mode_size_group->add_widget (mouse_audition_button);
3105 mouse_mode_size_group->add_widget (mouse_draw_button);
3106 mouse_mode_size_group->add_widget (mouse_content_button);
3108 if (!Profile->get_mixbus()) {
3109 mouse_mode_size_group->add_widget (zoom_in_button);
3110 mouse_mode_size_group->add_widget (zoom_out_button);
3111 mouse_mode_size_group->add_widget (zoom_out_full_button);
3112 mouse_mode_size_group->add_widget (zoom_focus_selector);
3113 mouse_mode_size_group->add_widget (tav_shrink_button);
3114 mouse_mode_size_group->add_widget (tav_expand_button);
3116 mouse_mode_size_group->add_widget (zoom_preset_selector);
3117 mouse_mode_size_group->add_widget (visible_tracks_selector);
3120 mouse_mode_size_group->add_widget (grid_type_selector);
3121 mouse_mode_size_group->add_widget (snap_mode_button);
3123 mouse_mode_size_group->add_widget (edit_point_selector);
3124 mouse_mode_size_group->add_widget (edit_mode_selector);
3126 mouse_mode_size_group->add_widget (*nudge_clock);
3127 mouse_mode_size_group->add_widget (nudge_forward_button);
3128 mouse_mode_size_group->add_widget (nudge_backward_button);
3130 mouse_mode_hbox->set_spacing (2);
3132 if (!ARDOUR::Profile->get_trx()) {
3133 mouse_mode_hbox->pack_start (smart_mode_button, false, false);
3136 mouse_mode_hbox->pack_start (mouse_move_button, false, false);
3137 mouse_mode_hbox->pack_start (mouse_select_button, false, false);
3139 if (!ARDOUR::Profile->get_mixbus()) {
3140 mouse_mode_hbox->pack_start (mouse_cut_button, false, false);
3141 mouse_mode_hbox->pack_start (mouse_audition_button, false, false);
3144 if (!ARDOUR::Profile->get_trx()) {
3145 mouse_mode_hbox->pack_start (mouse_timefx_button, false, false);
3146 mouse_mode_hbox->pack_start (mouse_draw_button, false, false);
3147 mouse_mode_hbox->pack_start (mouse_content_button, false, false);
3150 mouse_mode_vbox->pack_start (*mouse_mode_hbox);
3152 mouse_mode_align->add (*mouse_mode_vbox);
3153 mouse_mode_align->set (0.5, 1.0, 0.0, 0.0);
3155 mouse_mode_box->pack_start (*mouse_mode_align, false, false);
3157 edit_mode_selector.set_name ("mouse mode button");
3159 if (!ARDOUR::Profile->get_trx()) {
3160 mode_box->pack_start (edit_mode_selector, false, false);
3161 mode_box->pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3162 mode_box->pack_start (edit_point_selector, false, false);
3163 mode_box->pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3166 mode_box->pack_start (*mouse_mode_box, false, false);
3170 _zoom_box.set_spacing (2);
3171 _zoom_box.set_border_width (2);
3175 zoom_preset_selector.set_name ("zoom button");
3176 zoom_preset_selector.set_icon (ArdourIcon::ZoomExpand);
3178 zoom_in_button.set_name ("zoom button");
3179 zoom_in_button.set_icon (ArdourIcon::ZoomIn);
3180 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-in"));
3181 zoom_in_button.set_related_action (act);
3183 zoom_out_button.set_name ("zoom button");
3184 zoom_out_button.set_icon (ArdourIcon::ZoomOut);
3185 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-out"));
3186 zoom_out_button.set_related_action (act);
3188 zoom_out_full_button.set_name ("zoom button");
3189 zoom_out_full_button.set_icon (ArdourIcon::ZoomFull);
3190 act = ActionManager::get_action (X_("Editor"), X_("zoom-to-session"));
3191 zoom_out_full_button.set_related_action (act);
3193 zoom_focus_selector.set_name ("zoom button");
3195 if (ARDOUR::Profile->get_mixbus()) {
3196 _zoom_box.pack_start (zoom_preset_selector, false, false);
3197 } else if (ARDOUR::Profile->get_trx()) {
3198 mode_box->pack_start (zoom_out_button, false, false);
3199 mode_box->pack_start (zoom_in_button, false, false);
3201 _zoom_box.pack_start (zoom_out_button, false, false);
3202 _zoom_box.pack_start (zoom_in_button, false, false);
3203 _zoom_box.pack_start (zoom_out_full_button, false, false);
3204 _zoom_box.pack_start (zoom_focus_selector, false, false);
3207 /* Track zoom buttons */
3208 _track_box.set_spacing (2);
3209 _track_box.set_border_width (2);
3211 visible_tracks_selector.set_name ("zoom button");
3212 if (Profile->get_mixbus()) {
3213 visible_tracks_selector.set_icon (ArdourIcon::TimeAxisExpand);
3215 set_size_request_to_display_given_text (visible_tracks_selector, _("All"), 30, 2);
3218 tav_expand_button.set_name ("zoom button");
3219 tav_expand_button.set_icon (ArdourIcon::TimeAxisExpand);
3220 act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
3221 tav_expand_button.set_related_action (act);
3223 tav_shrink_button.set_name ("zoom button");
3224 tav_shrink_button.set_icon (ArdourIcon::TimeAxisShrink);
3225 act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
3226 tav_shrink_button.set_related_action (act);
3228 if (ARDOUR::Profile->get_mixbus()) {
3229 _track_box.pack_start (visible_tracks_selector);
3230 } else if (ARDOUR::Profile->get_trx()) {
3231 _track_box.pack_start (tav_shrink_button);
3232 _track_box.pack_start (tav_expand_button);
3234 _track_box.pack_start (visible_tracks_selector);
3235 _track_box.pack_start (tav_shrink_button);
3236 _track_box.pack_start (tav_expand_button);
3239 snap_box.set_spacing (2);
3240 snap_box.set_border_width (2);
3242 grid_type_selector.set_name ("mouse mode button");
3244 snap_mode_button.set_name ("mouse mode button");
3246 edit_point_selector.set_name ("mouse mode button");
3248 snap_box.pack_start (snap_mode_button, false, false);
3249 snap_box.pack_start (grid_type_selector, false, false);
3253 HBox *nudge_box = manage (new HBox);
3254 nudge_box->set_spacing (2);
3255 nudge_box->set_border_width (2);
3257 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
3258 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
3260 nudge_box->pack_start (nudge_backward_button, false, false);
3261 nudge_box->pack_start (nudge_forward_button, false, false);
3262 nudge_box->pack_start (*nudge_clock, false, false);
3265 /* Pack everything in... */
3267 toolbar_hbox.set_spacing (2);
3268 toolbar_hbox.set_border_width (2);
3270 ArdourWidgets::ArdourDropShadow *tool_shadow = manage (new (ArdourWidgets::ArdourDropShadow));
3271 tool_shadow->set_size_request (4, -1);
3272 tool_shadow->show();
3274 ebox_hpacker.pack_start (*tool_shadow, false, false);
3275 ebox_hpacker.pack_start(ebox_vpacker, true, true);
3277 Gtk::EventBox* spacer = manage (new Gtk::EventBox); // extra space under the mouse toolbar, for aesthetics
3278 spacer->set_name("EditorWindow");
3279 spacer->set_size_request(-1,4);
3282 ebox_vpacker.pack_start(toolbar_hbox, false, false);
3283 ebox_vpacker.pack_start(*spacer, false, false);
3284 ebox_vpacker.show();
3286 toolbar_hbox.pack_start (*mode_box, false, false);
3288 if (!ARDOUR::Profile->get_trx()) {
3290 toolbar_hbox.pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3292 toolbar_hbox.pack_start (snap_box, false, false);
3294 toolbar_hbox.pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3296 toolbar_hbox.pack_start (*nudge_box, false, false);
3298 toolbar_hbox.pack_end (_zoom_box, false, false, 2);
3300 toolbar_hbox.pack_end (*(manage (new ArdourVSpacer ())), false, false, 3);
3302 toolbar_hbox.pack_end (_track_box, false, false);
3306 toolbar_hbox.show_all ();
3310 Editor::build_edit_point_menu ()
3312 using namespace Menu_Helpers;
3314 edit_point_selector.AddMenuElem (MenuElem (edit_point_strings[(int)EditAtPlayhead], sigc::bind (sigc::mem_fun(*this, &Editor::edit_point_selection_done), (EditPoint) EditAtPlayhead)));
3315 if(!Profile->get_mixbus())
3316 edit_point_selector.AddMenuElem (MenuElem (edit_point_strings[(int)EditAtSelectedMarker], sigc::bind (sigc::mem_fun(*this, &Editor::edit_point_selection_done), (EditPoint) EditAtSelectedMarker)));
3317 edit_point_selector.AddMenuElem (MenuElem (edit_point_strings[(int)EditAtMouse], sigc::bind (sigc::mem_fun(*this, &Editor::edit_point_selection_done), (EditPoint) EditAtMouse)));
3319 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_TRIANGLE_WIDTH, 2);
3323 Editor::build_edit_mode_menu ()
3325 using namespace Menu_Helpers;
3327 edit_mode_selector.AddMenuElem (MenuElem (edit_mode_strings[(int)Slide], sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Slide)));
3328 edit_mode_selector.AddMenuElem (MenuElem (edit_mode_strings[(int)Ripple], sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Ripple)));
3329 edit_mode_selector.AddMenuElem (MenuElem (edit_mode_strings[(int)Lock], sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Lock)));
3330 /* Note: Splice was removed */
3332 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_TRIANGLE_WIDTH, 2);
3336 Editor::build_grid_type_menu ()
3338 using namespace Menu_Helpers;
3340 /* main grid: bars, quarter-notes, etc */
3341 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeNone], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeNone)));
3342 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBar], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBar)));
3343 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeat], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeat)));
3344 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv2], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv2)));
3345 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv4], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv4)));
3346 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv8], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv8)));
3347 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv16], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv16)));
3348 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv32], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv32)));
3351 grid_type_selector.AddMenuElem(SeparatorElem());
3352 Gtk::Menu *_triplet_menu = manage (new Menu);
3353 MenuList& triplet_items (_triplet_menu->items());
3355 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv3], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv3)));
3356 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv6], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv6)));
3357 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv12], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv12)));
3358 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv24], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv24)));
3360 grid_type_selector.AddMenuElem (Menu_Helpers::MenuElem (_("Triplets"), *_triplet_menu));
3362 /* quintuplet grid */
3363 Gtk::Menu *_quintuplet_menu = manage (new Menu);
3364 MenuList& quintuplet_items (_quintuplet_menu->items());
3366 quintuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv5], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv5)));
3367 quintuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv10], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv10)));
3368 quintuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv20], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv20)));
3370 grid_type_selector.AddMenuElem (Menu_Helpers::MenuElem (_("Quintuplets"), *_quintuplet_menu));
3372 /* septuplet grid */
3373 Gtk::Menu *_septuplet_menu = manage (new Menu);
3374 MenuList& septuplet_items (_septuplet_menu->items());
3376 septuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv7], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv7)));
3377 septuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv14], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv14)));
3378 septuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv28], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv28)));
3380 grid_type_selector.AddMenuElem (Menu_Helpers::MenuElem (_("Septuplets"), *_septuplet_menu));
3382 grid_type_selector.AddMenuElem(SeparatorElem());
3383 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeTimecode], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeTimecode)));
3384 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeMinSec], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeMinSec)));
3385 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeCDFrame], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeCDFrame)));
3389 Editor::setup_tooltips ()
3391 set_tooltip (smart_mode_button, _("Smart Mode (add range functions to Grab Mode)"));
3392 set_tooltip (mouse_move_button, _("Grab Mode (select/move objects)"));
3393 set_tooltip (mouse_cut_button, _("Cut Mode (split regions)"));
3394 set_tooltip (mouse_select_button, _("Range Mode (select time ranges)"));
3395 set_tooltip (mouse_draw_button, _("Draw Mode (draw and edit gain/notes/automation)"));
3396 set_tooltip (mouse_timefx_button, _("Stretch Mode (time-stretch audio and midi regions, preserving pitch)"));
3397 set_tooltip (mouse_audition_button, _("Audition Mode (listen to regions)"));
3398 set_tooltip (mouse_content_button, _("Internal Edit Mode (edit notes and automation points)"));
3399 set_tooltip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
3400 set_tooltip (nudge_forward_button, _("Nudge Region/Selection Later"));
3401 set_tooltip (nudge_backward_button, _("Nudge Region/Selection Earlier"));
3402 set_tooltip (zoom_in_button, _("Zoom In"));
3403 set_tooltip (zoom_out_button, _("Zoom Out"));
3404 set_tooltip (zoom_preset_selector, _("Zoom to Time Scale"));
3405 set_tooltip (zoom_out_full_button, _("Zoom to Session"));
3406 set_tooltip (zoom_focus_selector, _("Zoom Focus"));
3407 set_tooltip (tav_expand_button, _("Expand Tracks"));
3408 set_tooltip (tav_shrink_button, _("Shrink Tracks"));
3409 set_tooltip (visible_tracks_selector, _("Number of visible tracks"));
3410 set_tooltip (grid_type_selector, _("Grid Mode"));
3411 set_tooltip (snap_mode_button, _("Snap Mode\n\nRight-click to visit Snap preferences."));
3412 set_tooltip (edit_point_selector, _("Edit Point"));
3413 set_tooltip (edit_mode_selector, _("Edit Mode"));
3414 set_tooltip (nudge_clock, _("Nudge Clock\n(controls distance used to nudge regions and selections)"));
3418 Editor::convert_drop_to_paths (
3419 vector<string>& paths,
3420 const RefPtr<Gdk::DragContext>& /*context*/,
3423 const SelectionData& data,
3427 if (_session == 0) {
3431 vector<string> uris = data.get_uris();
3435 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3436 are actually URI lists. So do it by hand.
3439 if (data.get_target() != "text/plain") {
3443 /* Parse the "uri-list" format that Nautilus provides,
3444 where each pathname is delimited by \r\n.
3446 THERE MAY BE NO NULL TERMINATING CHAR!!!
3449 string txt = data.get_text();
3453 p = (char *) malloc (txt.length() + 1);
3454 txt.copy (p, txt.length(), 0);
3455 p[txt.length()] = '\0';
3461 while (g_ascii_isspace (*p))
3465 while (*q && (*q != '\n') && (*q != '\r')) {
3472 while (q > p && g_ascii_isspace (*q))
3477 uris.push_back (string (p, q - p + 1));
3481 p = strchr (p, '\n');
3493 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3494 if ((*i).substr (0,7) == "file://") {
3495 paths.push_back (Glib::filename_from_uri (*i));
3503 Editor::new_tempo_section ()
3508 Editor::map_transport_state ()
3510 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state);
3512 if (_session && _session->transport_stopped()) {
3513 have_pending_keyboard_selection = false;
3516 update_loop_range_view ();
3520 Editor::transport_looped ()
3522 /* reset Playhead position interpolation.
3523 * see Editor::super_rapid_screen_update
3525 _last_update_time = 0;
3531 Editor::begin_selection_op_history ()
3533 selection_op_cmd_depth = 0;
3534 selection_op_history_it = 0;
3536 while(!selection_op_history.empty()) {
3537 delete selection_op_history.front();
3538 selection_op_history.pop_front();
3541 selection_undo_action->set_sensitive (false);
3542 selection_redo_action->set_sensitive (false);
3543 selection_op_history.push_front (&_selection_memento->get_state ());
3547 Editor::begin_reversible_selection_op (string name)
3550 //cerr << name << endl;
3551 /* begin/commit pairs can be nested */
3552 selection_op_cmd_depth++;
3557 Editor::commit_reversible_selection_op ()
3560 if (selection_op_cmd_depth == 1) {
3562 if (selection_op_history_it > 0 && selection_op_history_it < selection_op_history.size()) {
3563 /* The user has undone some selection ops and then made a new one,
3564 * making anything earlier in the list invalid.
3567 list<XMLNode *>::iterator it = selection_op_history.begin();
3568 list<XMLNode *>::iterator e_it = it;
3569 advance (e_it, selection_op_history_it);
3571 for (; it != e_it; ++it) {
3574 selection_op_history.erase (selection_op_history.begin(), e_it);
3577 selection_op_history.push_front (&_selection_memento->get_state ());
3578 selection_op_history_it = 0;
3580 selection_undo_action->set_sensitive (true);
3581 selection_redo_action->set_sensitive (false);
3584 if (selection_op_cmd_depth > 0) {
3585 selection_op_cmd_depth--;
3591 Editor::undo_selection_op ()
3594 selection_op_history_it++;
3596 for (std::list<XMLNode *>::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) {
3597 if (n == selection_op_history_it) {
3598 _selection_memento->set_state (*(*i), Stateful::current_state_version);
3599 selection_redo_action->set_sensitive (true);
3603 /* is there an earlier entry? */
3604 if ((selection_op_history_it + 1) >= selection_op_history.size()) {
3605 selection_undo_action->set_sensitive (false);
3611 Editor::redo_selection_op ()
3614 if (selection_op_history_it > 0) {
3615 selection_op_history_it--;
3618 for (std::list<XMLNode *>::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) {
3619 if (n == selection_op_history_it) {
3620 _selection_memento->set_state (*(*i), Stateful::current_state_version);
3621 selection_undo_action->set_sensitive (true);
3626 if (selection_op_history_it == 0) {
3627 selection_redo_action->set_sensitive (false);
3633 Editor::begin_reversible_command (string name)
3636 before.push_back (&_selection_memento->get_state ());
3637 _session->begin_reversible_command (name);
3642 Editor::begin_reversible_command (GQuark q)
3645 before.push_back (&_selection_memento->get_state ());
3646 _session->begin_reversible_command (q);
3651 Editor::abort_reversible_command ()
3654 while(!before.empty()) {
3655 delete before.front();
3658 _session->abort_reversible_command ();
3663 Editor::commit_reversible_command ()
3666 if (before.size() == 1) {
3667 _session->add_command (new MementoCommand<SelectionMemento>(*(_selection_memento), before.front(), &_selection_memento->get_state ()));
3668 redo_action->set_sensitive(false);
3669 undo_action->set_sensitive(true);
3670 begin_selection_op_history ();
3673 if (before.empty()) {
3674 cerr << "Please call begin_reversible_command() before commit_reversible_command()." << endl;
3679 _session->commit_reversible_command ();
3684 Editor::history_changed ()
3688 if (undo_action && _session) {
3689 if (_session->undo_depth() == 0) {
3690 label = S_("Command|Undo");
3692 label = string_compose(S_("Command|Undo (%1)"), _session->next_undo());
3694 undo_action->property_label() = label;
3697 if (redo_action && _session) {
3698 if (_session->redo_depth() == 0) {
3700 redo_action->set_sensitive (false);
3702 label = string_compose(_("Redo (%1)"), _session->next_redo());
3703 redo_action->set_sensitive (true);
3705 redo_action->property_label() = label;
3710 Editor::duplicate_range (bool with_dialog)
3714 RegionSelection rs = get_regions_from_selection_and_entered ();
3716 if (selection->time.length() == 0 && rs.empty()) {
3722 ArdourDialog win (_("Duplicate"));
3723 Label label (_("Number of duplications:"));
3724 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3725 SpinButton spinner (adjustment, 0.0, 1);
3728 win.get_vbox()->set_spacing (12);
3729 win.get_vbox()->pack_start (hbox);
3730 hbox.set_border_width (6);
3731 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3733 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3734 place, visually. so do this by hand.
3737 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3738 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3739 spinner.grab_focus();
3745 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3746 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3747 win.set_default_response (RESPONSE_ACCEPT);
3749 spinner.grab_focus ();
3751 switch (win.run ()) {
3752 case RESPONSE_ACCEPT:
3758 times = adjustment.get_value();
3761 if ((current_mouse_mode() == MouseRange)) {
3762 if (selection->time.length()) {
3763 duplicate_selection (times);
3765 } else if (get_smart_mode()) {
3766 if (selection->time.length()) {
3767 duplicate_selection (times);
3769 duplicate_some_regions (rs, times);
3771 duplicate_some_regions (rs, times);
3776 Editor::set_edit_mode (EditMode m)
3778 Config->set_edit_mode (m);
3782 Editor::cycle_edit_mode ()
3784 switch (Config->get_edit_mode()) {
3786 Config->set_edit_mode (Ripple);
3790 Config->set_edit_mode (Lock);
3793 Config->set_edit_mode (Slide);
3799 Editor::edit_mode_selection_done (EditMode m)
3801 Config->set_edit_mode (m);
3805 Editor::grid_type_selection_done (GridType gridtype)
3807 RefPtr<RadioAction> ract = grid_type_action (gridtype);
3809 ract->set_active ();
3814 Editor::snap_mode_selection_done (SnapMode mode)
3816 RefPtr<RadioAction> ract = snap_mode_action (mode);
3819 ract->set_active (true);
3824 Editor::cycle_edit_point (bool with_marker)
3826 if(Profile->get_mixbus())
3827 with_marker = false;
3829 switch (_edit_point) {
3831 set_edit_point_preference (EditAtPlayhead);
3833 case EditAtPlayhead:
3835 set_edit_point_preference (EditAtSelectedMarker);
3837 set_edit_point_preference (EditAtMouse);
3840 case EditAtSelectedMarker:
3841 set_edit_point_preference (EditAtMouse);
3847 Editor::edit_point_selection_done (EditPoint ep)
3849 set_edit_point_preference (ep);
3853 Editor::build_zoom_focus_menu ()
3855 using namespace Menu_Helpers;
3857 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusLeft], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusLeft)));
3858 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusRight], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusRight)));
3859 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusCenter], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusCenter)));
3860 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusPlayhead], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusPlayhead)));
3861 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusMouse], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusMouse)));
3862 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusEdit], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusEdit)));
3864 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_TRIANGLE_WIDTH, 2);
3868 Editor::zoom_focus_selection_done (ZoomFocus f)
3870 RefPtr<RadioAction> ract = zoom_focus_action (f);
3872 ract->set_active ();
3877 Editor::build_track_count_menu ()
3879 using namespace Menu_Helpers;
3881 if (!Profile->get_mixbus()) {
3882 visible_tracks_selector.AddMenuElem (MenuElem (X_("1"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 1)));
3883 visible_tracks_selector.AddMenuElem (MenuElem (X_("2"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 2)));
3884 visible_tracks_selector.AddMenuElem (MenuElem (X_("3"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 3)));
3885 visible_tracks_selector.AddMenuElem (MenuElem (X_("4"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 4)));
3886 visible_tracks_selector.AddMenuElem (MenuElem (X_("8"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 8)));
3887 visible_tracks_selector.AddMenuElem (MenuElem (X_("12"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 12)));
3888 visible_tracks_selector.AddMenuElem (MenuElem (X_("16"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 16)));
3889 visible_tracks_selector.AddMenuElem (MenuElem (X_("20"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 20)));
3890 visible_tracks_selector.AddMenuElem (MenuElem (X_("24"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 24)));
3891 visible_tracks_selector.AddMenuElem (MenuElem (X_("32"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 32)));
3892 visible_tracks_selector.AddMenuElem (MenuElem (X_("64"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 64)));
3893 visible_tracks_selector.AddMenuElem (MenuElem (_("Selection"), sigc::mem_fun(*this, &Editor::fit_selection)));
3894 visible_tracks_selector.AddMenuElem (MenuElem (_("All"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 0)));
3896 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 1 track"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 1)));
3897 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 2 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 2)));
3898 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 4 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 4)));
3899 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 8 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 8)));
3900 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 16 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 16)));
3901 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 24 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 24)));
3902 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 32 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 32)));
3903 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 48 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 48)));
3904 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit All tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 0)));
3905 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit Selection"), sigc::mem_fun(*this, &Editor::fit_selection)));
3907 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 10 ms"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 10)));
3908 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 100 ms"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 100)));
3909 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 1 sec"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 1 * 1000)));
3910 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 10 sec"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 10 * 1000)));
3911 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 1 min"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 60 * 1000)));
3912 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 10 min"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 10 * 60 * 1000)));
3913 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 1 hour"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 60 * 60 * 1000)));
3914 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 8 hours"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 8 * 60 * 60 * 1000)));
3915 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 24 hours"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 24 * 60 * 60 * 1000)));
3916 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Session"), sigc::mem_fun(*this, &Editor::temporal_zoom_session)));
3917 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Extents"), sigc::mem_fun(*this, &Editor::temporal_zoom_extents)));
3918 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Range/Region Selection"), sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_selection), Horizontal)));
3923 Editor::set_zoom_preset (int64_t ms)
3926 temporal_zoom_session();
3930 ARDOUR::samplecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
3931 temporal_zoom ((sample_rate * ms / 1000) / _visible_canvas_width);
3935 Editor::set_visible_track_count (int32_t n)
3937 _visible_track_count = n;
3939 /* if the canvas hasn't really been allocated any size yet, just
3940 record the desired number of visible tracks and return. when canvas
3941 allocation happens, we will get called again and then we can do the
3945 if (_visible_canvas_height <= 1) {
3951 DisplaySuspender ds;
3953 if (_visible_track_count > 0) {
3954 h = trackviews_height() / _visible_track_count;
3955 std::ostringstream s;
3956 s << _visible_track_count;
3958 } else if (_visible_track_count == 0) {
3960 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
3961 if ((*i)->marked_for_display()) {
3963 TimeAxisView::Children cl ((*i)->get_child_list ());
3964 for (TimeAxisView::Children::const_iterator j = cl.begin(); j != cl.end(); ++j) {
3965 if ((*j)->marked_for_display()) {
3972 visible_tracks_selector.set_text (X_("*"));
3975 h = trackviews_height() / n;
3978 /* negative value means that the visible track count has
3979 been overridden by explicit track height changes.
3981 visible_tracks_selector.set_text (X_("*"));
3985 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3986 (*i)->set_height (h, TimeAxisView::HeightPerLane);
3989 if (str != visible_tracks_selector.get_text()) {
3990 visible_tracks_selector.set_text (str);
3995 Editor::override_visible_track_count ()
3997 _visible_track_count = -1;
3998 visible_tracks_selector.set_text (_("*"));
4002 Editor::edit_controls_button_release (GdkEventButton* ev)
4004 if (Keyboard::is_context_menu_event (ev)) {
4005 ARDOUR_UI::instance()->add_route ();
4006 } else if (ev->button == 1) {
4007 selection->clear_tracks ();
4014 Editor::mouse_select_button_release (GdkEventButton* ev)
4016 /* this handles just right-clicks */
4018 if (ev->button != 3) {
4026 Editor::set_zoom_focus (ZoomFocus f)
4028 string str = zoom_focus_strings[(int)f];
4030 if (str != zoom_focus_selector.get_text()) {
4031 zoom_focus_selector.set_text (str);
4034 if (zoom_focus != f) {
4041 Editor::cycle_zoom_focus ()
4043 switch (zoom_focus) {
4045 set_zoom_focus (ZoomFocusRight);
4047 case ZoomFocusRight:
4048 set_zoom_focus (ZoomFocusCenter);
4050 case ZoomFocusCenter:
4051 set_zoom_focus (ZoomFocusPlayhead);
4053 case ZoomFocusPlayhead:
4054 set_zoom_focus (ZoomFocusMouse);
4056 case ZoomFocusMouse:
4057 set_zoom_focus (ZoomFocusEdit);
4060 set_zoom_focus (ZoomFocusLeft);
4066 Editor::update_grid ()
4068 if (grid_musical()) {
4069 std::vector<TempoMap::BBTPoint> grid;
4070 if (bbt_ruler_scale != bbt_show_many) {
4071 compute_current_bbt_points (grid, _leftmost_sample, _leftmost_sample + current_page_samples());
4073 maybe_draw_grid_lines ();
4074 } else if (grid_nonmusical()) {
4075 maybe_draw_grid_lines ();
4082 Editor::toggle_follow_playhead ()
4084 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
4086 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
4087 set_follow_playhead (tact->get_active());
4091 /** @param yn true to follow playhead, otherwise false.
4092 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
4095 Editor::set_follow_playhead (bool yn, bool catch_up)
4097 if (_follow_playhead != yn) {
4098 if ((_follow_playhead = yn) == true && catch_up) {
4100 reset_x_origin_to_follow_playhead ();
4107 Editor::toggle_stationary_playhead ()
4109 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
4111 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
4112 set_stationary_playhead (tact->get_active());
4117 Editor::set_stationary_playhead (bool yn)
4119 if (_stationary_playhead != yn) {
4120 if ((_stationary_playhead = yn) == true) {
4121 /* catch up -- FIXME need a 3.0 equivalent of this 2.X call */
4122 // update_current_screen ();
4129 Editor::playlist_selector () const
4131 return *_playlist_selector;
4135 Editor::get_paste_offset (samplepos_t pos, unsigned paste_count, samplecnt_t duration)
4137 if (paste_count == 0) {
4138 /* don't bother calculating an offset that will be zero anyway */
4142 /* calculate basic unsnapped multi-paste offset */
4143 samplecnt_t offset = paste_count * duration;
4145 /* snap offset so pos + offset is aligned to the grid */
4146 MusicSample offset_pos (pos + offset, 0);
4147 snap_to(offset_pos, RoundUpMaybe);
4148 offset = offset_pos.sample - pos;
4154 Editor::get_grid_beat_divisions(samplepos_t position)
4156 switch (_grid_type) {
4157 case GridTypeBeatDiv32: return 32;
4158 case GridTypeBeatDiv28: return 28;
4159 case GridTypeBeatDiv24: return 24;
4160 case GridTypeBeatDiv20: return 20;
4161 case GridTypeBeatDiv16: return 16;
4162 case GridTypeBeatDiv14: return 14;
4163 case GridTypeBeatDiv12: return 12;
4164 case GridTypeBeatDiv10: return 10;
4165 case GridTypeBeatDiv8: return 8;
4166 case GridTypeBeatDiv7: return 7;
4167 case GridTypeBeatDiv6: return 6;
4168 case GridTypeBeatDiv5: return 5;
4169 case GridTypeBeatDiv4: return 4;
4170 case GridTypeBeatDiv3: return 3;
4171 case GridTypeBeatDiv2: return 2;
4172 case GridTypeBeat: return 1;
4173 case GridTypeBar: return 1;
4175 case GridTypeNone: return 0;
4176 case GridTypeTimecode: return 0;
4177 case GridTypeMinSec: return 0;
4178 case GridTypeCDFrame: return 0;
4184 /** returns the current musical grid divisiions using the supplied modifier mask from a GtkEvent.
4185 if the grid is non-musical, returns 0.
4186 if the grid is snapped to bars, returns -1.
4187 @param event_state the current keyboard modifier mask.
4190 Editor::get_grid_music_divisions (uint32_t event_state)
4192 if (snap_mode() == SnapOff && !ArdourKeyboard::indicates_snap (event_state)) {
4196 if (snap_mode() != SnapOff && ArdourKeyboard::indicates_snap (event_state)) {
4200 switch (_grid_type) {
4201 case GridTypeBeatDiv32: return 32;
4202 case GridTypeBeatDiv28: return 28;
4203 case GridTypeBeatDiv24: return 24;
4204 case GridTypeBeatDiv20: return 20;
4205 case GridTypeBeatDiv16: return 16;
4206 case GridTypeBeatDiv14: return 14;
4207 case GridTypeBeatDiv12: return 12;
4208 case GridTypeBeatDiv10: return 10;
4209 case GridTypeBeatDiv8: return 8;
4210 case GridTypeBeatDiv7: return 7;
4211 case GridTypeBeatDiv6: return 6;
4212 case GridTypeBeatDiv5: return 5;
4213 case GridTypeBeatDiv4: return 4;
4214 case GridTypeBeatDiv3: return 3;
4215 case GridTypeBeatDiv2: return 2;
4216 case GridTypeBeat: return 1;
4217 case GridTypeBar : return -1;
4219 case GridTypeNone: return 0;
4220 case GridTypeTimecode: return 0;
4221 case GridTypeMinSec: return 0;
4222 case GridTypeCDFrame: return 0;
4228 Editor::get_grid_type_as_beats (bool& success, samplepos_t position)
4232 const unsigned divisions = get_grid_beat_divisions(position);
4234 return Temporal::Beats(1.0 / (double)get_grid_beat_divisions(position));
4237 switch (_grid_type) {
4239 return Temporal::Beats(4.0 / _session->tempo_map().meter_at_sample (position).note_divisor());
4242 const Meter& m = _session->tempo_map().meter_at_sample (position);
4243 return Temporal::Beats((4.0 * m.divisions_per_bar()) / m.note_divisor());
4251 return Temporal::Beats();
4255 Editor::get_nudge_distance (samplepos_t pos, samplecnt_t& next)
4259 ret = nudge_clock->current_duration (pos);
4260 next = ret + 1; /* XXXX fix me */
4266 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
4268 ArdourDialog dialog (_("Playlist Deletion"));
4269 Label label (string_compose (_("Playlist %1 is currently unused.\n"
4270 "If it is kept, its audio files will not be cleaned.\n"
4271 "If it is deleted, audio files used by it alone will be cleaned."),
4274 dialog.set_position (WIN_POS_CENTER);
4275 dialog.get_vbox()->pack_start (label);
4279 dialog.add_button (_("Delete All Unused"), RESPONSE_YES); // needs clarification. this and all remaining ones
4280 dialog.add_button (_("Delete Playlist"), RESPONSE_ACCEPT);
4281 Button* keep = dialog.add_button (_("Keep Playlist"), RESPONSE_REJECT);
4282 dialog.add_button (_("Keep Remaining"), RESPONSE_NO); // ditto
4283 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
4285 /* by default gtk uses the left most button */
4286 keep->grab_focus ();
4288 switch (dialog.run ()) {
4290 /* keep this and all remaining ones */
4295 /* delete this and all others */
4299 case RESPONSE_ACCEPT:
4300 /* delete the playlist */
4304 case RESPONSE_REJECT:
4305 /* keep the playlist */
4317 Editor::audio_region_selection_covers (samplepos_t where)
4319 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
4320 if ((*a)->region()->covers (where)) {
4329 Editor::prepare_for_cleanup ()
4331 cut_buffer->clear_regions ();
4332 cut_buffer->clear_playlists ();
4334 selection->clear_regions ();
4335 selection->clear_playlists ();
4337 _regions->suspend_redisplay ();
4341 Editor::finish_cleanup ()
4343 _regions->resume_redisplay ();
4347 Editor::transport_loop_location()
4350 return _session->locations()->auto_loop_location();
4357 Editor::transport_punch_location()
4360 return _session->locations()->auto_punch_location();
4367 Editor::control_layout_scroll (GdkEventScroll* ev)
4369 /* Just forward to the normal canvas scroll method. The coordinate
4370 systems are different but since the canvas is always larger than the
4371 track headers, and aligned with the trackview area, this will work.
4373 In the not too distant future this layout is going away anyway and
4374 headers will be on the canvas.
4376 return canvas_scroll_event (ev, false);
4380 Editor::session_state_saved (string)
4383 _snapshots->redisplay ();
4387 Editor::maximise_editing_space ()
4393 Gtk::Window* toplevel = current_toplevel();
4396 toplevel->fullscreen ();
4402 Editor::restore_editing_space ()
4408 Gtk::Window* toplevel = current_toplevel();
4411 toplevel->unfullscreen();
4417 * Make new playlists for a given track and also any others that belong
4418 * to the same active route group with the `select' property.
4423 Editor::new_playlists (TimeAxisView* v)
4425 begin_reversible_command (_("new playlists"));
4426 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4427 _session->playlists->get (playlists);
4428 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::group_select.property_id);
4429 commit_reversible_command ();
4433 * Use a copy of the current playlist for a given track and also any others that belong
4434 * to the same active route group with the `select' property.
4439 Editor::copy_playlists (TimeAxisView* v)
4441 begin_reversible_command (_("copy playlists"));
4442 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4443 _session->playlists->get (playlists);
4444 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::group_select.property_id);
4445 commit_reversible_command ();
4448 /** Clear the current playlist for a given track and also any others that belong
4449 * to the same active route group with the `select' property.
4454 Editor::clear_playlists (TimeAxisView* v)
4456 begin_reversible_command (_("clear playlists"));
4457 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4458 _session->playlists->get (playlists);
4459 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::group_select.property_id);
4460 commit_reversible_command ();
4464 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4466 atv.use_new_playlist (sz > 1 ? false : true, playlists, false);
4470 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4472 atv.use_new_playlist (sz > 1 ? false : true, playlists, true);
4476 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4478 atv.clear_playlist ();
4482 Editor::get_y_origin () const
4484 return vertical_adjustment.get_value ();
4487 /** Queue up a change to the viewport x origin.
4488 * @param sample New x origin.
4491 Editor::reset_x_origin (samplepos_t sample)
4493 pending_visual_change.add (VisualChange::TimeOrigin);
4494 pending_visual_change.time_origin = sample;
4495 ensure_visual_change_idle_handler ();
4499 Editor::reset_y_origin (double y)
4501 pending_visual_change.add (VisualChange::YOrigin);
4502 pending_visual_change.y_origin = y;
4503 ensure_visual_change_idle_handler ();
4507 Editor::reset_zoom (samplecnt_t spp)
4509 if (spp == samples_per_pixel) {
4513 pending_visual_change.add (VisualChange::ZoomLevel);
4514 pending_visual_change.samples_per_pixel = spp;
4515 ensure_visual_change_idle_handler ();
4519 Editor::reposition_and_zoom (samplepos_t sample, double fpu)
4521 reset_x_origin (sample);
4524 if (!no_save_visual) {
4525 undo_visual_stack.push_back (current_visual_state(false));
4529 Editor::VisualState::VisualState (bool with_tracks)
4530 : gui_state (with_tracks ? new GUIObjectState : 0)
4534 Editor::VisualState::~VisualState ()
4539 Editor::VisualState*
4540 Editor::current_visual_state (bool with_tracks)
4542 VisualState* vs = new VisualState (with_tracks);
4543 vs->y_position = vertical_adjustment.get_value();
4544 vs->samples_per_pixel = samples_per_pixel;
4545 vs->_leftmost_sample = _leftmost_sample;
4546 vs->zoom_focus = zoom_focus;
4549 vs->gui_state->set_state (ARDOUR_UI::instance()->gui_object_state->get_state());
4556 Editor::undo_visual_state ()
4558 if (undo_visual_stack.empty()) {
4562 VisualState* vs = undo_visual_stack.back();
4563 undo_visual_stack.pop_back();
4566 redo_visual_stack.push_back (current_visual_state (vs ? vs->gui_state != 0 : false));
4569 use_visual_state (*vs);
4574 Editor::redo_visual_state ()
4576 if (redo_visual_stack.empty()) {
4580 VisualState* vs = redo_visual_stack.back();
4581 redo_visual_stack.pop_back();
4583 /* XXX: can 'vs' really be 0? Is there a place that puts NULL pointers onto the stack? */
4584 undo_visual_stack.push_back (current_visual_state (vs ? (vs->gui_state != 0) : false));
4587 use_visual_state (*vs);
4592 Editor::swap_visual_state ()
4594 if (undo_visual_stack.empty()) {
4595 redo_visual_state ();
4597 undo_visual_state ();
4602 Editor::use_visual_state (VisualState& vs)
4604 PBD::Unwinder<bool> nsv (no_save_visual, true);
4605 DisplaySuspender ds;
4607 vertical_adjustment.set_value (vs.y_position);
4609 set_zoom_focus (vs.zoom_focus);
4610 reposition_and_zoom (vs._leftmost_sample, vs.samples_per_pixel);
4613 ARDOUR_UI::instance()->gui_object_state->set_state (vs.gui_state->get_state());
4615 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4616 (*i)->clear_property_cache();
4617 (*i)->reset_visual_state ();
4621 _routes->update_visibility ();
4624 /** This is the core function that controls the zoom level of the canvas. It is called
4625 * whenever one or more calls are made to reset_zoom(). It executes in an idle handler.
4626 * @param spp new number of samples per pixel
4629 Editor::set_samples_per_pixel (samplecnt_t spp)
4635 const samplecnt_t three_days = 3 * 24 * 60 * 60 * (_session ? _session->sample_rate() : 48000);
4636 const samplecnt_t lots_of_pixels = 4000;
4638 /* if the zoom level is greater than what you'd get trying to display 3
4639 * days of audio on a really big screen, then it's too big.
4642 if (spp * lots_of_pixels > three_days) {
4646 samples_per_pixel = spp;
4650 Editor::on_samples_per_pixel_changed ()
4652 bool const showing_time_selection = selection->time.length() > 0;
4654 if (showing_time_selection && selection->time.start () != selection->time.end_sample ()) {
4655 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4656 (*i)->reshow_selection (selection->time);
4660 ZoomChanged (); /* EMIT_SIGNAL */
4662 ArdourCanvas::GtkCanvasViewport* c;
4664 c = get_track_canvas();
4666 c->canvas()->zoomed ();
4669 if (playhead_cursor) {
4670 playhead_cursor->set_position (playhead_cursor->current_sample ());
4673 refresh_location_display();
4674 _summary->set_overlays_dirty ();
4676 update_marker_labels ();
4682 Editor::playhead_cursor_sample () const
4684 return playhead_cursor->current_sample();
4688 Editor::queue_visual_videotimeline_update ()
4690 pending_visual_change.add (VisualChange::VideoTimeline);
4691 ensure_visual_change_idle_handler ();
4695 Editor::ensure_visual_change_idle_handler ()
4697 if (pending_visual_change.idle_handler_id < 0) {
4698 /* see comment in add_to_idle_resize above. */
4699 pending_visual_change.idle_handler_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, _idle_visual_changer, this, NULL);
4700 pending_visual_change.being_handled = false;
4705 Editor::_idle_visual_changer (void* arg)
4707 return static_cast<Editor*>(arg)->idle_visual_changer ();
4711 Editor::pre_render ()
4713 visual_change_queued = false;
4715 if (pending_visual_change.pending != 0) {
4716 ensure_visual_change_idle_handler();
4721 Editor::idle_visual_changer ()
4723 pending_visual_change.idle_handler_id = -1;
4725 if (pending_visual_change.pending == 0) {
4729 /* set_horizontal_position() below (and maybe other calls) call
4730 gtk_main_iteration(), so it's possible that a signal will be handled
4731 half-way through this method. If this signal wants an
4732 idle_visual_changer we must schedule another one after this one, so
4733 mark the idle_handler_id as -1 here to allow that. Also make a note
4734 that we are doing the visual change, so that changes in response to
4735 super-rapid-screen-update can be dropped if we are still processing
4739 if (visual_change_queued) {
4743 pending_visual_change.being_handled = true;
4745 VisualChange vc = pending_visual_change;
4747 pending_visual_change.pending = (VisualChange::Type) 0;
4749 visual_changer (vc);
4751 pending_visual_change.being_handled = false;
4753 visual_change_queued = true;
4755 return 0; /* this is always a one-shot call */
4759 Editor::visual_changer (const VisualChange& vc)
4762 * Changed first so the correct horizontal canvas position is calculated in
4763 * Editor::set_horizontal_position
4765 if (vc.pending & VisualChange::ZoomLevel) {
4766 set_samples_per_pixel (vc.samples_per_pixel);
4769 if (vc.pending & VisualChange::TimeOrigin) {
4770 double new_time_origin = sample_to_pixel_unrounded (vc.time_origin);
4771 set_horizontal_position (new_time_origin);
4774 if (vc.pending & VisualChange::YOrigin) {
4775 vertical_adjustment.set_value (vc.y_origin);
4779 * Now the canvas is in the final state before render the canvas items that
4780 * support the Item::prepare_for_render interface can calculate the correct
4781 * item to visible canvas intersection.
4783 if (vc.pending & VisualChange::ZoomLevel) {
4784 on_samples_per_pixel_changed ();
4786 compute_fixed_ruler_scale ();
4788 compute_bbt_ruler_scale (vc.time_origin, pending_visual_change.time_origin + current_page_samples());
4789 update_tempo_based_rulers ();
4792 if (!(vc.pending & VisualChange::ZoomLevel)) {
4793 /* If the canvas is not being zoomed then the canvas items will not change
4794 * and cause Item::prepare_for_render to be called so do it here manually.
4795 * Not ideal, but I can't think of a better solution atm.
4797 _track_canvas->prepare_for_render();
4800 /* If we are only scrolling vertically there is no need to update these */
4801 if (vc.pending != VisualChange::YOrigin) {
4802 update_fixed_rulers ();
4803 redisplay_grid (true);
4805 /* video frames & position need to be updated for zoom, horiz-scroll
4806 * and (explicitly) VisualChange::VideoTimeline.
4808 update_video_timeline();
4811 _summary->set_overlays_dirty ();
4814 struct EditorOrderTimeAxisSorter {
4815 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4816 return a->order () < b->order ();
4821 Editor::sort_track_selection (TrackViewList& sel)
4823 EditorOrderTimeAxisSorter cmp;
4828 Editor::get_preferred_edit_position (EditIgnoreOption ignore, bool from_context_menu, bool from_outside_canvas)
4831 samplepos_t where = 0;
4832 EditPoint ep = _edit_point;
4834 if (Profile->get_mixbus()) {
4835 if (ep == EditAtSelectedMarker) {
4836 ep = EditAtPlayhead;
4840 if (from_outside_canvas && (ep == EditAtMouse)) {
4841 ep = EditAtPlayhead;
4842 } else if (from_context_menu && (ep == EditAtMouse)) {
4843 return canvas_event_sample (&context_click_event, 0, 0);
4846 if (entered_marker) {
4847 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4848 return entered_marker->position();
4851 if ((ignore == EDIT_IGNORE_PHEAD) && ep == EditAtPlayhead) {
4852 ep = EditAtSelectedMarker;
4855 if ((ignore == EDIT_IGNORE_MOUSE) && ep == EditAtMouse) {
4856 ep = EditAtPlayhead;
4859 MusicSample snap_mf (0, 0);
4862 case EditAtPlayhead:
4863 if (_dragging_playhead) {
4864 /* NOTE: since the user is dragging with the mouse, this operation will implicitly be Snapped */
4865 where = playhead_cursor->current_sample();
4867 where = _session->audible_sample();
4869 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4872 case EditAtSelectedMarker:
4873 if (!selection->markers.empty()) {
4875 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4878 where = loc->start();
4882 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4890 if (!mouse_sample (where, ignored)) {
4891 /* XXX not right but what can we do ? */
4894 snap_mf.sample = where;
4896 where = snap_mf.sample;
4897 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4905 Editor::set_loop_range (samplepos_t start, samplepos_t end, string cmd)
4907 if (!_session) return;
4909 begin_reversible_command (cmd);
4913 if ((tll = transport_loop_location()) == 0) {
4914 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop, get_grid_music_divisions(0));
4915 XMLNode &before = _session->locations()->get_state();
4916 _session->locations()->add (loc, true);
4917 _session->set_auto_loop_location (loc);
4918 XMLNode &after = _session->locations()->get_state();
4919 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4921 XMLNode &before = tll->get_state();
4922 tll->set_hidden (false, this);
4923 tll->set (start, end);
4924 XMLNode &after = tll->get_state();
4925 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4928 commit_reversible_command ();
4932 Editor::set_punch_range (samplepos_t start, samplepos_t end, string cmd)
4934 if (!_session) return;
4936 begin_reversible_command (cmd);
4940 if ((tpl = transport_punch_location()) == 0) {
4941 Location* loc = new Location (*_session, start, end, _("Punch"), Location::IsAutoPunch, get_grid_music_divisions(0));
4942 XMLNode &before = _session->locations()->get_state();
4943 _session->locations()->add (loc, true);
4944 _session->set_auto_punch_location (loc);
4945 XMLNode &after = _session->locations()->get_state();
4946 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4948 XMLNode &before = tpl->get_state();
4949 tpl->set_hidden (false, this);
4950 tpl->set (start, end);
4951 XMLNode &after = tpl->get_state();
4952 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4955 commit_reversible_command ();
4958 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4959 * @param rs List to which found regions are added.
4960 * @param where Time to look at.
4961 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4964 Editor::get_regions_at (RegionSelection& rs, samplepos_t where, const TrackViewList& ts) const
4966 const TrackViewList* tracks;
4969 tracks = &track_views;
4974 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4976 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4979 boost::shared_ptr<Track> tr;
4980 boost::shared_ptr<Playlist> pl;
4982 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4984 boost::shared_ptr<RegionList> regions = pl->regions_at (where);
4986 for (RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4987 RegionView* rv = rtv->view()->find_view (*i);
4998 Editor::get_regions_after (RegionSelection& rs, samplepos_t where, const TrackViewList& ts) const
5000 const TrackViewList* tracks;
5003 tracks = &track_views;
5008 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
5009 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
5011 boost::shared_ptr<Track> tr;
5012 boost::shared_ptr<Playlist> pl;
5014 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
5016 boost::shared_ptr<RegionList> regions = pl->regions_touched (where, max_samplepos);
5018 for (RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
5020 RegionView* rv = rtv->view()->find_view (*i);
5031 /** Get regions using the following method:
5033 * Make a region list using:
5034 * (a) any selected regions
5035 * (b) the intersection of any selected tracks and the edit point(*)
5036 * (c) if neither exists, and edit_point == mouse, then whatever region is under the mouse
5038 * (*) NOTE: in this case, if 'No Selection = All Tracks' is active, search all tracks
5040 * Note that we have forced the rule that selected regions and selected tracks are mutually exclusive
5044 Editor::get_regions_from_selection_and_edit_point (EditIgnoreOption ignore, bool from_context_menu, bool from_outside_canvas)
5046 RegionSelection regions;
5048 if (_edit_point == EditAtMouse && entered_regionview && selection->tracks.empty() && selection->regions.empty()) {
5049 regions.add (entered_regionview);
5051 regions = selection->regions;
5054 if (regions.empty()) {
5055 TrackViewList tracks = selection->tracks;
5057 if (!tracks.empty()) {
5058 /* no region selected or entered, but some selected tracks:
5059 * act on all regions on the selected tracks at the edit point
5061 samplepos_t const where = get_preferred_edit_position (ignore, from_context_menu, from_outside_canvas);
5062 get_regions_at(regions, where, tracks);
5069 /** Get regions using the following method:
5071 * Make a region list using:
5072 * (a) any selected regions
5073 * (b) the intersection of any selected tracks and the edit point(*)
5074 * (c) if neither exists, then whatever region is under the mouse
5076 * (*) NOTE: in this case, if 'No Selection = All Tracks' is active, search all tracks
5078 * Note that we have forced the rule that selected regions and selected tracks are mutually exclusive
5081 Editor::get_regions_from_selection_and_mouse (samplepos_t pos)
5083 RegionSelection regions;
5085 if (entered_regionview && selection->tracks.empty() && selection->regions.empty()) {
5086 regions.add (entered_regionview);
5088 regions = selection->regions;
5091 if (regions.empty()) {
5092 TrackViewList tracks = selection->tracks;
5094 if (!tracks.empty()) {
5095 /* no region selected or entered, but some selected tracks:
5096 * act on all regions on the selected tracks at the edit point
5098 get_regions_at(regions, pos, tracks);
5105 /** Start with regions that are selected, or the entered regionview if none are selected.
5106 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
5107 * of the regions that we started with.
5111 Editor::get_regions_from_selection_and_entered () const
5113 RegionSelection regions = selection->regions;
5115 if (regions.empty() && entered_regionview) {
5116 regions.add (entered_regionview);
5123 Editor::get_regionviews_by_id (PBD::ID const id, RegionSelection & regions) const
5125 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5126 RouteTimeAxisView* rtav;
5128 if ((rtav = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5129 boost::shared_ptr<Playlist> pl;
5130 std::vector<boost::shared_ptr<Region> > results;
5131 boost::shared_ptr<Track> tr;
5133 if ((tr = rtav->track()) == 0) {
5138 if ((pl = (tr->playlist())) != 0) {
5139 boost::shared_ptr<Region> r = pl->region_by_id (id);
5141 RegionView* rv = rtav->view()->find_view (r);
5143 regions.push_back (rv);
5152 Editor::get_per_region_note_selection (list<pair<PBD::ID, set<boost::shared_ptr<Evoral::Note<Temporal::Beats> > > > > &selection) const
5155 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5156 MidiTimeAxisView* mtav;
5158 if ((mtav = dynamic_cast<MidiTimeAxisView*> (*i)) != 0) {
5160 mtav->get_per_region_note_selection (selection);
5167 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions, bool src_comparison)
5169 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5171 RouteTimeAxisView* tatv;
5173 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5175 boost::shared_ptr<Playlist> pl;
5176 vector<boost::shared_ptr<Region> > results;
5178 boost::shared_ptr<Track> tr;
5180 if ((tr = tatv->track()) == 0) {
5185 if ((pl = (tr->playlist())) != 0) {
5186 if (src_comparison) {
5187 pl->get_source_equivalent_regions (region, results);
5189 pl->get_region_list_equivalent_regions (region, results);
5193 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
5194 if ((marv = tatv->view()->find_view (*ir)) != 0) {
5195 regions.push_back (marv);
5204 Editor::regionview_from_region (boost::shared_ptr<Region> region) const
5206 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5207 RouteTimeAxisView* tatv;
5208 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5209 if (!tatv->track()) {
5212 RegionView* marv = tatv->view()->find_view (region);
5222 Editor::rtav_from_route (boost::shared_ptr<Route> route) const
5224 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5225 RouteTimeAxisView* rtav;
5226 if ((rtav = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5227 if (rtav->route() == route) {
5236 Editor::show_rhythm_ferret ()
5238 if (rhythm_ferret == 0) {
5239 rhythm_ferret = new RhythmFerret(*this);
5242 rhythm_ferret->set_session (_session);
5243 rhythm_ferret->show ();
5244 rhythm_ferret->present ();
5248 Editor::first_idle ()
5250 MessageDialog* dialog = 0;
5252 if (track_views.size() > 1) {
5253 Timers::TimerSuspender t;
5254 dialog = new MessageDialog (
5255 string_compose (_("Please wait while %1 loads visual data."), PROGRAM_NAME),
5259 ARDOUR_UI::instance()->flush_pending (60);
5262 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
5266 /* now that all regionviews should exist, setup region selection */
5270 for (list<PBD::ID>::iterator pr = selection->regions.pending.begin (); pr != selection->regions.pending.end (); ++pr) {
5271 /* this is cumulative: rs is NOT cleared each time */
5272 get_regionviews_by_id (*pr, rs);
5275 selection->set (rs);
5277 /* first idle adds route children (automation tracks), so we need to redisplay here */
5278 _routes->redisplay ();
5282 if (_session->undo_depth() == 0) {
5283 undo_action->set_sensitive(false);
5285 redo_action->set_sensitive(false);
5286 begin_selection_op_history ();
5292 Editor::_idle_resize (gpointer arg)
5294 return ((Editor*)arg)->idle_resize ();
5298 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
5300 if (resize_idle_id < 0) {
5301 /* https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#G-PRIORITY-HIGH-IDLE:CAPS
5302 * GTK+ uses G_PRIORITY_HIGH_IDLE + 10 for resizing operations, and G_PRIORITY_HIGH_IDLE + 20 for redrawing operations.
5303 * (This is done to ensure that any pending resizes are processed before any pending redraws, so that widgets are not redrawn twice unnecessarily.)
5305 resize_idle_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, _idle_resize, this, NULL);
5306 _pending_resize_amount = 0;
5309 /* make a note of the smallest resulting height, so that we can clamp the
5310 lower limit at TimeAxisView::hSmall */
5312 int32_t min_resulting = INT32_MAX;
5314 _pending_resize_amount += h;
5315 _pending_resize_view = view;
5317 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
5319 if (selection->tracks.contains (_pending_resize_view)) {
5320 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5321 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
5325 if (min_resulting < 0) {
5330 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
5331 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
5335 /** Handle pending resizing of tracks */
5337 Editor::idle_resize ()
5339 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
5341 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
5342 selection->tracks.contains (_pending_resize_view)) {
5344 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5345 if (*i != _pending_resize_view) {
5346 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
5351 _pending_resize_amount = 0;
5352 _group_tabs->set_dirty ();
5353 resize_idle_id = -1;
5361 ENSURE_GUI_THREAD (*this, &Editor::located);
5364 playhead_cursor->set_position (_session->audible_sample ());
5365 if (_follow_playhead && !_pending_initial_locate) {
5366 reset_x_origin_to_follow_playhead ();
5370 _pending_locate_request = false;
5371 _pending_initial_locate = false;
5372 _last_update_time = 0;
5376 Editor::region_view_added (RegionView * rv)
5378 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (rv);
5380 list<pair<PBD::ID const, list<Evoral::event_id_t> > >::iterator rnote;
5381 for (rnote = selection->pending_midi_note_selection.begin(); rnote != selection->pending_midi_note_selection.end(); ++rnote) {
5382 if (rv->region()->id () == (*rnote).first) {
5383 mrv->select_notes ((*rnote).second);
5384 selection->pending_midi_note_selection.erase(rnote);
5390 _summary->set_background_dirty ();
5392 mark_region_boundary_cache_dirty ();
5396 Editor::region_view_removed ()
5398 _summary->set_background_dirty ();
5400 mark_region_boundary_cache_dirty ();
5404 Editor::axis_view_by_stripable (boost::shared_ptr<Stripable> s) const
5406 for (TrackViewList::const_iterator j = track_views.begin (); j != track_views.end(); ++j) {
5407 if ((*j)->stripable() == s) {
5416 Editor::axis_view_by_control (boost::shared_ptr<AutomationControl> c) const
5418 for (TrackViewList::const_iterator j = track_views.begin (); j != track_views.end(); ++j) {
5419 if ((*j)->control() == c) {
5423 TimeAxisView::Children kids = (*j)->get_child_list ();
5425 for (TimeAxisView::Children::iterator k = kids.begin(); k != kids.end(); ++k) {
5426 if ((*k)->control() == c) {
5436 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
5440 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
5441 TimeAxisView* tv = time_axis_view_from_stripable (*i);
5451 Editor::suspend_route_redisplay ()
5454 _routes->suspend_redisplay();
5459 Editor::resume_route_redisplay ()
5462 _routes->redisplay(); // queue redisplay
5463 _routes->resume_redisplay();
5468 Editor::add_vcas (VCAList& vlist)
5472 for (VCAList::iterator v = vlist.begin(); v != vlist.end(); ++v) {
5473 sl.push_back (boost::dynamic_pointer_cast<Stripable> (*v));
5476 add_stripables (sl);
5480 Editor::add_routes (RouteList& rlist)
5484 for (RouteList::iterator r = rlist.begin(); r != rlist.end(); ++r) {
5488 add_stripables (sl);
5492 Editor::add_stripables (StripableList& sl)
5494 list<TimeAxisView*> new_views;
5495 boost::shared_ptr<VCA> v;
5496 boost::shared_ptr<Route> r;
5497 TrackViewList new_selection;
5498 bool from_scratch = (track_views.size() == 0);
5500 sl.sort (Stripable::Sorter());
5502 for (StripableList::iterator s = sl.begin(); s != sl.end(); ++s) {
5504 if ((v = boost::dynamic_pointer_cast<VCA> (*s)) != 0) {
5506 VCATimeAxisView* vtv = new VCATimeAxisView (*this, _session, *_track_canvas);
5508 new_views.push_back (vtv);
5510 } else if ((r = boost::dynamic_pointer_cast<Route> (*s)) != 0) {
5512 if (r->is_auditioner() || r->is_monitor()) {
5516 RouteTimeAxisView* rtv;
5517 DataType dt = r->input()->default_type();
5519 if (dt == ARDOUR::DataType::AUDIO) {
5520 rtv = new AudioTimeAxisView (*this, _session, *_track_canvas);
5522 } else if (dt == ARDOUR::DataType::MIDI) {
5523 rtv = new MidiTimeAxisView (*this, _session, *_track_canvas);
5526 throw unknown_type();
5529 new_views.push_back (rtv);
5530 track_views.push_back (rtv);
5531 new_selection.push_back (rtv);
5533 rtv->effective_gain_display ();
5535 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
5536 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
5540 if (new_views.size() > 0) {
5541 _routes->time_axis_views_added (new_views);
5542 //_summary->routes_added (new_selection); /* XXX requires RouteTimeAxisViewList */
5545 /* note: !new_selection.empty() means that we got some routes rather
5549 if (!from_scratch && !new_selection.empty()) {
5550 selection->set (new_selection);
5551 begin_selection_op_history();
5554 if (show_editor_mixer_when_tracks_arrive && !new_selection.empty()) {
5555 show_editor_mixer (true);
5558 editor_list_button.set_sensitive (true);
5562 Editor::timeaxisview_deleted (TimeAxisView *tv)
5564 if (tv == entered_track) {
5568 if (_session && _session->deletion_in_progress()) {
5569 /* the situation is under control */
5573 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
5575 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
5577 _routes->route_removed (tv);
5579 TimeAxisView::Children c = tv->get_child_list ();
5580 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
5581 if (entered_track == i->get()) {
5586 /* remove it from the list of track views */
5588 TrackViewList::iterator i;
5590 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
5591 i = track_views.erase (i);
5594 /* update whatever the current mixer strip is displaying, if revelant */
5596 boost::shared_ptr<Route> route;
5599 route = rtav->route ();
5602 if (current_mixer_strip && current_mixer_strip->route() == route) {
5604 TimeAxisView* next_tv;
5606 if (track_views.empty()) {
5608 } else if (i == track_views.end()) {
5609 next_tv = track_views.front();
5614 // skip VCAs (cannot be selected, n/a in editor-mixer)
5615 if (dynamic_cast<VCATimeAxisView*> (next_tv)) {
5616 /* VCAs are sorted last in line -- route_sorter.h, jump to top */
5617 next_tv = track_views.front();
5619 if (dynamic_cast<VCATimeAxisView*> (next_tv)) {
5620 /* just in case: no master, only a VCA remains */
5626 set_selected_mixer_strip (*next_tv);
5628 /* make the editor mixer strip go away setting the
5629 * button to inactive (which also unticks the menu option)
5632 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
5638 Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection)
5644 DisplaySuspender ds;
5645 PresentationInfo::ChangeSuspender cs;
5647 if (apply_to_selection) {
5648 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end();) {
5650 TrackSelection::iterator j = i;
5653 hide_track_in_display (*i, false);
5658 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
5660 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
5661 /* this will hide the mixer strip */
5662 set_selected_mixer_strip (*tv);
5665 _routes->hide_track_in_display (*tv);
5670 Editor::show_track_in_display (TimeAxisView* tv, bool move_into_view)
5675 _routes->show_track_in_display (*tv);
5676 if (move_into_view) {
5677 ensure_time_axis_view_is_visible (*tv, false);
5682 Editor::sync_track_view_list_and_routes ()
5684 track_views = TrackViewList (_routes->views ());
5686 _summary->set_background_dirty();
5687 _group_tabs->set_dirty ();
5689 return false; // do not call again (until needed)
5693 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5695 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5700 /** Find a StripableTimeAxisView by the ID of its stripable */
5701 StripableTimeAxisView*
5702 Editor::get_stripable_time_axis_by_id (const PBD::ID& id) const
5704 StripableTimeAxisView* v;
5706 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5707 if((v = dynamic_cast<StripableTimeAxisView*>(*i)) != 0) {
5708 if(v->stripable()->id() == id) {
5718 Editor::fit_route_group (RouteGroup *g)
5720 TrackViewList ts = axis_views_from_routes (g->route_list ());
5725 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5727 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5730 _session->cancel_audition ();
5734 if (_session->is_auditioning()) {
5735 _session->cancel_audition ();
5736 if (r == last_audition_region) {
5741 _session->audition_region (r);
5742 last_audition_region = r;
5747 Editor::hide_a_region (boost::shared_ptr<Region> r)
5749 r->set_hidden (true);
5753 Editor::show_a_region (boost::shared_ptr<Region> r)
5755 r->set_hidden (false);
5759 Editor::audition_region_from_region_list ()
5761 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5765 Editor::hide_region_from_region_list ()
5767 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5771 Editor::show_region_in_region_list ()
5773 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5777 Editor::step_edit_status_change (bool yn)
5780 start_step_editing ();
5782 stop_step_editing ();
5787 Editor::start_step_editing ()
5789 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5793 Editor::stop_step_editing ()
5795 step_edit_connection.disconnect ();
5799 Editor::check_step_edit ()
5801 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5802 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5804 mtv->check_step_edit ();
5808 return true; // do it again, till we stop
5812 Editor::scroll_press (Direction dir)
5814 ++_scroll_callbacks;
5816 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5817 /* delay the first auto-repeat */
5823 scroll_backward (1);
5831 scroll_up_one_track ();
5835 scroll_down_one_track ();
5839 /* do hacky auto-repeat */
5840 if (!_scroll_connection.connected ()) {
5842 _scroll_connection = Glib::signal_timeout().connect (
5843 sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100
5846 _scroll_callbacks = 0;
5853 Editor::scroll_release ()
5855 _scroll_connection.disconnect ();
5858 /** Queue a change for the Editor viewport x origin to follow the playhead */
5860 Editor::reset_x_origin_to_follow_playhead ()
5862 samplepos_t const sample = playhead_cursor->current_sample ();
5864 if (sample < _leftmost_sample || sample > _leftmost_sample + current_page_samples()) {
5866 if (_session->transport_speed() < 0) {
5868 if (sample > (current_page_samples() / 2)) {
5869 center_screen (sample-(current_page_samples()/2));
5871 center_screen (current_page_samples()/2);
5878 if (sample < _leftmost_sample) {
5880 if (_session->transport_rolling()) {
5881 /* rolling; end up with the playhead at the right of the page */
5882 l = sample - current_page_samples ();
5884 /* not rolling: end up with the playhead 1/4 of the way along the page */
5885 l = sample - current_page_samples() / 4;
5889 if (_session->transport_rolling()) {
5890 /* rolling: end up with the playhead on the left of the page */
5893 /* not rolling: end up with the playhead 3/4 of the way along the page */
5894 l = sample - 3 * current_page_samples() / 4;
5902 center_screen_internal (l + (current_page_samples() / 2), current_page_samples ());
5908 Editor::super_rapid_screen_update ()
5910 if (!_session || !_session->engine().running()) {
5914 /* METERING / MIXER STRIPS */
5916 /* update track meters, if required */
5917 if (contents().is_mapped() && meters_running) {
5918 RouteTimeAxisView* rtv;
5919 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5920 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5921 rtv->fast_update ();
5926 /* and any current mixer strip */
5927 if (current_mixer_strip) {
5928 current_mixer_strip->fast_update ();
5931 bool latent_locate = false;
5932 samplepos_t sample = _session->audible_sample (&latent_locate);
5933 const int64_t now = g_get_monotonic_time ();
5936 if (_session->exporting ()) {
5937 /* freewheel/export may be faster or slower than transport_speed() / SR.
5938 * Also exporting multiple ranges locates/jumps without a _pending_locate_request.
5940 _last_update_time = 0;
5943 if (!_session->transport_rolling () || _session->is_auditioning ()) {
5944 /* Do not interpolate the playhead position; just set it */
5945 _last_update_time = 0;
5948 if (_last_update_time > 0) {
5949 /* interpolate and smoothen playhead position */
5950 const double ds = (now - _last_update_time) * _session->transport_speed() * _session->nominal_sample_rate () * 1e-6;
5951 samplepos_t guess = playhead_cursor->current_sample () + rint (ds);
5952 err = sample - guess;
5954 guess += err * .12 + _err_screen_engine; // time-constant based on 25fps (super_rapid_screen_update)
5955 _err_screen_engine += .0144 * (err - _err_screen_engine); // tc^2
5958 printf ("eng: %ld gui:%ld (%+6.1f) diff: %6.1f (err: %7.2f)\n",
5960 err, _err_screen_engine);
5965 _err_screen_engine = 0;
5968 if (err > 8192 || latent_locate) {
5969 // in case of x-runs or freewheeling
5970 _last_update_time = 0;
5971 sample = _session->audible_sample ();
5973 _last_update_time = now;
5976 /* snapped cursor stuff (the snapped_cursor shows where an operation is going to occur) */
5978 MusicSample where (sample, 0);
5979 if (!UIConfiguration::instance().get_show_snapped_cursor()) {
5980 snapped_cursor->hide ();
5981 } else if (_edit_point == EditAtPlayhead && !_dragging_playhead) {
5982 /* EditAtPlayhead does not snap */
5983 } else if (_edit_point == EditAtSelectedMarker) {
5984 /* NOTE: I don't think EditAtSelectedMarker should snap. They are what they are.
5985 * however, the current editing code -does- snap so I'll draw it that way for now.
5987 if (!selection->markers.empty()) {
5988 MusicSample ms (selection->markers.front()->position(), 0);
5989 snap_to (ms); // should use snap_to_with_modifier?
5990 snapped_cursor->set_position (ms.sample);
5991 snapped_cursor->show ();
5993 } else if (mouse_sample (where.sample, ignored)) { // cursor is in the editing canvas. show it.
5994 snapped_cursor->show ();
5995 } else { // mouse is out of the editing canvas. hide the snapped_cursor
5996 snapped_cursor->hide ();
5999 /* There are a few reasons why we might not update the playhead / viewport stuff:
6001 * 1. we don't update things when there's a pending locate request, otherwise
6002 * when the editor requests a locate there is a chance that this method
6003 * will move the playhead before the locate request is processed, causing
6005 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
6006 * 3. if we're still at the same frame that we were last time, there's nothing to do.
6008 if (_pending_locate_request) {
6009 _last_update_time = 0;
6013 if (_dragging_playhead) {
6014 _last_update_time = 0;
6018 if (playhead_cursor->current_sample () == sample) {
6022 playhead_cursor->set_position (sample);
6024 if (_session->requested_return_sample() >= 0) {
6025 _last_update_time = 0;
6029 if (!_follow_playhead || pending_visual_change.being_handled) {
6030 /* We only do this if we aren't already
6031 * handling a visual change (ie if
6032 * pending_visual_change.being_handled is
6033 * false) so that these requests don't stack
6034 * up there are too many of them to handle in
6040 if (!_stationary_playhead) {
6041 reset_x_origin_to_follow_playhead ();
6043 samplepos_t const sample = playhead_cursor->current_sample ();
6044 double target = ((double)sample - (double)current_page_samples() / 2.0);
6045 if (target <= 0.0) {
6048 /* compare to EditorCursor::set_position() */
6049 double const old_pos = sample_to_pixel_unrounded (_leftmost_sample);
6050 double const new_pos = sample_to_pixel_unrounded (target);
6051 if (rint (new_pos) != rint (old_pos)) {
6052 reset_x_origin (pixel_to_sample (new_pos));
6059 Editor::session_going_away ()
6061 _have_idled = false;
6063 _session_connections.drop_connections ();
6065 super_rapid_screen_update_connection.disconnect ();
6067 selection->clear ();
6068 cut_buffer->clear ();
6070 clicked_regionview = 0;
6071 clicked_axisview = 0;
6072 clicked_routeview = 0;
6073 entered_regionview = 0;
6075 _last_update_time = 0;
6078 playhead_cursor->hide ();
6080 /* rip everything out of the list displays */
6084 _route_groups->clear ();
6086 /* do this first so that deleting a track doesn't reset cms to null
6087 and thus cause a leak.
6090 if (current_mixer_strip) {
6091 if (current_mixer_strip->get_parent() != 0) {
6092 global_hpacker.remove (*current_mixer_strip);
6094 delete current_mixer_strip;
6095 current_mixer_strip = 0;
6098 /* delete all trackviews */
6100 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6103 track_views.clear ();
6105 nudge_clock->set_session (0);
6107 editor_list_button.set_active(false);
6108 editor_list_button.set_sensitive(false);
6110 /* clear tempo/meter rulers */
6111 remove_metric_marks ();
6112 clear_marker_display ();
6118 stop_step_editing ();
6122 /* get rid of any existing editor mixer strip */
6124 WindowTitle title(Glib::get_application_name());
6125 title += _("Editor");
6127 own_window()->set_title (title.get_string());
6130 SessionHandlePtr::session_going_away ();
6134 Editor::trigger_script (int i)
6136 LuaInstance::instance()-> call_action (i);
6140 Editor::show_editor_list (bool yn)
6143 _editor_list_vbox.show ();
6145 _editor_list_vbox.hide ();
6150 Editor::change_region_layering_order (bool from_context_menu)
6152 const samplepos_t position = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context_menu);
6154 if (!clicked_routeview) {
6155 if (layering_order_editor) {
6156 layering_order_editor->hide ();
6161 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
6167 boost::shared_ptr<Playlist> pl = track->playlist();
6173 if (layering_order_editor == 0) {
6174 layering_order_editor = new RegionLayeringOrderEditor (*this);
6177 layering_order_editor->set_context (clicked_routeview->name(), _session, clicked_routeview, pl, position);
6178 layering_order_editor->maybe_present ();
6182 Editor::update_region_layering_order_editor ()
6184 if (layering_order_editor && layering_order_editor->is_visible ()) {
6185 change_region_layering_order (true);
6190 Editor::setup_fade_images ()
6192 _xfade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadein-linear")));
6193 _xfade_in_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadein-symmetric")));
6194 _xfade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadein-fast-cut")));
6195 _xfade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadein-slow-cut")));
6196 _xfade_in_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadein-constant-power")));
6198 _xfade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadeout-linear")));
6199 _xfade_out_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-symmetric")));
6200 _xfade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut")));
6201 _xfade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut")));
6202 _xfade_out_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-constant-power")));
6206 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
6208 Editor::action_menu_item (std::string const & name)
6210 Glib::RefPtr<Action> a = editor_actions->get_action (name);
6213 return *manage (a->create_menu_item ());
6217 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
6219 EventBox* b = manage (new EventBox);
6220 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
6221 Label* l = manage (new Label (name));
6225 _the_notebook.append_page (widget, *b);
6229 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
6231 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
6232 _the_notebook.set_current_page (_the_notebook.page_num (*page));
6235 if (ev->type == GDK_2BUTTON_PRESS) {
6237 /* double-click on a notebook tab shrinks or expands the notebook */
6239 if (_notebook_shrunk) {
6240 if (pre_notebook_shrink_pane_width) {
6241 edit_pane.set_divider (0, *pre_notebook_shrink_pane_width);
6243 _notebook_shrunk = false;
6245 pre_notebook_shrink_pane_width = edit_pane.get_divider();
6247 /* this expands the LHS of the edit pane to cover the notebook
6248 PAGE but leaves the tabs visible.
6250 edit_pane.set_divider (0, edit_pane.get_divider() + page->get_width());
6251 _notebook_shrunk = true;
6259 Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
6261 using namespace Menu_Helpers;
6263 MenuList& items = _control_point_context_menu.items ();
6266 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &Editor::edit_control_point), item)));
6267 items.push_back (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun (*this, &Editor::remove_control_point), item)));
6268 if (!can_remove_control_point (item)) {
6269 items.back().set_sensitive (false);
6272 _control_point_context_menu.popup (event->button.button, event->button.time);
6276 Editor::popup_note_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
6278 using namespace Menu_Helpers;
6280 NoteBase* note = reinterpret_cast<NoteBase*>(item->get_data("notebase"));
6285 /* We need to get the selection here and pass it to the operations, since
6286 popping up the menu will cause a region leave event which clears
6287 entered_regionview. */
6289 MidiRegionView& mrv = note->region_view();
6290 const RegionSelection rs = get_regions_from_selection_and_entered ();
6291 const uint32_t sel_size = mrv.selection_size ();
6293 MenuList& items = _note_context_menu.items();
6297 items.push_back(MenuElem(_("Delete"),
6298 sigc::mem_fun(mrv, &MidiRegionView::delete_selection)));
6301 items.push_back(MenuElem(_("Edit..."),
6302 sigc::bind(sigc::mem_fun(*this, &Editor::edit_notes), &mrv)));
6303 if (sel_size != 1) {
6304 items.back().set_sensitive (false);
6307 items.push_back(MenuElem(_("Transpose..."),
6308 sigc::bind(sigc::mem_fun(*this, &Editor::transpose_regions), rs)));
6311 items.push_back(MenuElem(_("Legatize"),
6312 sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, false)));
6314 items.back().set_sensitive (false);
6317 items.push_back(MenuElem(_("Quantize..."),
6318 sigc::bind(sigc::mem_fun(*this, &Editor::quantize_regions), rs)));
6320 items.push_back(MenuElem(_("Remove Overlap"),
6321 sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, true)));
6323 items.back().set_sensitive (false);
6326 items.push_back(MenuElem(_("Transform..."),
6327 sigc::bind(sigc::mem_fun(*this, &Editor::transform_regions), rs)));
6329 _note_context_menu.popup (event->button.button, event->button.time);
6333 Editor::zoom_vertical_modifier_released()
6335 _stepping_axis_view = 0;
6339 Editor::ui_parameter_changed (string parameter)
6341 if (parameter == "icon-set") {
6342 while (!_cursor_stack.empty()) {
6343 _cursor_stack.pop_back();
6345 _cursors->set_cursor_set (UIConfiguration::instance().get_icon_set());
6346 _cursor_stack.push_back(_cursors->grabber);
6347 edit_pane.set_drag_cursor (*_cursors->expand_left_right);
6348 editor_summary_pane.set_drag_cursor (*_cursors->expand_up_down);
6350 } else if (parameter == "draggable-playhead") {
6351 if (_verbose_cursor) {
6352 playhead_cursor->set_sensitive (UIConfiguration::instance().get_draggable_playhead());
6354 } else if (parameter == "use-note-bars-for-velocity") {
6355 ArdourCanvas::Note::set_show_velocity_bars (UIConfiguration::instance().get_use_note_bars_for_velocity());
6356 _track_canvas->request_redraw (_track_canvas->visible_area());
6357 } else if (parameter == "use-note-color-for-velocity") {
6358 /* handled individually by each MidiRegionView */
6363 Editor::use_own_window (bool and_fill_it)
6365 bool new_window = !own_window();
6367 Gtk::Window* win = Tabbable::use_own_window (and_fill_it);
6369 if (win && new_window) {
6370 win->set_name ("EditorWindow");
6372 ARDOUR_UI::instance()->setup_toplevel_window (*win, _("Editor"), this);
6374 // win->signal_realize().connect (*this, &Editor::on_realize);
6375 win->signal_event().connect (sigc::bind (sigc::ptr_fun (&Keyboard::catch_user_event_for_pre_dialog_focus), win));
6376 win->signal_event().connect (sigc::mem_fun (*this, &Editor::generic_event_handler));
6377 win->set_data ("ardour-bindings", bindings);
6382 DisplaySuspender ds;
6383 contents().show_all ();
6385 /* XXX: this is a bit unfortunate; it would probably
6386 be nicer if we could just call show () above rather
6387 than needing the show_all ()
6390 /* re-hide stuff if necessary */
6391 editor_list_button_toggled ();
6392 parameter_changed ("show-summary");
6393 parameter_changed ("show-group-tabs");
6394 parameter_changed ("show-zoom-tools");
6396 /* now reset all audio_time_axis heights, because widgets might need
6402 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6403 tv = (static_cast<TimeAxisView*>(*i));
6404 tv->reset_height ();
6407 if (current_mixer_strip) {
6408 current_mixer_strip->hide_things ();
6409 current_mixer_strip->parameter_changed ("mixer-element-visibility");