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)
452 /* we are a singleton */
454 PublicEditor::_instance = this;
458 last_event_time.tv_sec = 0;
459 last_event_time.tv_usec = 0;
461 selection_op_history.clear();
464 grid_type_strings = I18N (_grid_type_strings);
465 zoom_focus_strings = I18N (_zoom_focus_strings);
466 edit_mode_strings = I18N (_edit_mode_strings);
467 edit_point_strings = I18N (_edit_point_strings);
468 #ifdef USE_RUBBERBAND
469 rb_opt_strings = I18N (_rb_opt_strings);
473 build_edit_mode_menu();
474 build_zoom_focus_menu();
475 build_track_count_menu();
476 build_grid_type_menu();
477 build_edit_point_menu();
479 location_marker_color = UIConfiguration::instance().color ("location marker");
480 location_range_color = UIConfiguration::instance().color ("location range");
481 location_cd_marker_color = UIConfiguration::instance().color ("location cd marker");
482 location_loop_color = UIConfiguration::instance().color ("location loop");
483 location_punch_color = UIConfiguration::instance().color ("location punch");
485 timebar_height = std::max (12., ceil (15. * UIConfiguration::instance().get_ui_scale()));
487 TimeAxisView::setup_sizes ();
488 ArdourMarker::setup_sizes (timebar_height);
489 TempoCurve::setup_sizes (timebar_height);
491 bbt_label.set_name ("EditorRulerLabel");
492 bbt_label.set_size_request (-1, (int)timebar_height);
493 bbt_label.set_alignment (1.0, 0.5);
494 bbt_label.set_padding (5,0);
496 bbt_label.set_no_show_all();
497 minsec_label.set_name ("EditorRulerLabel");
498 minsec_label.set_size_request (-1, (int)timebar_height);
499 minsec_label.set_alignment (1.0, 0.5);
500 minsec_label.set_padding (5,0);
501 minsec_label.hide ();
502 minsec_label.set_no_show_all();
503 timecode_label.set_name ("EditorRulerLabel");
504 timecode_label.set_size_request (-1, (int)timebar_height);
505 timecode_label.set_alignment (1.0, 0.5);
506 timecode_label.set_padding (5,0);
507 timecode_label.hide ();
508 timecode_label.set_no_show_all();
509 samples_label.set_name ("EditorRulerLabel");
510 samples_label.set_size_request (-1, (int)timebar_height);
511 samples_label.set_alignment (1.0, 0.5);
512 samples_label.set_padding (5,0);
513 samples_label.hide ();
514 samples_label.set_no_show_all();
516 tempo_label.set_name ("EditorRulerLabel");
517 tempo_label.set_size_request (-1, (int)timebar_height);
518 tempo_label.set_alignment (1.0, 0.5);
519 tempo_label.set_padding (5,0);
521 tempo_label.set_no_show_all();
523 meter_label.set_name ("EditorRulerLabel");
524 meter_label.set_size_request (-1, (int)timebar_height);
525 meter_label.set_alignment (1.0, 0.5);
526 meter_label.set_padding (5,0);
528 meter_label.set_no_show_all();
530 if (Profile->get_trx()) {
531 mark_label.set_text (_("Markers"));
533 mark_label.set_name ("EditorRulerLabel");
534 mark_label.set_size_request (-1, (int)timebar_height);
535 mark_label.set_alignment (1.0, 0.5);
536 mark_label.set_padding (5,0);
538 mark_label.set_no_show_all();
540 cd_mark_label.set_name ("EditorRulerLabel");
541 cd_mark_label.set_size_request (-1, (int)timebar_height);
542 cd_mark_label.set_alignment (1.0, 0.5);
543 cd_mark_label.set_padding (5,0);
544 cd_mark_label.hide();
545 cd_mark_label.set_no_show_all();
547 videotl_bar_height = 4;
548 videotl_label.set_name ("EditorRulerLabel");
549 videotl_label.set_size_request (-1, (int)timebar_height * videotl_bar_height);
550 videotl_label.set_alignment (1.0, 0.5);
551 videotl_label.set_padding (5,0);
552 videotl_label.hide();
553 videotl_label.set_no_show_all();
555 range_mark_label.set_name ("EditorRulerLabel");
556 range_mark_label.set_size_request (-1, (int)timebar_height);
557 range_mark_label.set_alignment (1.0, 0.5);
558 range_mark_label.set_padding (5,0);
559 range_mark_label.hide();
560 range_mark_label.set_no_show_all();
562 transport_mark_label.set_name ("EditorRulerLabel");
563 transport_mark_label.set_size_request (-1, (int)timebar_height);
564 transport_mark_label.set_alignment (1.0, 0.5);
565 transport_mark_label.set_padding (5,0);
566 transport_mark_label.hide();
567 transport_mark_label.set_no_show_all();
569 initialize_canvas ();
571 CairoWidget::set_focus_handler (sigc::mem_fun (ARDOUR_UI::instance(), &ARDOUR_UI::reset_focus));
573 _summary = new EditorSummary (this);
575 selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
576 selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
578 editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
580 selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
581 selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
583 edit_controls_vbox.set_spacing (0);
584 vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
585 _track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
587 HBox* h = manage (new HBox);
588 _group_tabs = new EditorGroupTabs (this);
589 if (!ARDOUR::Profile->get_trx()) {
590 h->pack_start (*_group_tabs, PACK_SHRINK);
592 h->pack_start (edit_controls_vbox);
593 controls_layout.add (*h);
595 controls_layout.set_name ("EditControlsBase");
596 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK|Gdk::SCROLL_MASK);
597 controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
598 controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
600 _cursors = new MouseCursors;
601 _cursors->set_cursor_set (UIConfiguration::instance().get_icon_set());
602 cerr << "Set cursor set to " << UIConfiguration::instance().get_icon_set() << endl;
604 /* Push default cursor to ever-present bottom of cursor stack. */
605 push_canvas_cursor(_cursors->grabber);
607 ArdourCanvas::GtkCanvas* time_pad = manage (new ArdourCanvas::GtkCanvas ());
609 ArdourCanvas::Line* pad_line_1 = new ArdourCanvas::Line (time_pad->root());
610 pad_line_1->set (ArdourCanvas::Duple (0.0, 1.0), ArdourCanvas::Duple (100.0, 1.0));
611 pad_line_1->set_outline_color (0xFF0000FF);
617 edit_packer.set_col_spacings (0);
618 edit_packer.set_row_spacings (0);
619 edit_packer.set_homogeneous (false);
620 edit_packer.set_border_width (0);
621 edit_packer.set_name ("EditorWindow");
623 time_bars_event_box.add (time_bars_vbox);
624 time_bars_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
625 time_bars_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
627 ArdourWidgets::ArdourDropShadow *axis_view_shadow = manage (new (ArdourWidgets::ArdourDropShadow));
628 axis_view_shadow->set_size_request (4, -1);
629 axis_view_shadow->set_name("EditorWindow");
630 axis_view_shadow->show();
632 edit_packer.attach (*axis_view_shadow, 0, 1, 0, 2, FILL, FILL|EXPAND, 0, 0);
634 /* labels for the time bars */
635 edit_packer.attach (time_bars_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
637 edit_packer.attach (controls_layout, 1, 2, 1, 2, FILL, FILL|EXPAND, 0, 0);
639 edit_packer.attach (*_track_canvas_viewport, 2, 3, 0, 2, FILL|EXPAND, FILL|EXPAND, 0, 0);
641 bottom_hbox.set_border_width (2);
642 bottom_hbox.set_spacing (3);
644 PresentationInfo::Change.connect (*this, MISSING_INVALIDATOR, boost::bind (&Editor::presentation_info_changed, this, _1), gui_context());
646 _route_groups = new EditorRouteGroups (this);
647 _routes = new EditorRoutes (this);
648 _regions = new EditorRegions (this);
649 _snapshots = new EditorSnapshots (this);
650 _locations = new EditorLocations (this);
651 _time_info_box = new TimeInfoBox ("EditorTimeInfo", true);
653 /* these are static location signals */
655 Location::start_changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context());
656 Location::end_changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context());
657 Location::changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context());
659 add_notebook_page (_("Regions"), _regions->widget ());
660 add_notebook_page (_("Tracks & Busses"), _routes->widget ());
661 add_notebook_page (_("Snapshots"), _snapshots->widget ());
662 add_notebook_page (_("Track & Bus Groups"), _route_groups->widget ());
663 add_notebook_page (_("Ranges & Marks"), _locations->widget ());
665 _the_notebook.set_show_tabs (true);
666 _the_notebook.set_scrollable (true);
667 _the_notebook.popup_disable ();
668 _the_notebook.set_tab_pos (Gtk::POS_RIGHT);
669 _the_notebook.show_all ();
671 _notebook_shrunk = false;
674 /* Pick up some settings we need to cache, early */
676 XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
679 settings->get_property ("notebook-shrunk", _notebook_shrunk);
682 editor_summary_pane.set_check_divider_position (true);
683 editor_summary_pane.add (edit_packer);
685 Button* summary_arrow_left = manage (new Button);
686 summary_arrow_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
687 summary_arrow_left->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), LEFT)));
688 summary_arrow_left->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
690 Button* summary_arrow_right = manage (new Button);
691 summary_arrow_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
692 summary_arrow_right->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), RIGHT)));
693 summary_arrow_right->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
695 VBox* summary_arrows_left = manage (new VBox);
696 summary_arrows_left->pack_start (*summary_arrow_left);
698 VBox* summary_arrows_right = manage (new VBox);
699 summary_arrows_right->pack_start (*summary_arrow_right);
701 Frame* summary_frame = manage (new Frame);
702 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
704 summary_frame->add (*_summary);
705 summary_frame->show ();
707 _summary_hbox.pack_start (*summary_arrows_left, false, false);
708 _summary_hbox.pack_start (*summary_frame, true, true);
709 _summary_hbox.pack_start (*summary_arrows_right, false, false);
711 if (!ARDOUR::Profile->get_trx()) {
712 editor_summary_pane.add (_summary_hbox);
715 edit_pane.set_check_divider_position (true);
716 edit_pane.add (editor_summary_pane);
717 if (!ARDOUR::Profile->get_trx()) {
718 _editor_list_vbox.pack_start (*_time_info_box, false, false, 0);
719 _editor_list_vbox.pack_start (_the_notebook);
720 edit_pane.add (_editor_list_vbox);
721 edit_pane.set_child_minsize (_editor_list_vbox, 30); /* rough guess at width of notebook tabs */
724 edit_pane.set_drag_cursor (*_cursors->expand_left_right);
725 editor_summary_pane.set_drag_cursor (*_cursors->expand_up_down);
728 if (!settings || !settings->get_property ("edit-horizontal-pane-pos", fract) || fract > 1.0) {
729 /* initial allocation is 90% to canvas, 10% to notebook */
732 edit_pane.set_divider (0, fract);
734 if (!settings || !settings->get_property ("edit-vertical-pane-pos", fract) || fract > 1.0) {
735 /* initial allocation is 90% to canvas, 10% to summary */
738 editor_summary_pane.set_divider (0, fract);
740 global_vpacker.set_spacing (0);
741 global_vpacker.set_border_width (0);
743 /* the next three EventBoxes provide the ability for their child widgets to have a background color. That is all. */
745 Gtk::EventBox* ebox = manage (new Gtk::EventBox); // a themeable box
746 ebox->set_name("EditorWindow");
747 ebox->add (ebox_hpacker);
749 Gtk::EventBox* epane_box = manage (new EventBoxExt); // a themeable box
750 epane_box->set_name("EditorWindow");
751 epane_box->add (edit_pane);
753 Gtk::EventBox* epane_box2 = manage (new EventBoxExt); // a themeable box
754 epane_box2->set_name("EditorWindow");
755 epane_box2->add (global_vpacker);
757 ArdourWidgets::ArdourDropShadow *toolbar_shadow = manage (new (ArdourWidgets::ArdourDropShadow));
758 toolbar_shadow->set_size_request (-1, 4);
759 toolbar_shadow->set_mode(ArdourWidgets::ArdourDropShadow::DropShadowBoth);
760 toolbar_shadow->set_name("EditorWindow");
761 toolbar_shadow->show();
763 global_vpacker.pack_start (*toolbar_shadow, false, false);
764 global_vpacker.pack_start (*ebox, false, false);
765 global_vpacker.pack_start (*epane_box, true, true);
766 global_hpacker.pack_start (*epane_box2, true, true);
768 /* need to show the "contents" widget so that notebook will show if tab is switched to
771 global_hpacker.show ();
775 /* register actions now so that set_state() can find them and set toggles/checks etc */
782 _playlist_selector = new PlaylistSelector();
783 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
785 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), boost::bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
789 nudge_forward_button.set_name ("nudge button");
790 nudge_forward_button.set_icon(ArdourIcon::NudgeRight);
792 nudge_backward_button.set_name ("nudge button");
793 nudge_backward_button.set_icon(ArdourIcon::NudgeLeft);
795 fade_context_menu.set_name ("ArdourContextMenu");
797 Gtkmm2ext::Keyboard::the_keyboard().ZoomVerticalModifierReleased.connect (sigc::mem_fun (*this, &Editor::zoom_vertical_modifier_released));
799 /* allow external control surfaces/protocols to do various things */
801 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
802 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
803 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
804 ControlProtocol::Undo.connect (*this, invalidator (*this), boost::bind (&Editor::undo, this, true), gui_context());
805 ControlProtocol::Redo.connect (*this, invalidator (*this), boost::bind (&Editor::redo, this, true), gui_context());
806 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), boost::bind (&Editor::control_scroll, this, _1), gui_context());
807 ControlProtocol::StepTracksUp.connect (*this, invalidator (*this), boost::bind (&Editor::control_step_tracks_up, this), gui_context());
808 ControlProtocol::StepTracksDown.connect (*this, invalidator (*this), boost::bind (&Editor::control_step_tracks_down, this), gui_context());
809 ControlProtocol::GotoView.connect (*this, invalidator (*this), boost::bind (&Editor::control_view, this, _1), gui_context());
810 ControlProtocol::CloseDialog.connect (*this, invalidator (*this), Keyboard::close_current_dialog, gui_context());
811 ControlProtocol::VerticalZoomInAll.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_in_all, this), gui_context());
812 ControlProtocol::VerticalZoomOutAll.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_out_all, this), gui_context());
813 ControlProtocol::VerticalZoomInSelected.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_in_selected, this), gui_context());
814 ControlProtocol::VerticalZoomOutSelected.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_out_selected, this), gui_context());
816 ControlProtocol::AddStripableToSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Add), gui_context());
817 ControlProtocol::RemoveStripableFromSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Toggle), gui_context());
818 ControlProtocol::SetStripableSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Set), gui_context());
819 ControlProtocol::ToggleStripableSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Toggle), gui_context());
820 ControlProtocol::ClearStripableSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_unselect, this), gui_context());
822 BasicUI::AccessAction.connect (*this, invalidator (*this), boost::bind (&Editor::access_action, this, _1, _2), gui_context());
826 ARDOUR_UI::instance()->Escape.connect (*this, invalidator (*this), boost::bind (&Editor::escape, this), gui_context());
828 /* problematic: has to return a value and thus cannot be x-thread */
830 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
832 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&Editor::parameter_changed, this, _1), gui_context());
833 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &Editor::ui_parameter_changed));
835 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
837 _ignore_region_action = false;
838 _last_region_menu_was_main = false;
839 _popup_region_menu_item = 0;
841 _show_marker_lines = false;
843 /* Button bindings */
845 button_bindings = new Bindings ("editor-mouse");
847 XMLNode* node = button_settings();
849 for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
850 button_bindings->load_operation (**i);
856 /* grab current parameter state */
857 boost::function<void (string)> pc (boost::bind (&Editor::ui_parameter_changed, this, _1));
858 UIConfiguration::instance().map_parameters (pc);
860 setup_fade_images ();
862 set_grid_to (GridTypeNone);
867 delete button_bindings;
869 delete _route_groups;
870 delete _track_canvas_viewport;
873 delete _verbose_cursor;
874 delete quantize_dialog;
880 delete _playlist_selector;
881 delete _time_info_box;
886 LuaInstance::destroy_instance ();
888 for (list<XMLNode *>::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) {
891 for (std::map<ARDOUR::FadeShape, Gtk::Image*>::const_iterator i = _xfade_in_images.begin(); i != _xfade_in_images.end (); ++i) {
894 for (std::map<ARDOUR::FadeShape, Gtk::Image*>::const_iterator i = _xfade_out_images.begin(); i != _xfade_out_images.end (); ++i) {
900 Editor::button_settings () const
902 XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
903 XMLNode* node = find_named_node (*settings, X_("Buttons"));
906 node = new XMLNode (X_("Buttons"));
913 Editor::get_smart_mode () const
915 return ((current_mouse_mode() == MouseObject) && smart_mode_action->get_active());
919 Editor::catch_vanishing_regionview (RegionView *rv)
921 /* note: the selection will take care of the vanishing
922 audioregionview by itself.
925 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
929 if (clicked_regionview == rv) {
930 clicked_regionview = 0;
933 if (entered_regionview == rv) {
934 set_entered_regionview (0);
937 if (!_all_region_actions_sensitized) {
938 sensitize_all_region_actions (true);
943 Editor::set_entered_regionview (RegionView* rv)
945 if (rv == entered_regionview) {
949 if (entered_regionview) {
950 entered_regionview->exited ();
953 entered_regionview = rv;
955 if (entered_regionview != 0) {
956 entered_regionview->entered ();
959 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
960 /* This RegionView entry might have changed what region actions
961 are allowed, so sensitize them all in case a key is pressed.
963 sensitize_all_region_actions (true);
968 Editor::set_entered_track (TimeAxisView* tav)
971 entered_track->exited ();
977 entered_track->entered ();
982 Editor::instant_save ()
984 if (!constructed || !_session || no_save_instant) {
988 _session->add_instant_xml(get_state());
992 Editor::control_vertical_zoom_in_all ()
994 tav_zoom_smooth (false, true);
998 Editor::control_vertical_zoom_out_all ()
1000 tav_zoom_smooth (true, true);
1004 Editor::control_vertical_zoom_in_selected ()
1006 tav_zoom_smooth (false, false);
1010 Editor::control_vertical_zoom_out_selected ()
1012 tav_zoom_smooth (true, false);
1016 Editor::control_view (uint32_t view)
1018 goto_visual_state (view);
1022 Editor::control_unselect ()
1024 selection->clear_tracks ();
1028 Editor::control_select (boost::shared_ptr<Stripable> s, Selection::Operation op)
1030 TimeAxisView* tav = time_axis_view_from_stripable (s);
1034 case Selection::Add:
1035 selection->add (tav);
1037 case Selection::Toggle:
1038 selection->toggle (tav);
1040 case Selection::Extend:
1042 case Selection::Set:
1043 selection->set (tav);
1047 selection->clear_tracks ();
1052 Editor::control_step_tracks_up ()
1054 scroll_tracks_up_line ();
1058 Editor::control_step_tracks_down ()
1060 scroll_tracks_down_line ();
1064 Editor::control_scroll (float fraction)
1066 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
1072 double step = fraction * current_page_samples();
1075 _control_scroll_target is an optional<T>
1077 it acts like a pointer to an samplepos_t, with
1078 a operator conversion to boolean to check
1079 that it has a value could possibly use
1080 playhead_cursor->current_sample to store the
1081 value and a boolean in the class to know
1082 when it's out of date
1085 if (!_control_scroll_target) {
1086 _control_scroll_target = _session->transport_sample();
1087 _dragging_playhead = true;
1090 if ((fraction < 0.0f) && (*_control_scroll_target <= (samplepos_t) fabs(step))) {
1091 *_control_scroll_target = 0;
1092 } else if ((fraction > 0.0f) && (max_samplepos - *_control_scroll_target < step)) {
1093 *_control_scroll_target = max_samplepos - (current_page_samples()*2); // allow room for slop in where the PH is on the screen
1095 *_control_scroll_target += (samplepos_t) trunc (step);
1098 /* move visuals, we'll catch up with it later */
1100 playhead_cursor->set_position (*_control_scroll_target);
1101 UpdateAllTransportClocks (*_control_scroll_target);
1103 if (*_control_scroll_target > (current_page_samples() / 2)) {
1104 /* try to center PH in window */
1105 reset_x_origin (*_control_scroll_target - (current_page_samples()/2));
1111 Now we do a timeout to actually bring the session to the right place
1112 according to the playhead. This is to avoid reading disk buffers on every
1113 call to control_scroll, which is driven by ScrollTimeline and therefore
1114 probably by a control surface wheel which can generate lots of events.
1116 /* cancel the existing timeout */
1118 control_scroll_connection.disconnect ();
1120 /* add the next timeout */
1122 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
1126 Editor::deferred_control_scroll (samplepos_t /*target*/)
1128 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
1129 /* reset for next stream */
1130 _control_scroll_target = boost::none;
1131 _dragging_playhead = false;
1136 Editor::access_action (const std::string& action_group, const std::string& action_item)
1142 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
1145 act = ActionManager::get_action (action_group.c_str(), action_item.c_str());
1153 Editor::set_toggleaction (const std::string& action_group, const std::string& action_item, bool s)
1155 ActionManager::set_toggleaction_state (action_group.c_str(), action_item.c_str(), s);
1159 Editor::on_realize ()
1163 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
1164 start_lock_event_timing ();
1169 Editor::start_lock_event_timing ()
1171 /* check if we should lock the GUI every 30 seconds */
1173 Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::lock_timeout_callback), 30 * 1000);
1177 Editor::generic_event_handler (GdkEvent* ev)
1180 case GDK_BUTTON_PRESS:
1181 case GDK_BUTTON_RELEASE:
1182 case GDK_MOTION_NOTIFY:
1184 case GDK_KEY_RELEASE:
1185 if (contents().is_mapped()) {
1186 gettimeofday (&last_event_time, 0);
1190 case GDK_LEAVE_NOTIFY:
1191 switch (ev->crossing.detail) {
1192 case GDK_NOTIFY_UNKNOWN:
1193 case GDK_NOTIFY_INFERIOR:
1194 case GDK_NOTIFY_ANCESTOR:
1196 case GDK_NOTIFY_VIRTUAL:
1197 case GDK_NOTIFY_NONLINEAR:
1198 case GDK_NOTIFY_NONLINEAR_VIRTUAL:
1199 /* leaving window, so reset focus, thus ending any and
1200 all text entry operations.
1202 ARDOUR_UI::instance()->reset_focus (&contents());
1215 Editor::lock_timeout_callback ()
1217 struct timeval now, delta;
1219 gettimeofday (&now, 0);
1221 timersub (&now, &last_event_time, &delta);
1223 if (delta.tv_sec > (time_t) UIConfiguration::instance().get_lock_gui_after_seconds()) {
1225 /* don't call again. Returning false will effectively
1226 disconnect us from the timer callback.
1228 unlock() will call start_lock_event_timing() to get things
1238 Editor::map_position_change (samplepos_t sample)
1240 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, sample)
1242 if (_session == 0) {
1246 if (_follow_playhead) {
1247 center_screen (sample);
1250 playhead_cursor->set_position (sample);
1254 Editor::center_screen (samplepos_t sample)
1256 samplecnt_t const page = _visible_canvas_width * samples_per_pixel;
1258 /* if we're off the page, then scroll.
1261 if (sample < _leftmost_sample || sample >= _leftmost_sample + page) {
1262 center_screen_internal (sample, page);
1267 Editor::center_screen_internal (samplepos_t sample, float page)
1271 if (sample > page) {
1272 sample -= (samplepos_t) page;
1277 reset_x_origin (sample);
1282 Editor::update_title ()
1284 ENSURE_GUI_THREAD (*this, &Editor::update_title);
1286 if (!own_window()) {
1291 bool dirty = _session->dirty();
1293 string session_name;
1295 if (_session->snap_name() != _session->name()) {
1296 session_name = _session->snap_name();
1298 session_name = _session->name();
1302 session_name = "*" + session_name;
1305 WindowTitle title(session_name);
1306 title += S_("Window|Editor");
1307 title += Glib::get_application_name();
1308 own_window()->set_title (title.get_string());
1310 /* ::session_going_away() will have taken care of it */
1315 Editor::set_session (Session *t)
1317 SessionHandlePtr::set_session (t);
1323 /* initialize _leftmost_sample to the extents of the session
1324 * this prevents a bogus setting of leftmost = "0" if the summary view asks for the leftmost sample
1325 * before the visible state has been loaded from instant.xml */
1326 _leftmost_sample = session_gui_extents().first;
1328 _playlist_selector->set_session (_session);
1329 nudge_clock->set_session (_session);
1330 _summary->set_session (_session);
1331 _group_tabs->set_session (_session);
1332 _route_groups->set_session (_session);
1333 _regions->set_session (_session);
1334 _snapshots->set_session (_session);
1335 _routes->set_session (_session);
1336 _locations->set_session (_session);
1337 _time_info_box->set_session (_session);
1339 if (rhythm_ferret) {
1340 rhythm_ferret->set_session (_session);
1343 if (analysis_window) {
1344 analysis_window->set_session (_session);
1348 sfbrowser->set_session (_session);
1351 compute_fixed_ruler_scale ();
1353 /* Make sure we have auto loop and auto punch ranges */
1355 Location* loc = _session->locations()->auto_loop_location();
1357 loc->set_name (_("Loop"));
1360 loc = _session->locations()->auto_punch_location();
1363 loc->set_name (_("Punch"));
1366 refresh_location_display ();
1368 /* This must happen after refresh_location_display(), as (amongst other things) we restore
1369 * the selected Marker; this needs the LocationMarker list to be available.
1371 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1372 set_state (*node, Stateful::loading_state_version);
1374 /* catch up on selection state, etc. */
1377 sc.add (Properties::selected);
1378 presentation_info_changed (sc);
1380 /* catch up with the playhead */
1382 _session->request_locate (playhead_cursor->current_sample ());
1383 _pending_initial_locate = true;
1387 /* These signals can all be emitted by a non-GUI thread. Therefore the
1388 handlers for them must not attempt to directly interact with the GUI,
1389 but use PBD::Signal<T>::connect() which accepts an event loop
1390 ("context") where the handler will be asked to run.
1393 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::step_edit_status_change, this, _1), gui_context());
1394 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1395 _session->TransportLooped.connect (_session_connections, invalidator (*this), boost::bind (&Editor::transport_looped, this), gui_context());
1396 _session->PositionChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_position_change, this, _1), gui_context());
1397 _session->vca_manager().VCAAdded.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_vcas, this, _1), gui_context());
1398 _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_routes, this, _1), gui_context());
1399 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1400 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::tempo_map_changed, this, _1), gui_context());
1401 _session->tempo_map().MetricPositionChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::tempometric_position_changed, this, _1), gui_context());
1402 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1403 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::parameter_changed, this, _1), gui_context());
1404 _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Editor::session_state_saved, this, _1), gui_context());
1405 _session->locations()->added.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_new_location, this, _1), gui_context());
1406 _session->locations()->removed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::location_gone, this, _1), gui_context());
1407 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1408 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1410 playhead_cursor->track_canvas_item().reparent ((ArdourCanvas::Item*) get_cursor_scroll_group());
1411 playhead_cursor->show ();
1413 snapped_cursor->track_canvas_item().reparent ((ArdourCanvas::Item*) get_cursor_scroll_group());
1414 snapped_cursor->set_color (UIConfiguration::instance().color ("edit point"));
1415 snapped_cursor->show ();
1417 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1418 Config->map_parameters (pc);
1419 _session->config.map_parameters (pc);
1421 restore_ruler_visibility ();
1422 //tempo_map_changed (PropertyChange (0));
1423 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1425 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1426 (static_cast<TimeAxisView*>(*i))->set_samples_per_pixel (samples_per_pixel);
1429 super_rapid_screen_update_connection = Timers::super_rapid_connect (
1430 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1433 /* register for undo history */
1434 _session->register_with_memento_command_factory(id(), this);
1435 _session->register_with_memento_command_factory(_selection_memento->id(), _selection_memento);
1437 LuaInstance::instance()->set_session(_session);
1439 start_updating_meters ();
1443 Editor::fill_xfade_menu (Menu_Helpers::MenuList& items, bool start)
1445 using namespace Menu_Helpers;
1447 void (Editor::*emf)(FadeShape);
1448 std::map<ARDOUR::FadeShape,Gtk::Image*>* images;
1451 images = &_xfade_in_images;
1452 emf = &Editor::set_fade_in_shape;
1454 images = &_xfade_out_images;
1455 emf = &Editor::set_fade_out_shape;
1460 _("Linear (for highly correlated material)"),
1461 *(*images)[FadeLinear],
1462 sigc::bind (sigc::mem_fun (*this, emf), FadeLinear)
1466 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1470 _("Constant power"),
1471 *(*images)[FadeConstantPower],
1472 sigc::bind (sigc::mem_fun (*this, emf), FadeConstantPower)
1475 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1480 *(*images)[FadeSymmetric],
1481 sigc::bind (sigc::mem_fun (*this, emf), FadeSymmetric)
1485 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1490 *(*images)[FadeSlow],
1491 sigc::bind (sigc::mem_fun (*this, emf), FadeSlow)
1494 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1499 *(*images)[FadeFast],
1500 sigc::bind (sigc::mem_fun (*this, emf), FadeFast)
1503 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1506 /** Pop up a context menu for when the user clicks on a start crossfade */
1508 Editor::popup_xfade_in_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType /*item_type*/)
1510 using namespace Menu_Helpers;
1511 AudioRegionView* arv = dynamic_cast<AudioRegionView*> ((RegionView*)item->get_data ("regionview"));
1516 MenuList& items (xfade_in_context_menu.items());
1519 if (arv->audio_region()->fade_in_active()) {
1520 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1522 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1525 items.push_back (SeparatorElem());
1526 fill_xfade_menu (items, true);
1528 xfade_in_context_menu.popup (button, time);
1531 /** Pop up a context menu for when the user clicks on an end crossfade */
1533 Editor::popup_xfade_out_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType /*item_type*/)
1535 using namespace Menu_Helpers;
1536 AudioRegionView* arv = dynamic_cast<AudioRegionView*> ((RegionView*)item->get_data ("regionview"));
1541 MenuList& items (xfade_out_context_menu.items());
1544 if (arv->audio_region()->fade_out_active()) {
1545 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1547 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1550 items.push_back (SeparatorElem());
1551 fill_xfade_menu (items, false);
1553 xfade_out_context_menu.popup (button, time);
1557 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1559 using namespace Menu_Helpers;
1560 Menu* (Editor::*build_menu_function)();
1563 switch (item_type) {
1565 case RegionViewName:
1566 case RegionViewNameHighlight:
1567 case LeftFrameHandle:
1568 case RightFrameHandle:
1569 if (with_selection) {
1570 build_menu_function = &Editor::build_track_selection_context_menu;
1572 build_menu_function = &Editor::build_track_region_context_menu;
1577 if (with_selection) {
1578 build_menu_function = &Editor::build_track_selection_context_menu;
1580 build_menu_function = &Editor::build_track_context_menu;
1585 if (clicked_routeview->track()) {
1586 build_menu_function = &Editor::build_track_context_menu;
1588 build_menu_function = &Editor::build_track_bus_context_menu;
1593 /* probably shouldn't happen but if it does, we don't care */
1597 menu = (this->*build_menu_function)();
1598 menu->set_name ("ArdourContextMenu");
1600 /* now handle specific situations */
1602 switch (item_type) {
1604 case RegionViewName:
1605 case RegionViewNameHighlight:
1606 case LeftFrameHandle:
1607 case RightFrameHandle:
1608 if (!with_selection) {
1609 if (region_edit_menu_split_item) {
1610 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1611 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1613 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1616 if (region_edit_menu_split_multichannel_item) {
1617 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1618 region_edit_menu_split_multichannel_item->set_sensitive (true);
1620 region_edit_menu_split_multichannel_item->set_sensitive (false);
1633 /* probably shouldn't happen but if it does, we don't care */
1637 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1639 /* Bounce to disk */
1641 using namespace Menu_Helpers;
1642 MenuList& edit_items = menu->items();
1644 edit_items.push_back (SeparatorElem());
1646 switch (clicked_routeview->audio_track()->freeze_state()) {
1647 case AudioTrack::NoFreeze:
1648 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1651 case AudioTrack::Frozen:
1652 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1655 case AudioTrack::UnFrozen:
1656 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1662 if (item_type == StreamItem && clicked_routeview) {
1663 clicked_routeview->build_underlay_menu(menu);
1666 /* When the region menu is opened, we setup the actions so that they look right
1669 sensitize_the_right_region_actions (false);
1670 _last_region_menu_was_main = false;
1672 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1673 menu->popup (button, time);
1677 Editor::build_track_context_menu ()
1679 using namespace Menu_Helpers;
1681 MenuList& edit_items = track_context_menu.items();
1684 add_dstream_context_items (edit_items);
1685 return &track_context_menu;
1689 Editor::build_track_bus_context_menu ()
1691 using namespace Menu_Helpers;
1693 MenuList& edit_items = track_context_menu.items();
1696 add_bus_context_items (edit_items);
1697 return &track_context_menu;
1701 Editor::build_track_region_context_menu ()
1703 using namespace Menu_Helpers;
1704 MenuList& edit_items = track_region_context_menu.items();
1707 /* we've just cleared the track region context menu, so the menu that these
1708 two items were on will have disappeared; stop them dangling.
1710 region_edit_menu_split_item = 0;
1711 region_edit_menu_split_multichannel_item = 0;
1713 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1716 boost::shared_ptr<Track> tr;
1717 boost::shared_ptr<Playlist> pl;
1719 if ((tr = rtv->track())) {
1720 add_region_context_items (edit_items, tr);
1724 add_dstream_context_items (edit_items);
1726 return &track_region_context_menu;
1730 Editor::loudness_analyze_region_selection ()
1735 Selection& s (PublicEditor::instance ().get_selection ());
1736 RegionSelection ars = s.regions;
1737 ARDOUR::AnalysisGraph ag (_session);
1738 samplecnt_t total_work = 0;
1740 for (RegionSelection::iterator j = ars.begin (); j != ars.end (); ++j) {
1741 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*j);
1745 if (!boost::dynamic_pointer_cast<AudioRegion> (arv->region ())) {
1748 assert (dynamic_cast<RouteTimeAxisView *> (&arv->get_time_axis_view ()));
1749 total_work += arv->region ()->length ();
1752 SimpleProgressDialog spd (_("Region Loudness Analysis"), sigc::mem_fun (ag, &AnalysisGraph::cancel));
1754 ag.set_total_samples (total_work);
1755 ag.Progress.connect_same_thread (c, boost::bind (&SimpleProgressDialog::update_progress, &spd, _1, _2));
1758 for (RegionSelection::iterator j = ars.begin (); j != ars.end (); ++j) {
1759 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*j);
1763 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (arv->region ());
1767 ag.analyze_region (ar);
1770 if (!ag.canceled ()) {
1771 ExportReport er (_("Audio Report/Analysis"), ag.results ());
1777 Editor::loudness_analyze_range_selection ()
1782 Selection& s (PublicEditor::instance ().get_selection ());
1783 TimeSelection ts = s.time;
1784 ARDOUR::AnalysisGraph ag (_session);
1785 samplecnt_t total_work = 0;
1787 for (TrackSelection::iterator i = s.tracks.begin (); i != s.tracks.end (); ++i) {
1788 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist> ((*i)->playlist ());
1792 RouteUI *rui = dynamic_cast<RouteUI *> (*i);
1796 for (std::list<AudioRange>::iterator j = ts.begin (); j != ts.end (); ++j) {
1797 total_work += j->length ();
1801 SimpleProgressDialog spd (_("Range Loudness Analysis"), sigc::mem_fun (ag, &AnalysisGraph::cancel));
1803 ag.set_total_samples (total_work);
1804 ag.Progress.connect_same_thread (c, boost::bind (&SimpleProgressDialog::update_progress, &spd, _1, _2));
1807 for (TrackSelection::iterator i = s.tracks.begin (); i != s.tracks.end (); ++i) {
1808 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist> ((*i)->playlist ());
1812 RouteUI *rui = dynamic_cast<RouteUI *> (*i);
1816 ag.analyze_range (rui->route (), pl, ts);
1819 if (!ag.canceled ()) {
1820 ExportReport er (_("Audio Report/Analysis"), ag.results ());
1826 Editor::spectral_analyze_region_selection ()
1828 if (analysis_window == 0) {
1829 analysis_window = new AnalysisWindow();
1832 analysis_window->set_session(_session);
1834 analysis_window->show_all();
1837 analysis_window->set_regionmode();
1838 analysis_window->analyze();
1840 analysis_window->present();
1844 Editor::spectral_analyze_range_selection()
1846 if (analysis_window == 0) {
1847 analysis_window = new AnalysisWindow();
1850 analysis_window->set_session(_session);
1852 analysis_window->show_all();
1855 analysis_window->set_rangemode();
1856 analysis_window->analyze();
1858 analysis_window->present();
1862 Editor::build_track_selection_context_menu ()
1864 using namespace Menu_Helpers;
1865 MenuList& edit_items = track_selection_context_menu.items();
1866 edit_items.clear ();
1868 add_selection_context_items (edit_items);
1869 // edit_items.push_back (SeparatorElem());
1870 // add_dstream_context_items (edit_items);
1872 return &track_selection_context_menu;
1876 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr<Track> track)
1878 using namespace Menu_Helpers;
1880 /* OK, stick the region submenu at the top of the list, and then add
1884 RegionSelection rs = get_regions_from_selection_and_entered ();
1886 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1888 if (_popup_region_menu_item == 0) {
1889 _popup_region_menu_item = new MenuItem (menu_item_name, false);
1890 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1891 _popup_region_menu_item->show ();
1893 _popup_region_menu_item->set_label (menu_item_name);
1896 /* No layering allowed in later is higher layering model */
1897 RefPtr<Action> act = ActionManager::get_action (X_("EditorMenu"), X_("RegionMenuLayering"));
1898 if (act && Config->get_layer_model() == LaterHigher) {
1899 act->set_sensitive (false);
1901 act->set_sensitive (true);
1904 const samplepos_t position = get_preferred_edit_position (EDIT_IGNORE_NONE, true);
1906 edit_items.push_back (*_popup_region_menu_item);
1907 if (Config->get_layer_model() == Manual && track->playlist()->count_regions_at (position) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1908 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region-context-menu")->create_menu_item ()));
1910 edit_items.push_back (SeparatorElem());
1913 /** Add context menu items relevant to selection ranges.
1914 * @param edit_items List to add the items to.
1917 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1919 using namespace Menu_Helpers;
1921 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1922 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1924 edit_items.push_back (SeparatorElem());
1925 edit_items.push_back (MenuElem (_("Zoom to Range"), sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_selection), Horizontal)));
1927 edit_items.push_back (SeparatorElem());
1928 edit_items.push_back (MenuElem (_("Loudness Analysis"), sigc::mem_fun(*this, &Editor::loudness_analyze_range_selection)));
1929 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::spectral_analyze_range_selection)));
1931 edit_items.push_back (SeparatorElem());
1933 edit_items.push_back (
1935 _("Move Range Start to Previous Region Boundary"),
1936 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, false)
1940 edit_items.push_back (
1942 _("Move Range Start to Next Region Boundary"),
1943 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, true)
1947 edit_items.push_back (
1949 _("Move Range End to Previous Region Boundary"),
1950 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, false)
1954 edit_items.push_back (
1956 _("Move Range End to Next Region Boundary"),
1957 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, true)
1961 edit_items.push_back (SeparatorElem());
1962 edit_items.push_back (MenuElem (_("Separate"), mem_fun(*this, &Editor::separate_region_from_selection)));
1963 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1965 edit_items.push_back (SeparatorElem());
1966 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1968 edit_items.push_back (SeparatorElem());
1969 edit_items.push_back (MenuElem (_("Set Loop from Selection"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1970 edit_items.push_back (MenuElem (_("Set Punch from Selection"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1971 edit_items.push_back (MenuElem (_("Set Session Start/End from Selection"), sigc::mem_fun(*this, &Editor::set_session_extents_from_selection)));
1973 edit_items.push_back (SeparatorElem());
1974 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1976 edit_items.push_back (SeparatorElem());
1977 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1978 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_range), false)));
1980 edit_items.push_back (SeparatorElem());
1981 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1982 edit_items.push_back (MenuElem (_("Consolidate Range with Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1983 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1984 edit_items.push_back (MenuElem (_("Bounce Range to Region List with Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1985 edit_items.push_back (MenuElem (_("Export Range..."), sigc::mem_fun(*this, &Editor::export_selection)));
1986 if (ARDOUR_UI::instance()->video_timeline->get_duration() > 0) {
1987 edit_items.push_back (MenuElem (_("Export Video Range..."), sigc::bind (sigc::mem_fun(*(ARDOUR_UI::instance()), &ARDOUR_UI::export_video), true)));
1993 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1995 using namespace Menu_Helpers;
1999 Menu *play_menu = manage (new Menu);
2000 MenuList& play_items = play_menu->items();
2001 play_menu->set_name ("ArdourContextMenu");
2003 play_items.push_back (MenuElem (_("Play from Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
2004 play_items.push_back (MenuElem (_("Play from Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
2005 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
2006 play_items.push_back (SeparatorElem());
2007 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
2009 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2013 Menu *select_menu = manage (new Menu);
2014 MenuList& select_items = select_menu->items();
2015 select_menu->set_name ("ArdourContextMenu");
2017 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2018 select_items.push_back (MenuElem (_("Select All Objects"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_objects), Selection::Set)));
2019 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2020 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2021 select_items.push_back (SeparatorElem());
2022 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
2023 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
2024 select_items.push_back (MenuElem (_("Set Range to Selected Regions"), sigc::mem_fun(*this, &Editor::set_selection_from_region)));
2025 select_items.push_back (SeparatorElem());
2026 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true, true)));
2027 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false, true)));
2028 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2029 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2030 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
2031 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
2032 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
2034 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2038 Menu *cutnpaste_menu = manage (new Menu);
2039 MenuList& cutnpaste_items = cutnpaste_menu->items();
2040 cutnpaste_menu->set_name ("ArdourContextMenu");
2042 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2043 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2044 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f, true)));
2046 cutnpaste_items.push_back (SeparatorElem());
2048 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
2049 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
2051 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
2053 /* Adding new material */
2055 edit_items.push_back (SeparatorElem());
2056 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
2057 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
2061 Menu *nudge_menu = manage (new Menu());
2062 MenuList& nudge_items = nudge_menu->items();
2063 nudge_menu->set_name ("ArdourContextMenu");
2065 edit_items.push_back (SeparatorElem());
2066 nudge_items.push_back (MenuElem (_("Nudge Entire Track Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2067 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2068 nudge_items.push_back (MenuElem (_("Nudge Entire Track Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2069 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2071 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2075 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
2077 using namespace Menu_Helpers;
2081 Menu *play_menu = manage (new Menu);
2082 MenuList& play_items = play_menu->items();
2083 play_menu->set_name ("ArdourContextMenu");
2085 play_items.push_back (MenuElem (_("Play from Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
2086 play_items.push_back (MenuElem (_("Play from Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
2087 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2091 Menu *select_menu = manage (new Menu);
2092 MenuList& select_items = select_menu->items();
2093 select_menu->set_name ("ArdourContextMenu");
2095 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2096 select_items.push_back (MenuElem (_("Select All Objects"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_objects), Selection::Set)));
2097 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2098 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2099 select_items.push_back (SeparatorElem());
2100 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true, true)));
2101 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false, true)));
2102 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2103 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2105 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2109 Menu *cutnpaste_menu = manage (new Menu);
2110 MenuList& cutnpaste_items = cutnpaste_menu->items();
2111 cutnpaste_menu->set_name ("ArdourContextMenu");
2113 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2114 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2115 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f, true)));
2117 Menu *nudge_menu = manage (new Menu());
2118 MenuList& nudge_items = nudge_menu->items();
2119 nudge_menu->set_name ("ArdourContextMenu");
2121 edit_items.push_back (SeparatorElem());
2122 nudge_items.push_back (MenuElem (_("Nudge Entire Track Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2123 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2124 nudge_items.push_back (MenuElem (_("Nudge Entire Track Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2125 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2127 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2131 Editor::grid_type() const
2137 Editor::grid_musical() const
2139 switch (_grid_type) {
2140 case GridTypeBeatDiv32:
2141 case GridTypeBeatDiv28:
2142 case GridTypeBeatDiv24:
2143 case GridTypeBeatDiv20:
2144 case GridTypeBeatDiv16:
2145 case GridTypeBeatDiv14:
2146 case GridTypeBeatDiv12:
2147 case GridTypeBeatDiv10:
2148 case GridTypeBeatDiv8:
2149 case GridTypeBeatDiv7:
2150 case GridTypeBeatDiv6:
2151 case GridTypeBeatDiv5:
2152 case GridTypeBeatDiv4:
2153 case GridTypeBeatDiv3:
2154 case GridTypeBeatDiv2:
2159 case GridTypeTimecode:
2160 case GridTypeMinSec:
2161 case GridTypeCDFrame:
2168 Editor::grid_nonmusical() const
2170 switch (_grid_type) {
2171 case GridTypeTimecode:
2172 case GridTypeMinSec:
2173 case GridTypeCDFrame:
2175 case GridTypeBeatDiv32:
2176 case GridTypeBeatDiv28:
2177 case GridTypeBeatDiv24:
2178 case GridTypeBeatDiv20:
2179 case GridTypeBeatDiv16:
2180 case GridTypeBeatDiv14:
2181 case GridTypeBeatDiv12:
2182 case GridTypeBeatDiv10:
2183 case GridTypeBeatDiv8:
2184 case GridTypeBeatDiv7:
2185 case GridTypeBeatDiv6:
2186 case GridTypeBeatDiv5:
2187 case GridTypeBeatDiv4:
2188 case GridTypeBeatDiv3:
2189 case GridTypeBeatDiv2:
2198 Editor::snap_mode() const
2204 Editor::show_rulers_for_grid ()
2206 /* show appropriate rulers for this grid setting. */
2207 if (grid_musical()) {
2208 ruler_tempo_action->set_active(true);
2209 ruler_meter_action->set_active(true);
2210 ruler_bbt_action->set_active(true);
2212 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2213 ruler_timecode_action->set_active(false);
2214 ruler_minsec_action->set_active(false);
2215 ruler_samples_action->set_active(false);
2217 } else if (_grid_type == GridTypeTimecode) {
2218 ruler_timecode_action->set_active(true);
2220 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2221 ruler_tempo_action->set_active(false);
2222 ruler_meter_action->set_active(false);
2223 ruler_bbt_action->set_active(false);
2224 ruler_minsec_action->set_active(false);
2225 ruler_samples_action->set_active(false);
2227 } else if (_grid_type == GridTypeMinSec) {
2228 ruler_minsec_action->set_active(true);
2230 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2231 ruler_tempo_action->set_active(false);
2232 ruler_meter_action->set_active(false);
2233 ruler_bbt_action->set_active(false);
2234 ruler_timecode_action->set_active(false);
2235 ruler_samples_action->set_active(false);
2237 } else if (_grid_type == GridTypeCDFrame) {
2238 ruler_cd_marker_action->set_active(true);
2239 ruler_minsec_action->set_active(true);
2241 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2242 ruler_tempo_action->set_active(false);
2243 ruler_meter_action->set_active(false);
2244 ruler_bbt_action->set_active(false);
2245 ruler_timecode_action->set_active(false);
2246 ruler_samples_action->set_active(false);
2252 Editor::set_grid_to (GridType gt)
2254 if (_grid_type == gt) { // already set
2258 unsigned int grid_ind = (unsigned int)gt;
2260 if (internal_editing() && UIConfiguration::instance().get_grid_follows_internal()) {
2261 internal_grid_type = gt;
2263 pre_internal_grid_type = gt;
2268 if (grid_ind > grid_type_strings.size() - 1) {
2270 _grid_type = (GridType)grid_ind;
2273 string str = grid_type_strings[grid_ind];
2275 if (str != grid_type_selector.get_text()) {
2276 grid_type_selector.set_text (str);
2279 if (UIConfiguration::instance().get_show_grids_ruler()) {
2280 show_rulers_for_grid ();
2285 if (grid_musical()) {
2286 compute_bbt_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
2287 update_tempo_based_rulers ();
2290 mark_region_boundary_cache_dirty ();
2292 redisplay_grid (false);
2294 SnapChanged (); /* EMIT SIGNAL */
2298 Editor::set_snap_mode (SnapMode mode)
2300 if (internal_editing()) {
2301 internal_snap_mode = mode;
2303 pre_internal_snap_mode = mode;
2308 if (_snap_mode == SnapOff) {
2309 snap_mode_button.set_active_state (Gtkmm2ext::Off);
2311 snap_mode_button.set_active_state (Gtkmm2ext::ExplicitActive);
2318 Editor::set_edit_point_preference (EditPoint ep, bool force)
2320 bool changed = (_edit_point != ep);
2323 if (Profile->get_mixbus())
2324 if (ep == EditAtSelectedMarker)
2325 ep = EditAtPlayhead;
2327 string str = edit_point_strings[(int)ep];
2328 if (str != edit_point_selector.get_text ()) {
2329 edit_point_selector.set_text (str);
2332 update_all_enter_cursors();
2334 if (!force && !changed) {
2338 const char* action=NULL;
2340 switch (_edit_point) {
2341 case EditAtPlayhead:
2342 action = "edit-at-playhead";
2344 case EditAtSelectedMarker:
2345 action = "edit-at-marker";
2348 action = "edit-at-mouse";
2352 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2354 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2358 bool in_track_canvas;
2360 if (!mouse_sample (foo, in_track_canvas)) {
2361 in_track_canvas = false;
2364 reset_canvas_action_sensitivity (in_track_canvas);
2365 sensitize_the_right_region_actions (false);
2371 Editor::set_state (const XMLNode& node, int version)
2374 PBD::Unwinder<bool> nsi (no_save_instant, true);
2377 Tabbable::set_state (node, version);
2380 if (_session && node.get_property ("playhead", ph_pos)) {
2382 playhead_cursor->set_position (ph_pos);
2384 warning << _("Playhead position stored with a negative value - ignored (use zero instead)") << endmsg;
2385 playhead_cursor->set_position (0);
2388 playhead_cursor->set_position (0);
2391 node.get_property ("mixer-width", editor_mixer_strip_width);
2393 node.get_property ("zoom-focus", zoom_focus);
2394 zoom_focus_selection_done (zoom_focus);
2397 if (node.get_property ("zoom", z)) {
2398 /* older versions of ardour used floating point samples_per_pixel */
2399 reset_zoom (llrintf (z));
2401 reset_zoom (samples_per_pixel);
2405 if (node.get_property ("visible-track-count", cnt)) {
2406 set_visible_track_count (cnt);
2410 if (!node.get_property ("grid-type", grid_type)) {
2411 grid_type = _grid_type;
2413 set_grid_to (grid_type);
2416 if (node.get_property ("snap-mode", sm)) {
2417 snap_mode_selection_done(sm);
2418 /* set text of Dropdown. in case _snap_mode == SnapOff (default)
2419 * snap_mode_selection_done() will only mark an already active item as active
2420 * which does not trigger set_text().
2424 set_snap_mode (_snap_mode);
2427 node.get_property ("internal-grid-type", internal_grid_type);
2428 node.get_property ("internal-snap-mode", internal_snap_mode);
2429 node.get_property ("pre-internal-grid-type", pre_internal_grid_type);
2430 node.get_property ("pre-internal-snap-mode", pre_internal_snap_mode);
2433 if (node.get_property ("mouse-mode", mm_str)) {
2434 MouseMode m = str2mousemode(mm_str);
2435 set_mouse_mode (m, true);
2437 set_mouse_mode (MouseObject, true);
2441 if (node.get_property ("left-frame", lf_pos)) {
2445 reset_x_origin (lf_pos);
2449 if (node.get_property ("y-origin", y_origin)) {
2450 reset_y_origin (y_origin);
2453 if (node.get_property ("join-object-range", yn)) {
2454 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object-range"));
2456 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2457 tact->set_active (!yn);
2458 tact->set_active (yn);
2460 set_mouse_mode(mouse_mode, true);
2464 if (node.get_property ("edit-point", ep)) {
2465 set_edit_point_preference (ep, true);
2467 set_edit_point_preference (_edit_point);
2470 if (node.get_property ("follow-playhead", yn)) {
2471 set_follow_playhead (yn);
2474 if (node.get_property ("stationary-playhead", yn)) {
2475 set_stationary_playhead (yn);
2478 RegionListSortType sort_type;
2479 if (node.get_property ("region-list-sort-type", sort_type)) {
2480 _regions->reset_sort_type (sort_type, true);
2483 if (node.get_property ("show-editor-mixer", yn)) {
2485 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2488 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2490 /* do it twice to force the change */
2492 tact->set_active (!yn);
2493 tact->set_active (yn);
2496 if (node.get_property ("show-editor-list", yn)) {
2498 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2501 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2503 /* do it twice to force the change */
2505 tact->set_active (!yn);
2506 tact->set_active (yn);
2510 if (node.get_property (X_("editor-list-page"), el_page)) {
2511 _the_notebook.set_current_page (el_page);
2514 if (node.get_property (X_("show-marker-lines"), yn)) {
2515 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2517 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2519 tact->set_active (!yn);
2520 tact->set_active (yn);
2523 XMLNodeList children = node.children ();
2524 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2525 selection->set_state (**i, Stateful::current_state_version);
2526 _regions->set_state (**i);
2527 _locations->set_state (**i);
2530 if (node.get_property ("maximised", yn)) {
2531 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMaximalEditor"));
2533 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2534 bool fs = tact && tact->get_active();
2536 ActionManager::do_action ("Common", "ToggleMaximalEditor");
2540 samplepos_t nudge_clock_value;
2541 if (node.get_property ("nudge-clock-value", nudge_clock_value)) {
2542 nudge_clock->set (nudge_clock_value);
2544 nudge_clock->set_mode (AudioClock::Timecode);
2545 nudge_clock->set (_session->sample_rate() * 5, true);
2550 * Not all properties may have been in XML, but
2551 * those that are linked to a private variable may need changing
2555 act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2556 yn = _follow_playhead;
2558 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2559 if (tact->get_active() != yn) {
2560 tact->set_active (yn);
2564 act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2565 yn = _stationary_playhead;
2567 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2568 if (tact->get_active() != yn) {
2569 tact->set_active (yn);
2578 Editor::get_state ()
2580 XMLNode* node = new XMLNode (X_("Editor"));
2582 node->set_property ("id", id().to_s ());
2584 node->add_child_nocopy (Tabbable::get_state());
2586 node->set_property("edit-horizontal-pane-pos", edit_pane.get_divider ());
2587 node->set_property("notebook-shrunk", _notebook_shrunk);
2588 node->set_property("edit-vertical-pane-pos", editor_summary_pane.get_divider());
2590 maybe_add_mixer_strip_width (*node);
2592 node->set_property ("zoom-focus", zoom_focus);
2594 node->set_property ("zoom", samples_per_pixel);
2595 node->set_property ("grid-type", _grid_type);
2596 node->set_property ("snap-mode", _snap_mode);
2597 node->set_property ("internal-grid-type", internal_grid_type);
2598 node->set_property ("internal-snap-mode", internal_snap_mode);
2599 node->set_property ("pre-internal-grid-type", pre_internal_grid_type);
2600 node->set_property ("pre-internal-snap-mode", pre_internal_snap_mode);
2601 node->set_property ("edit-point", _edit_point);
2602 node->set_property ("visible-track-count", _visible_track_count);
2604 node->set_property ("playhead", playhead_cursor->current_sample ());
2605 node->set_property ("left-frame", _leftmost_sample);
2606 node->set_property ("y-origin", vertical_adjustment.get_value ());
2608 node->set_property ("maximised", _maximised);
2609 node->set_property ("follow-playhead", _follow_playhead);
2610 node->set_property ("stationary-playhead", _stationary_playhead);
2611 node->set_property ("region-list-sort-type", _regions->sort_type ());
2612 node->set_property ("mouse-mode", mouse_mode);
2613 node->set_property ("join-object-range", smart_mode_action->get_active ());
2615 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2617 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2618 node->set_property (X_("show-editor-mixer"), tact->get_active());
2621 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2623 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2624 node->set_property (X_("show-editor-list"), tact->get_active());
2627 node->set_property (X_("editor-list-page"), _the_notebook.get_current_page ());
2629 if (button_bindings) {
2630 XMLNode* bb = new XMLNode (X_("Buttons"));
2631 button_bindings->save (*bb);
2632 node->add_child_nocopy (*bb);
2635 node->set_property (X_("show-marker-lines"), _show_marker_lines);
2637 node->add_child_nocopy (selection->get_state ());
2638 node->add_child_nocopy (_regions->get_state ());
2640 node->set_property ("nudge-clock-value", nudge_clock->current_duration());
2642 node->add_child_nocopy (_locations->get_state ());
2647 /** if @param trackview_relative_offset is true, @param y y is an offset into the trackview area, in pixel units
2648 * if @param trackview_relative_offset is false, @param y y is a global canvas * coordinate, in pixel units
2650 * @return pair: TimeAxisView that y is over, layer index.
2652 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2653 * in stacked or expanded region display mode, otherwise 0.
2655 std::pair<TimeAxisView *, double>
2656 Editor::trackview_by_y_position (double y, bool trackview_relative_offset) const
2658 if (!trackview_relative_offset) {
2659 y -= _trackview_group->canvas_origin().y;
2663 return std::make_pair ((TimeAxisView *) 0, 0);
2666 for (TrackViewList::const_iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2668 std::pair<TimeAxisView*, double> const r = (*iter)->covers_y_position (y);
2675 return std::make_pair ((TimeAxisView *) 0, 0);
2679 Editor::set_snapped_cursor_position (samplepos_t pos)
2681 if (_edit_point == EditAtMouse) {
2682 snapped_cursor->set_position(pos);
2687 /** Snap a position to the grid, if appropriate, taking into account current
2688 * grid settings and also the state of any snap modifier keys that may be pressed.
2689 * @param start Position to snap.
2690 * @param event Event to get current key modifier information from, or 0.
2693 Editor::snap_to_with_modifier (MusicSample& start, GdkEvent const * event, RoundMode direction, SnapPref pref)
2695 if (!_session || !event) {
2699 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2700 if (_snap_mode == SnapOff) {
2701 snap_to_internal (start, direction, pref);
2703 start.set (start.sample, 0);
2706 if (_snap_mode != SnapOff) {
2707 snap_to_internal (start, direction, pref);
2708 } else if (ArdourKeyboard::indicates_snap_delta (event->button.state)) {
2709 /* SnapOff, but we pressed the snap_delta modifier */
2710 snap_to_internal (start, direction, pref);
2712 start.set (start.sample, 0);
2718 Editor::snap_to (MusicSample& start, RoundMode direction, SnapPref pref, bool ensure_snap)
2720 if (!_session || (_snap_mode == SnapOff && !ensure_snap)) {
2721 start.set (start.sample, 0);
2725 snap_to_internal (start, direction, pref, ensure_snap);
2729 check_best_snap (samplepos_t presnap, samplepos_t &test, samplepos_t &dist, samplepos_t &best)
2731 samplepos_t diff = abs (test - presnap);
2737 test = max_samplepos; // reset this so it doesn't get accidentally reused
2741 Editor::snap_to_timecode (MusicSample presnap, RoundMode direction, SnapPref gpref)
2743 samplepos_t start = presnap.sample;
2744 const samplepos_t one_timecode_second = (samplepos_t)(rint(_session->timecode_frames_per_second()) * _session->samples_per_timecode_frame());
2745 samplepos_t one_timecode_minute = (samplepos_t)(rint(_session->timecode_frames_per_second()) * _session->samples_per_timecode_frame() * 60);
2747 TimecodeRulerScale scale = (gpref != SnapToGrid_Unscaled) ? timecode_ruler_scale : timecode_show_samples;
2750 case timecode_show_bits:
2751 case timecode_show_samples:
2752 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2753 fmod((double)start, (double)_session->samples_per_timecode_frame()) == 0) {
2754 /* start is already on a whole timecode frame, do nothing */
2755 } else if (((direction == 0) && (fmod((double)start, (double)_session->samples_per_timecode_frame()) > (_session->samples_per_timecode_frame() / 2))) || (direction > 0)) {
2756 start = (samplepos_t) (ceil ((double) start / _session->samples_per_timecode_frame()) * _session->samples_per_timecode_frame());
2758 start = (samplepos_t) (floor ((double) start / _session->samples_per_timecode_frame()) * _session->samples_per_timecode_frame());
2762 case timecode_show_seconds:
2763 if (_session->config.get_timecode_offset_negative()) {
2764 start += _session->config.get_timecode_offset ();
2766 start -= _session->config.get_timecode_offset ();
2768 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2769 (start % one_timecode_second == 0)) {
2770 /* start is already on a whole second, do nothing */
2771 } else if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2772 start = (samplepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2774 start = (samplepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2777 if (_session->config.get_timecode_offset_negative()) {
2778 start -= _session->config.get_timecode_offset ();
2780 start += _session->config.get_timecode_offset ();
2784 case timecode_show_minutes:
2785 case timecode_show_hours:
2786 case timecode_show_many_hours:
2787 if (_session->config.get_timecode_offset_negative()) {
2788 start += _session->config.get_timecode_offset ();
2790 start -= _session->config.get_timecode_offset ();
2792 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2793 (start % one_timecode_minute == 0)) {
2794 /* start is already on a whole minute, do nothing */
2795 } else if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2796 start = (samplepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2798 start = (samplepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2800 if (_session->config.get_timecode_offset_negative()) {
2801 start -= _session->config.get_timecode_offset ();
2803 start += _session->config.get_timecode_offset ();
2807 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2810 MusicSample ret(start,0);
2815 Editor::snap_to_minsec (MusicSample presnap, RoundMode direction, SnapPref gpref)
2817 MusicSample ret(presnap);
2819 const samplepos_t one_second = _session->sample_rate();
2820 const samplepos_t one_minute = one_second * 60;
2821 const samplepos_t one_hour = one_minute * 60;
2823 MinsecRulerScale scale = (gpref != SnapToGrid_Unscaled) ? minsec_ruler_scale : minsec_show_seconds;
2826 case minsec_show_msecs:
2827 case minsec_show_seconds: {
2828 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2829 presnap.sample % one_second == 0) {
2830 /* start is already on a whole second, do nothing */
2831 } else if (((direction == 0) && (presnap.sample % one_second > one_second / 2)) || (direction > 0)) {
2832 ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_second) * one_second;
2834 ret.sample = (samplepos_t) floor ((double) presnap.sample / one_second) * one_second;
2838 case minsec_show_minutes: {
2839 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2840 presnap.sample % one_minute == 0) {
2841 /* start is already on a whole minute, do nothing */
2842 } else if (((direction == 0) && (presnap.sample % one_minute > one_minute / 2)) || (direction > 0)) {
2843 ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_minute) * one_minute;
2845 ret.sample = (samplepos_t) floor ((double) presnap.sample / one_minute) * one_minute;
2850 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2851 presnap.sample % one_hour == 0) {
2852 /* start is already on a whole hour, do nothing */
2853 } else if (((direction == 0) && (presnap.sample % one_hour > one_hour / 2)) || (direction > 0)) {
2854 ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_hour) * one_hour;
2856 ret.sample = (samplepos_t) floor ((double) presnap.sample / one_hour) * one_hour;
2865 Editor::snap_to_cd_frames (MusicSample presnap, RoundMode direction, SnapPref gpref)
2867 if ((gpref != SnapToGrid_Unscaled) && (minsec_ruler_scale != minsec_show_msecs)) {
2868 return snap_to_minsec (presnap, direction, gpref);
2871 const samplepos_t one_second = _session->sample_rate();
2873 MusicSample ret(presnap);
2875 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2876 presnap.sample % (one_second/75) == 0) {
2877 /* start is already on a whole CD sample, do nothing */
2878 } else if (((direction == 0) && (presnap.sample % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2879 ret.sample = (samplepos_t) ceil ((double) presnap.sample / (one_second / 75)) * (one_second / 75);
2881 ret.sample = (samplepos_t) floor ((double) presnap.sample / (one_second / 75)) * (one_second / 75);
2888 Editor::snap_to_bbt (MusicSample presnap, RoundMode direction, SnapPref gpref)
2890 MusicSample ret(presnap);
2892 if (gpref != SnapToGrid_Unscaled) { // use the visual grid lines which are limited by the zoom scale that the user selected
2895 switch (_grid_type) {
2896 case GridTypeBeatDiv3:
2897 case GridTypeBeatDiv6:
2898 case GridTypeBeatDiv12:
2899 case GridTypeBeatDiv24:
2902 case GridTypeBeatDiv5:
2903 case GridTypeBeatDiv10:
2904 case GridTypeBeatDiv20:
2907 case GridTypeBeatDiv7:
2908 case GridTypeBeatDiv14:
2909 case GridTypeBeatDiv28:
2916 BBTRulerScale scale = bbt_ruler_scale;
2923 ret = _session->tempo_map().round_to_bar (presnap.sample, direction);
2925 case bbt_show_quarters:
2926 ret = _session->tempo_map().round_to_beat (presnap.sample, direction);
2928 case bbt_show_eighths:
2929 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 1 * divisor, direction);
2931 case bbt_show_sixteenths:
2932 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 2 * divisor, direction);
2934 case bbt_show_thirtyseconds:
2935 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 4 * divisor, direction);
2939 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, get_grid_beat_divisions(_grid_type), direction);
2946 Editor::snap_to_grid (MusicSample presnap, RoundMode direction, SnapPref gpref)
2948 MusicSample ret(presnap);
2950 if (grid_musical()) {
2951 ret = snap_to_bbt (presnap, direction, gpref);
2954 switch (_grid_type) {
2955 case GridTypeTimecode:
2956 ret = snap_to_timecode(presnap, direction, gpref);
2958 case GridTypeMinSec:
2959 ret = snap_to_minsec(presnap, direction, gpref);
2961 case GridTypeCDFrame:
2962 ret = snap_to_cd_frames(presnap, direction, gpref);
2972 Editor::snap_to_marker (samplepos_t presnap, RoundMode direction)
2978 _session->locations()->marks_either_side (presnap, before, after);
2980 if (before == max_samplepos && after == max_samplepos) {
2981 /* No marks to snap to, so just don't snap */
2983 } else if (before == max_samplepos) {
2985 } else if (after == max_samplepos) {
2988 if ((direction == RoundUpMaybe || direction == RoundUpAlways)) {
2990 } else if ((direction == RoundDownMaybe || direction == RoundDownAlways)) {
2992 } else if (direction == 0) {
2993 if ((presnap - before) < (after - presnap)) {
3005 Editor::snap_to_internal (MusicSample& start, RoundMode direction, SnapPref pref, bool ensure_snap)
3007 const samplepos_t presnap = start.sample;
3009 samplepos_t test = max_samplepos; // for each snap, we'll use this value
3010 samplepos_t dist = max_samplepos; // this records the distance of the best snap result we've found so far
3011 samplepos_t best = max_samplepos; // this records the best snap-result we've found so far
3013 /* check snap-to-marker */
3014 if ((pref == SnapToAny_Visual) && UIConfiguration::instance().get_snap_to_marks()) {
3015 test = snap_to_marker (presnap, direction);
3016 check_best_snap(presnap, test, dist, best);
3019 /* check snap-to-region-{start/end/sync} */
3021 (pref == SnapToAny_Visual) &&
3022 (UIConfiguration::instance().get_snap_to_region_start() || UIConfiguration::instance().get_snap_to_region_end() || UIConfiguration::instance().get_snap_to_region_sync())
3024 if (!region_boundary_cache.empty()) {
3026 vector<samplepos_t>::iterator prev = region_boundary_cache.begin();
3027 vector<samplepos_t>::iterator next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), presnap);
3028 if (next != region_boundary_cache.begin ()) {
3033 if ((direction == RoundUpMaybe || direction == RoundUpAlways))
3035 else if ((direction == RoundDownMaybe || direction == RoundDownAlways))
3037 else if (direction == 0) {
3038 if ((presnap - *prev) < (*next - presnap)) {
3047 check_best_snap(presnap, test, dist, best);
3051 if (UIConfiguration::instance().get_snap_to_grid() && (_grid_type != GridTypeNone)) {
3052 MusicSample pre(presnap, 0);
3053 MusicSample post = snap_to_grid (pre, direction, pref);
3054 check_best_snap(presnap, post.sample, dist, best);
3057 /* now check "magnetic" state: is the grid within reasonable on-screen distance to trigger a snap?
3058 * this also helps to avoid snapping to somewhere the user can't see. (i.e.: I clicked on a region and it disappeared!!)
3059 * ToDo: Perhaps this should only occur if EditPointMouse?
3061 int snap_threshold_s = pixel_to_sample(UIConfiguration::instance().get_snap_threshold());
3063 start.set (best, 0);
3065 } else if (presnap > best) {
3066 if (presnap > (best+ snap_threshold_s)) {
3069 } else if (presnap < best) {
3070 if (presnap < (best - snap_threshold_s)) {
3075 start.set (best, 0);
3080 Editor::setup_toolbar ()
3082 HBox* mode_box = manage(new HBox);
3083 mode_box->set_border_width (2);
3084 mode_box->set_spacing(2);
3086 HBox* mouse_mode_box = manage (new HBox);
3087 HBox* mouse_mode_hbox = manage (new HBox);
3088 VBox* mouse_mode_vbox = manage (new VBox);
3089 Alignment* mouse_mode_align = manage (new Alignment);
3091 Glib::RefPtr<SizeGroup> mouse_mode_size_group = SizeGroup::create (SIZE_GROUP_VERTICAL);
3092 mouse_mode_size_group->add_widget (smart_mode_button);
3093 mouse_mode_size_group->add_widget (mouse_move_button);
3094 mouse_mode_size_group->add_widget (mouse_cut_button);
3095 mouse_mode_size_group->add_widget (mouse_select_button);
3096 mouse_mode_size_group->add_widget (mouse_timefx_button);
3097 mouse_mode_size_group->add_widget (mouse_audition_button);
3098 mouse_mode_size_group->add_widget (mouse_draw_button);
3099 mouse_mode_size_group->add_widget (mouse_content_button);
3101 if (!Profile->get_mixbus()) {
3102 mouse_mode_size_group->add_widget (zoom_in_button);
3103 mouse_mode_size_group->add_widget (zoom_out_button);
3104 mouse_mode_size_group->add_widget (zoom_out_full_button);
3105 mouse_mode_size_group->add_widget (zoom_focus_selector);
3106 mouse_mode_size_group->add_widget (tav_shrink_button);
3107 mouse_mode_size_group->add_widget (tav_expand_button);
3109 mouse_mode_size_group->add_widget (zoom_preset_selector);
3110 mouse_mode_size_group->add_widget (visible_tracks_selector);
3113 mouse_mode_size_group->add_widget (grid_type_selector);
3114 mouse_mode_size_group->add_widget (snap_mode_button);
3116 mouse_mode_size_group->add_widget (edit_point_selector);
3117 mouse_mode_size_group->add_widget (edit_mode_selector);
3119 mouse_mode_size_group->add_widget (*nudge_clock);
3120 mouse_mode_size_group->add_widget (nudge_forward_button);
3121 mouse_mode_size_group->add_widget (nudge_backward_button);
3123 mouse_mode_hbox->set_spacing (2);
3125 if (!ARDOUR::Profile->get_trx()) {
3126 mouse_mode_hbox->pack_start (smart_mode_button, false, false);
3129 mouse_mode_hbox->pack_start (mouse_move_button, false, false);
3130 mouse_mode_hbox->pack_start (mouse_select_button, false, false);
3132 if (!ARDOUR::Profile->get_mixbus()) {
3133 mouse_mode_hbox->pack_start (mouse_cut_button, false, false);
3134 mouse_mode_hbox->pack_start (mouse_audition_button, false, false);
3137 if (!ARDOUR::Profile->get_trx()) {
3138 mouse_mode_hbox->pack_start (mouse_timefx_button, false, false);
3139 mouse_mode_hbox->pack_start (mouse_draw_button, false, false);
3140 mouse_mode_hbox->pack_start (mouse_content_button, false, false);
3143 mouse_mode_vbox->pack_start (*mouse_mode_hbox);
3145 mouse_mode_align->add (*mouse_mode_vbox);
3146 mouse_mode_align->set (0.5, 1.0, 0.0, 0.0);
3148 mouse_mode_box->pack_start (*mouse_mode_align, false, false);
3150 edit_mode_selector.set_name ("mouse mode button");
3152 if (!ARDOUR::Profile->get_trx()) {
3153 mode_box->pack_start (edit_mode_selector, false, false);
3154 mode_box->pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3155 mode_box->pack_start (edit_point_selector, false, false);
3156 mode_box->pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3159 mode_box->pack_start (*mouse_mode_box, false, false);
3163 _zoom_box.set_spacing (2);
3164 _zoom_box.set_border_width (2);
3168 zoom_preset_selector.set_name ("zoom button");
3169 zoom_preset_selector.set_icon (ArdourIcon::ZoomExpand);
3171 zoom_in_button.set_name ("zoom button");
3172 zoom_in_button.set_icon (ArdourIcon::ZoomIn);
3173 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-in"));
3174 zoom_in_button.set_related_action (act);
3176 zoom_out_button.set_name ("zoom button");
3177 zoom_out_button.set_icon (ArdourIcon::ZoomOut);
3178 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-out"));
3179 zoom_out_button.set_related_action (act);
3181 zoom_out_full_button.set_name ("zoom button");
3182 zoom_out_full_button.set_icon (ArdourIcon::ZoomFull);
3183 act = ActionManager::get_action (X_("Editor"), X_("zoom-to-session"));
3184 zoom_out_full_button.set_related_action (act);
3186 zoom_focus_selector.set_name ("zoom button");
3188 if (ARDOUR::Profile->get_mixbus()) {
3189 _zoom_box.pack_start (zoom_preset_selector, false, false);
3190 } else if (ARDOUR::Profile->get_trx()) {
3191 mode_box->pack_start (zoom_out_button, false, false);
3192 mode_box->pack_start (zoom_in_button, false, false);
3194 _zoom_box.pack_start (zoom_out_button, false, false);
3195 _zoom_box.pack_start (zoom_in_button, false, false);
3196 _zoom_box.pack_start (zoom_out_full_button, false, false);
3197 _zoom_box.pack_start (zoom_focus_selector, false, false);
3200 /* Track zoom buttons */
3201 _track_box.set_spacing (2);
3202 _track_box.set_border_width (2);
3204 visible_tracks_selector.set_name ("zoom button");
3205 if (Profile->get_mixbus()) {
3206 visible_tracks_selector.set_icon (ArdourIcon::TimeAxisExpand);
3208 set_size_request_to_display_given_text (visible_tracks_selector, _("All"), 30, 2);
3211 tav_expand_button.set_name ("zoom button");
3212 tav_expand_button.set_icon (ArdourIcon::TimeAxisExpand);
3213 act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
3214 tav_expand_button.set_related_action (act);
3216 tav_shrink_button.set_name ("zoom button");
3217 tav_shrink_button.set_icon (ArdourIcon::TimeAxisShrink);
3218 act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
3219 tav_shrink_button.set_related_action (act);
3221 if (ARDOUR::Profile->get_mixbus()) {
3222 _track_box.pack_start (visible_tracks_selector);
3223 } else if (ARDOUR::Profile->get_trx()) {
3224 _track_box.pack_start (tav_shrink_button);
3225 _track_box.pack_start (tav_expand_button);
3227 _track_box.pack_start (visible_tracks_selector);
3228 _track_box.pack_start (tav_shrink_button);
3229 _track_box.pack_start (tav_expand_button);
3232 snap_box.set_spacing (2);
3233 snap_box.set_border_width (2);
3235 grid_type_selector.set_name ("mouse mode button");
3237 snap_mode_button.set_name ("mouse mode button");
3239 edit_point_selector.set_name ("mouse mode button");
3241 snap_box.pack_start (snap_mode_button, false, false);
3242 snap_box.pack_start (grid_type_selector, false, false);
3246 HBox *nudge_box = manage (new HBox);
3247 nudge_box->set_spacing (2);
3248 nudge_box->set_border_width (2);
3250 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
3251 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
3253 nudge_box->pack_start (nudge_backward_button, false, false);
3254 nudge_box->pack_start (nudge_forward_button, false, false);
3255 nudge_box->pack_start (*nudge_clock, false, false);
3258 /* Pack everything in... */
3260 toolbar_hbox.set_spacing (2);
3261 toolbar_hbox.set_border_width (2);
3263 ArdourWidgets::ArdourDropShadow *tool_shadow = manage (new (ArdourWidgets::ArdourDropShadow));
3264 tool_shadow->set_size_request (4, -1);
3265 tool_shadow->show();
3267 ebox_hpacker.pack_start (*tool_shadow, false, false);
3268 ebox_hpacker.pack_start(ebox_vpacker, true, true);
3270 Gtk::EventBox* spacer = manage (new Gtk::EventBox); // extra space under the mouse toolbar, for aesthetics
3271 spacer->set_name("EditorWindow");
3272 spacer->set_size_request(-1,4);
3275 ebox_vpacker.pack_start(toolbar_hbox, false, false);
3276 ebox_vpacker.pack_start(*spacer, false, false);
3277 ebox_vpacker.show();
3279 toolbar_hbox.pack_start (*mode_box, false, false);
3281 if (!ARDOUR::Profile->get_trx()) {
3283 toolbar_hbox.pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3285 toolbar_hbox.pack_start (snap_box, false, false);
3287 toolbar_hbox.pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3289 toolbar_hbox.pack_start (*nudge_box, false, false);
3291 toolbar_hbox.pack_end (_zoom_box, false, false, 2);
3293 toolbar_hbox.pack_end (*(manage (new ArdourVSpacer ())), false, false, 3);
3295 toolbar_hbox.pack_end (_track_box, false, false);
3299 toolbar_hbox.show_all ();
3303 Editor::build_edit_point_menu ()
3305 using namespace Menu_Helpers;
3307 edit_point_selector.AddMenuElem (MenuElem (edit_point_strings[(int)EditAtPlayhead], sigc::bind (sigc::mem_fun(*this, &Editor::edit_point_selection_done), (EditPoint) EditAtPlayhead)));
3308 if(!Profile->get_mixbus())
3309 edit_point_selector.AddMenuElem (MenuElem (edit_point_strings[(int)EditAtSelectedMarker], sigc::bind (sigc::mem_fun(*this, &Editor::edit_point_selection_done), (EditPoint) EditAtSelectedMarker)));
3310 edit_point_selector.AddMenuElem (MenuElem (edit_point_strings[(int)EditAtMouse], sigc::bind (sigc::mem_fun(*this, &Editor::edit_point_selection_done), (EditPoint) EditAtMouse)));
3312 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_TRIANGLE_WIDTH, 2);
3316 Editor::build_edit_mode_menu ()
3318 using namespace Menu_Helpers;
3320 edit_mode_selector.AddMenuElem (MenuElem (edit_mode_strings[(int)Slide], sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Slide)));
3321 edit_mode_selector.AddMenuElem (MenuElem (edit_mode_strings[(int)Ripple], sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Ripple)));
3322 edit_mode_selector.AddMenuElem (MenuElem (edit_mode_strings[(int)Lock], sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Lock)));
3323 /* Note: Splice was removed */
3325 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_TRIANGLE_WIDTH, 2);
3329 Editor::build_grid_type_menu ()
3331 using namespace Menu_Helpers;
3333 /* main grid: bars, quarter-notes, etc */
3334 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeNone], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeNone)));
3335 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBar], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBar)));
3336 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeat], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeat)));
3337 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv2], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv2)));
3338 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv4], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv4)));
3339 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv8], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv8)));
3340 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv16], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv16)));
3341 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv32], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv32)));
3344 grid_type_selector.AddMenuElem(SeparatorElem());
3345 Gtk::Menu *_triplet_menu = manage (new Menu);
3346 MenuList& triplet_items (_triplet_menu->items());
3348 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv3], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv3)));
3349 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv6], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv6)));
3350 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv12], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv12)));
3351 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv24], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv24)));
3353 grid_type_selector.AddMenuElem (Menu_Helpers::MenuElem (_("Triplets"), *_triplet_menu));
3355 /* quintuplet grid */
3356 Gtk::Menu *_quintuplet_menu = manage (new Menu);
3357 MenuList& quintuplet_items (_quintuplet_menu->items());
3359 quintuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv5], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv5)));
3360 quintuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv10], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv10)));
3361 quintuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv20], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv20)));
3363 grid_type_selector.AddMenuElem (Menu_Helpers::MenuElem (_("Quintuplets"), *_quintuplet_menu));
3365 /* septuplet grid */
3366 Gtk::Menu *_septuplet_menu = manage (new Menu);
3367 MenuList& septuplet_items (_septuplet_menu->items());
3369 septuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv7], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv7)));
3370 septuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv14], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv14)));
3371 septuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv28], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv28)));
3373 grid_type_selector.AddMenuElem (Menu_Helpers::MenuElem (_("Septuplets"), *_septuplet_menu));
3375 grid_type_selector.AddMenuElem(SeparatorElem());
3376 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeTimecode], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeTimecode)));
3377 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeMinSec], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeMinSec)));
3378 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeCDFrame], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeCDFrame)));
3382 Editor::setup_tooltips ()
3384 set_tooltip (smart_mode_button, _("Smart Mode (add range functions to Grab Mode)"));
3385 set_tooltip (mouse_move_button, _("Grab Mode (select/move objects)"));
3386 set_tooltip (mouse_cut_button, _("Cut Mode (split regions)"));
3387 set_tooltip (mouse_select_button, _("Range Mode (select time ranges)"));
3388 set_tooltip (mouse_draw_button, _("Draw Mode (draw and edit gain/notes/automation)"));
3389 set_tooltip (mouse_timefx_button, _("Stretch Mode (time-stretch audio and midi regions, preserving pitch)"));
3390 set_tooltip (mouse_audition_button, _("Audition Mode (listen to regions)"));
3391 set_tooltip (mouse_content_button, _("Internal Edit Mode (edit notes and automation points)"));
3392 set_tooltip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
3393 set_tooltip (nudge_forward_button, _("Nudge Region/Selection Later"));
3394 set_tooltip (nudge_backward_button, _("Nudge Region/Selection Earlier"));
3395 set_tooltip (zoom_in_button, _("Zoom In"));
3396 set_tooltip (zoom_out_button, _("Zoom Out"));
3397 set_tooltip (zoom_preset_selector, _("Zoom to Time Scale"));
3398 set_tooltip (zoom_out_full_button, _("Zoom to Session"));
3399 set_tooltip (zoom_focus_selector, _("Zoom Focus"));
3400 set_tooltip (tav_expand_button, _("Expand Tracks"));
3401 set_tooltip (tav_shrink_button, _("Shrink Tracks"));
3402 set_tooltip (visible_tracks_selector, _("Number of visible tracks"));
3403 set_tooltip (grid_type_selector, _("Grid Mode"));
3404 set_tooltip (snap_mode_button, _("Snap Mode\n\nRight-click to visit Snap preferences."));
3405 set_tooltip (edit_point_selector, _("Edit Point"));
3406 set_tooltip (edit_mode_selector, _("Edit Mode"));
3407 set_tooltip (nudge_clock, _("Nudge Clock\n(controls distance used to nudge regions and selections)"));
3411 Editor::convert_drop_to_paths (
3412 vector<string>& paths,
3413 const RefPtr<Gdk::DragContext>& /*context*/,
3416 const SelectionData& data,
3420 if (_session == 0) {
3424 vector<string> uris = data.get_uris();
3428 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3429 are actually URI lists. So do it by hand.
3432 if (data.get_target() != "text/plain") {
3436 /* Parse the "uri-list" format that Nautilus provides,
3437 where each pathname is delimited by \r\n.
3439 THERE MAY BE NO NULL TERMINATING CHAR!!!
3442 string txt = data.get_text();
3446 p = (char *) malloc (txt.length() + 1);
3447 txt.copy (p, txt.length(), 0);
3448 p[txt.length()] = '\0';
3454 while (g_ascii_isspace (*p))
3458 while (*q && (*q != '\n') && (*q != '\r')) {
3465 while (q > p && g_ascii_isspace (*q))
3470 uris.push_back (string (p, q - p + 1));
3474 p = strchr (p, '\n');
3486 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3487 if ((*i).substr (0,7) == "file://") {
3488 paths.push_back (Glib::filename_from_uri (*i));
3496 Editor::new_tempo_section ()
3501 Editor::map_transport_state ()
3503 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state);
3505 if (_session && _session->transport_stopped()) {
3506 have_pending_keyboard_selection = false;
3509 update_loop_range_view ();
3513 Editor::transport_looped ()
3515 /* reset Playhead position interpolation.
3516 * see Editor::super_rapid_screen_update
3518 _last_update_time = 0;
3524 Editor::begin_selection_op_history ()
3526 selection_op_cmd_depth = 0;
3527 selection_op_history_it = 0;
3529 while(!selection_op_history.empty()) {
3530 delete selection_op_history.front();
3531 selection_op_history.pop_front();
3534 selection_undo_action->set_sensitive (false);
3535 selection_redo_action->set_sensitive (false);
3536 selection_op_history.push_front (&_selection_memento->get_state ());
3540 Editor::begin_reversible_selection_op (string name)
3543 //cerr << name << endl;
3544 /* begin/commit pairs can be nested */
3545 selection_op_cmd_depth++;
3550 Editor::commit_reversible_selection_op ()
3553 if (selection_op_cmd_depth == 1) {
3555 if (selection_op_history_it > 0 && selection_op_history_it < selection_op_history.size()) {
3556 /* The user has undone some selection ops and then made a new one,
3557 * making anything earlier in the list invalid.
3560 list<XMLNode *>::iterator it = selection_op_history.begin();
3561 list<XMLNode *>::iterator e_it = it;
3562 advance (e_it, selection_op_history_it);
3564 for (; it != e_it; ++it) {
3567 selection_op_history.erase (selection_op_history.begin(), e_it);
3570 selection_op_history.push_front (&_selection_memento->get_state ());
3571 selection_op_history_it = 0;
3573 selection_undo_action->set_sensitive (true);
3574 selection_redo_action->set_sensitive (false);
3577 if (selection_op_cmd_depth > 0) {
3578 selection_op_cmd_depth--;
3584 Editor::undo_selection_op ()
3587 selection_op_history_it++;
3589 for (std::list<XMLNode *>::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) {
3590 if (n == selection_op_history_it) {
3591 _selection_memento->set_state (*(*i), Stateful::current_state_version);
3592 selection_redo_action->set_sensitive (true);
3596 /* is there an earlier entry? */
3597 if ((selection_op_history_it + 1) >= selection_op_history.size()) {
3598 selection_undo_action->set_sensitive (false);
3604 Editor::redo_selection_op ()
3607 if (selection_op_history_it > 0) {
3608 selection_op_history_it--;
3611 for (std::list<XMLNode *>::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) {
3612 if (n == selection_op_history_it) {
3613 _selection_memento->set_state (*(*i), Stateful::current_state_version);
3614 selection_undo_action->set_sensitive (true);
3619 if (selection_op_history_it == 0) {
3620 selection_redo_action->set_sensitive (false);
3626 Editor::begin_reversible_command (string name)
3629 before.push_back (&_selection_memento->get_state ());
3630 _session->begin_reversible_command (name);
3635 Editor::begin_reversible_command (GQuark q)
3638 before.push_back (&_selection_memento->get_state ());
3639 _session->begin_reversible_command (q);
3644 Editor::abort_reversible_command ()
3647 while(!before.empty()) {
3648 delete before.front();
3651 _session->abort_reversible_command ();
3656 Editor::commit_reversible_command ()
3659 if (before.size() == 1) {
3660 _session->add_command (new MementoCommand<SelectionMemento>(*(_selection_memento), before.front(), &_selection_memento->get_state ()));
3661 redo_action->set_sensitive(false);
3662 undo_action->set_sensitive(true);
3663 begin_selection_op_history ();
3666 if (before.empty()) {
3667 cerr << "Please call begin_reversible_command() before commit_reversible_command()." << endl;
3672 _session->commit_reversible_command ();
3677 Editor::history_changed ()
3681 if (undo_action && _session) {
3682 if (_session->undo_depth() == 0) {
3683 label = S_("Command|Undo");
3685 label = string_compose(S_("Command|Undo (%1)"), _session->next_undo());
3687 undo_action->property_label() = label;
3690 if (redo_action && _session) {
3691 if (_session->redo_depth() == 0) {
3693 redo_action->set_sensitive (false);
3695 label = string_compose(_("Redo (%1)"), _session->next_redo());
3696 redo_action->set_sensitive (true);
3698 redo_action->property_label() = label;
3703 Editor::duplicate_range (bool with_dialog)
3707 RegionSelection rs = get_regions_from_selection_and_entered ();
3709 if (selection->time.length() == 0 && rs.empty()) {
3715 ArdourDialog win (_("Duplicate"));
3716 Label label (_("Number of duplications:"));
3717 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3718 SpinButton spinner (adjustment, 0.0, 1);
3721 win.get_vbox()->set_spacing (12);
3722 win.get_vbox()->pack_start (hbox);
3723 hbox.set_border_width (6);
3724 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3726 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3727 place, visually. so do this by hand.
3730 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3731 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3732 spinner.grab_focus();
3738 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3739 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3740 win.set_default_response (RESPONSE_ACCEPT);
3742 spinner.grab_focus ();
3744 switch (win.run ()) {
3745 case RESPONSE_ACCEPT:
3751 times = adjustment.get_value();
3754 if ((current_mouse_mode() == MouseRange)) {
3755 if (selection->time.length()) {
3756 duplicate_selection (times);
3758 } else if (get_smart_mode()) {
3759 if (selection->time.length()) {
3760 duplicate_selection (times);
3762 duplicate_some_regions (rs, times);
3764 duplicate_some_regions (rs, times);
3769 Editor::set_edit_mode (EditMode m)
3771 Config->set_edit_mode (m);
3775 Editor::cycle_edit_mode ()
3777 switch (Config->get_edit_mode()) {
3779 Config->set_edit_mode (Ripple);
3783 Config->set_edit_mode (Lock);
3786 Config->set_edit_mode (Slide);
3792 Editor::edit_mode_selection_done (EditMode m)
3794 Config->set_edit_mode (m);
3798 Editor::grid_type_selection_done (GridType gridtype)
3800 RefPtr<RadioAction> ract = grid_type_action (gridtype);
3802 ract->set_active ();
3807 Editor::snap_mode_selection_done (SnapMode mode)
3809 RefPtr<RadioAction> ract = snap_mode_action (mode);
3812 ract->set_active (true);
3817 Editor::cycle_edit_point (bool with_marker)
3819 if(Profile->get_mixbus())
3820 with_marker = false;
3822 switch (_edit_point) {
3824 set_edit_point_preference (EditAtPlayhead);
3826 case EditAtPlayhead:
3828 set_edit_point_preference (EditAtSelectedMarker);
3830 set_edit_point_preference (EditAtMouse);
3833 case EditAtSelectedMarker:
3834 set_edit_point_preference (EditAtMouse);
3840 Editor::edit_point_selection_done (EditPoint ep)
3842 set_edit_point_preference (ep);
3846 Editor::build_zoom_focus_menu ()
3848 using namespace Menu_Helpers;
3850 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusLeft], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusLeft)));
3851 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusRight], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusRight)));
3852 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusCenter], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusCenter)));
3853 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusPlayhead], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusPlayhead)));
3854 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusMouse], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusMouse)));
3855 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusEdit], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusEdit)));
3857 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_TRIANGLE_WIDTH, 2);
3861 Editor::zoom_focus_selection_done (ZoomFocus f)
3863 RefPtr<RadioAction> ract = zoom_focus_action (f);
3865 ract->set_active ();
3870 Editor::build_track_count_menu ()
3872 using namespace Menu_Helpers;
3874 if (!Profile->get_mixbus()) {
3875 visible_tracks_selector.AddMenuElem (MenuElem (X_("1"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 1)));
3876 visible_tracks_selector.AddMenuElem (MenuElem (X_("2"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 2)));
3877 visible_tracks_selector.AddMenuElem (MenuElem (X_("3"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 3)));
3878 visible_tracks_selector.AddMenuElem (MenuElem (X_("4"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 4)));
3879 visible_tracks_selector.AddMenuElem (MenuElem (X_("8"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 8)));
3880 visible_tracks_selector.AddMenuElem (MenuElem (X_("12"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 12)));
3881 visible_tracks_selector.AddMenuElem (MenuElem (X_("16"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 16)));
3882 visible_tracks_selector.AddMenuElem (MenuElem (X_("20"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 20)));
3883 visible_tracks_selector.AddMenuElem (MenuElem (X_("24"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 24)));
3884 visible_tracks_selector.AddMenuElem (MenuElem (X_("32"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 32)));
3885 visible_tracks_selector.AddMenuElem (MenuElem (X_("64"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 64)));
3886 visible_tracks_selector.AddMenuElem (MenuElem (_("Selection"), sigc::mem_fun(*this, &Editor::fit_selection)));
3887 visible_tracks_selector.AddMenuElem (MenuElem (_("All"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 0)));
3889 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 1 track"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 1)));
3890 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 2 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 2)));
3891 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 4 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 4)));
3892 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 8 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 8)));
3893 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 16 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 16)));
3894 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 24 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 24)));
3895 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 32 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 32)));
3896 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 48 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 48)));
3897 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit All tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 0)));
3898 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit Selection"), sigc::mem_fun(*this, &Editor::fit_selection)));
3900 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 10 ms"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 10)));
3901 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 100 ms"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 100)));
3902 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 1 sec"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 1 * 1000)));
3903 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 10 sec"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 10 * 1000)));
3904 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 1 min"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 60 * 1000)));
3905 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 10 min"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 10 * 60 * 1000)));
3906 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 1 hour"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 60 * 60 * 1000)));
3907 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 8 hours"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 8 * 60 * 60 * 1000)));
3908 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 24 hours"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 24 * 60 * 60 * 1000)));
3909 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Session"), sigc::mem_fun(*this, &Editor::temporal_zoom_session)));
3910 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Extents"), sigc::mem_fun(*this, &Editor::temporal_zoom_extents)));
3911 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Range/Region Selection"), sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_selection), Horizontal)));
3916 Editor::set_zoom_preset (int64_t ms)
3919 temporal_zoom_session();
3923 ARDOUR::samplecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
3924 temporal_zoom ((sample_rate * ms / 1000) / _visible_canvas_width);
3928 Editor::set_visible_track_count (int32_t n)
3930 _visible_track_count = n;
3932 /* if the canvas hasn't really been allocated any size yet, just
3933 record the desired number of visible tracks and return. when canvas
3934 allocation happens, we will get called again and then we can do the
3938 if (_visible_canvas_height <= 1) {
3944 DisplaySuspender ds;
3946 if (_visible_track_count > 0) {
3947 h = trackviews_height() / _visible_track_count;
3948 std::ostringstream s;
3949 s << _visible_track_count;
3951 } else if (_visible_track_count == 0) {
3953 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
3954 if ((*i)->marked_for_display()) {
3956 TimeAxisView::Children cl ((*i)->get_child_list ());
3957 for (TimeAxisView::Children::const_iterator j = cl.begin(); j != cl.end(); ++j) {
3958 if ((*j)->marked_for_display()) {
3965 visible_tracks_selector.set_text (X_("*"));
3968 h = trackviews_height() / n;
3971 /* negative value means that the visible track count has
3972 been overridden by explicit track height changes.
3974 visible_tracks_selector.set_text (X_("*"));
3978 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3979 (*i)->set_height (h, TimeAxisView::HeightPerLane);
3982 if (str != visible_tracks_selector.get_text()) {
3983 visible_tracks_selector.set_text (str);
3988 Editor::override_visible_track_count ()
3990 _visible_track_count = -1;
3991 visible_tracks_selector.set_text (_("*"));
3995 Editor::edit_controls_button_release (GdkEventButton* ev)
3997 if (Keyboard::is_context_menu_event (ev)) {
3998 ARDOUR_UI::instance()->add_route ();
3999 } else if (ev->button == 1) {
4000 selection->clear_tracks ();
4007 Editor::mouse_select_button_release (GdkEventButton* ev)
4009 /* this handles just right-clicks */
4011 if (ev->button != 3) {
4019 Editor::set_zoom_focus (ZoomFocus f)
4021 string str = zoom_focus_strings[(int)f];
4023 if (str != zoom_focus_selector.get_text()) {
4024 zoom_focus_selector.set_text (str);
4027 if (zoom_focus != f) {
4034 Editor::cycle_zoom_focus ()
4036 switch (zoom_focus) {
4038 set_zoom_focus (ZoomFocusRight);
4040 case ZoomFocusRight:
4041 set_zoom_focus (ZoomFocusCenter);
4043 case ZoomFocusCenter:
4044 set_zoom_focus (ZoomFocusPlayhead);
4046 case ZoomFocusPlayhead:
4047 set_zoom_focus (ZoomFocusMouse);
4049 case ZoomFocusMouse:
4050 set_zoom_focus (ZoomFocusEdit);
4053 set_zoom_focus (ZoomFocusLeft);
4059 Editor::update_grid ()
4061 if (grid_musical()) {
4062 std::vector<TempoMap::BBTPoint> grid;
4063 if (bbt_ruler_scale != bbt_show_many) {
4064 compute_current_bbt_points (grid, _leftmost_sample, _leftmost_sample + current_page_samples());
4066 maybe_draw_grid_lines ();
4067 } else if (grid_nonmusical()) {
4068 maybe_draw_grid_lines ();
4075 Editor::toggle_follow_playhead ()
4077 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
4079 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
4080 set_follow_playhead (tact->get_active());
4084 /** @param yn true to follow playhead, otherwise false.
4085 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
4088 Editor::set_follow_playhead (bool yn, bool catch_up)
4090 if (_follow_playhead != yn) {
4091 if ((_follow_playhead = yn) == true && catch_up) {
4093 reset_x_origin_to_follow_playhead ();
4100 Editor::toggle_stationary_playhead ()
4102 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
4104 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
4105 set_stationary_playhead (tact->get_active());
4110 Editor::set_stationary_playhead (bool yn)
4112 if (_stationary_playhead != yn) {
4113 if ((_stationary_playhead = yn) == true) {
4114 /* catch up -- FIXME need a 3.0 equivalent of this 2.X call */
4115 // update_current_screen ();
4122 Editor::playlist_selector () const
4124 return *_playlist_selector;
4128 Editor::get_paste_offset (samplepos_t pos, unsigned paste_count, samplecnt_t duration)
4130 if (paste_count == 0) {
4131 /* don't bother calculating an offset that will be zero anyway */
4135 /* calculate basic unsnapped multi-paste offset */
4136 samplecnt_t offset = paste_count * duration;
4138 /* snap offset so pos + offset is aligned to the grid */
4139 MusicSample offset_pos (pos + offset, 0);
4140 snap_to(offset_pos, RoundUpMaybe);
4141 offset = offset_pos.sample - pos;
4147 Editor::get_grid_beat_divisions(samplepos_t position)
4149 switch (_grid_type) {
4150 case GridTypeBeatDiv32: return 32;
4151 case GridTypeBeatDiv28: return 28;
4152 case GridTypeBeatDiv24: return 24;
4153 case GridTypeBeatDiv20: return 20;
4154 case GridTypeBeatDiv16: return 16;
4155 case GridTypeBeatDiv14: return 14;
4156 case GridTypeBeatDiv12: return 12;
4157 case GridTypeBeatDiv10: return 10;
4158 case GridTypeBeatDiv8: return 8;
4159 case GridTypeBeatDiv7: return 7;
4160 case GridTypeBeatDiv6: return 6;
4161 case GridTypeBeatDiv5: return 5;
4162 case GridTypeBeatDiv4: return 4;
4163 case GridTypeBeatDiv3: return 3;
4164 case GridTypeBeatDiv2: return 2;
4165 case GridTypeBeat: return 1;
4166 case GridTypeBar: return 1;
4168 case GridTypeNone: return 0;
4169 case GridTypeTimecode: return 0;
4170 case GridTypeMinSec: return 0;
4171 case GridTypeCDFrame: return 0;
4177 /** returns the current musical grid divisiions using the supplied modifier mask from a GtkEvent.
4178 if the grid is non-musical, returns 0.
4179 if the grid is snapped to bars, returns -1.
4180 @param event_state the current keyboard modifier mask.
4183 Editor::get_grid_music_divisions (uint32_t event_state)
4185 if (snap_mode() == SnapOff && !ArdourKeyboard::indicates_snap (event_state)) {
4189 if (snap_mode() != SnapOff && ArdourKeyboard::indicates_snap (event_state)) {
4193 switch (_grid_type) {
4194 case GridTypeBeatDiv32: return 32;
4195 case GridTypeBeatDiv28: return 28;
4196 case GridTypeBeatDiv24: return 24;
4197 case GridTypeBeatDiv20: return 20;
4198 case GridTypeBeatDiv16: return 16;
4199 case GridTypeBeatDiv14: return 14;
4200 case GridTypeBeatDiv12: return 12;
4201 case GridTypeBeatDiv10: return 10;
4202 case GridTypeBeatDiv8: return 8;
4203 case GridTypeBeatDiv7: return 7;
4204 case GridTypeBeatDiv6: return 6;
4205 case GridTypeBeatDiv5: return 5;
4206 case GridTypeBeatDiv4: return 4;
4207 case GridTypeBeatDiv3: return 3;
4208 case GridTypeBeatDiv2: return 2;
4209 case GridTypeBeat: return 1;
4210 case GridTypeBar : return -1;
4212 case GridTypeNone: return 0;
4213 case GridTypeTimecode: return 0;
4214 case GridTypeMinSec: return 0;
4215 case GridTypeCDFrame: return 0;
4221 Editor::get_grid_type_as_beats (bool& success, samplepos_t position)
4225 const unsigned divisions = get_grid_beat_divisions(position);
4227 return Temporal::Beats(1.0 / (double)get_grid_beat_divisions(position));
4230 switch (_grid_type) {
4232 return Temporal::Beats(4.0 / _session->tempo_map().meter_at_sample (position).note_divisor());
4235 const Meter& m = _session->tempo_map().meter_at_sample (position);
4236 return Temporal::Beats((4.0 * m.divisions_per_bar()) / m.note_divisor());
4244 return Temporal::Beats();
4248 Editor::get_nudge_distance (samplepos_t pos, samplecnt_t& next)
4252 ret = nudge_clock->current_duration (pos);
4253 next = ret + 1; /* XXXX fix me */
4259 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
4261 ArdourDialog dialog (_("Playlist Deletion"));
4262 Label label (string_compose (_("Playlist %1 is currently unused.\n"
4263 "If it is kept, its audio files will not be cleaned.\n"
4264 "If it is deleted, audio files used by it alone will be cleaned."),
4267 dialog.set_position (WIN_POS_CENTER);
4268 dialog.get_vbox()->pack_start (label);
4272 dialog.add_button (_("Delete All Unused"), RESPONSE_YES); // needs clarification. this and all remaining ones
4273 dialog.add_button (_("Delete Playlist"), RESPONSE_ACCEPT);
4274 Button* keep = dialog.add_button (_("Keep Playlist"), RESPONSE_REJECT);
4275 dialog.add_button (_("Keep Remaining"), RESPONSE_NO); // ditto
4276 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
4278 /* by default gtk uses the left most button */
4279 keep->grab_focus ();
4281 switch (dialog.run ()) {
4283 /* keep this and all remaining ones */
4288 /* delete this and all others */
4292 case RESPONSE_ACCEPT:
4293 /* delete the playlist */
4297 case RESPONSE_REJECT:
4298 /* keep the playlist */
4310 Editor::audio_region_selection_covers (samplepos_t where)
4312 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
4313 if ((*a)->region()->covers (where)) {
4322 Editor::prepare_for_cleanup ()
4324 cut_buffer->clear_regions ();
4325 cut_buffer->clear_playlists ();
4327 selection->clear_regions ();
4328 selection->clear_playlists ();
4330 _regions->suspend_redisplay ();
4334 Editor::finish_cleanup ()
4336 _regions->resume_redisplay ();
4340 Editor::transport_loop_location()
4343 return _session->locations()->auto_loop_location();
4350 Editor::transport_punch_location()
4353 return _session->locations()->auto_punch_location();
4360 Editor::control_layout_scroll (GdkEventScroll* ev)
4362 /* Just forward to the normal canvas scroll method. The coordinate
4363 systems are different but since the canvas is always larger than the
4364 track headers, and aligned with the trackview area, this will work.
4366 In the not too distant future this layout is going away anyway and
4367 headers will be on the canvas.
4369 return canvas_scroll_event (ev, false);
4373 Editor::session_state_saved (string)
4376 _snapshots->redisplay ();
4380 Editor::maximise_editing_space ()
4386 Gtk::Window* toplevel = current_toplevel();
4389 toplevel->fullscreen ();
4395 Editor::restore_editing_space ()
4401 Gtk::Window* toplevel = current_toplevel();
4404 toplevel->unfullscreen();
4410 * Make new playlists for a given track and also any others that belong
4411 * to the same active route group with the `select' property.
4416 Editor::new_playlists (TimeAxisView* v)
4418 begin_reversible_command (_("new playlists"));
4419 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4420 _session->playlists->get (playlists);
4421 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::group_select.property_id);
4422 commit_reversible_command ();
4426 * Use a copy of the current playlist for a given track and also any others that belong
4427 * to the same active route group with the `select' property.
4432 Editor::copy_playlists (TimeAxisView* v)
4434 begin_reversible_command (_("copy playlists"));
4435 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4436 _session->playlists->get (playlists);
4437 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::group_select.property_id);
4438 commit_reversible_command ();
4441 /** Clear the current playlist for a given track and also any others that belong
4442 * to the same active route group with the `select' property.
4447 Editor::clear_playlists (TimeAxisView* v)
4449 begin_reversible_command (_("clear playlists"));
4450 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4451 _session->playlists->get (playlists);
4452 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::group_select.property_id);
4453 commit_reversible_command ();
4457 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4459 atv.use_new_playlist (sz > 1 ? false : true, playlists, false);
4463 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4465 atv.use_new_playlist (sz > 1 ? false : true, playlists, true);
4469 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4471 atv.clear_playlist ();
4475 Editor::get_y_origin () const
4477 return vertical_adjustment.get_value ();
4480 /** Queue up a change to the viewport x origin.
4481 * @param sample New x origin.
4484 Editor::reset_x_origin (samplepos_t sample)
4486 pending_visual_change.add (VisualChange::TimeOrigin);
4487 pending_visual_change.time_origin = sample;
4488 ensure_visual_change_idle_handler ();
4492 Editor::reset_y_origin (double y)
4494 pending_visual_change.add (VisualChange::YOrigin);
4495 pending_visual_change.y_origin = y;
4496 ensure_visual_change_idle_handler ();
4500 Editor::reset_zoom (samplecnt_t spp)
4502 if (spp == samples_per_pixel) {
4506 pending_visual_change.add (VisualChange::ZoomLevel);
4507 pending_visual_change.samples_per_pixel = spp;
4508 ensure_visual_change_idle_handler ();
4512 Editor::reposition_and_zoom (samplepos_t sample, double fpu)
4514 reset_x_origin (sample);
4517 if (!no_save_visual) {
4518 undo_visual_stack.push_back (current_visual_state(false));
4522 Editor::VisualState::VisualState (bool with_tracks)
4523 : gui_state (with_tracks ? new GUIObjectState : 0)
4527 Editor::VisualState::~VisualState ()
4532 Editor::VisualState*
4533 Editor::current_visual_state (bool with_tracks)
4535 VisualState* vs = new VisualState (with_tracks);
4536 vs->y_position = vertical_adjustment.get_value();
4537 vs->samples_per_pixel = samples_per_pixel;
4538 vs->_leftmost_sample = _leftmost_sample;
4539 vs->zoom_focus = zoom_focus;
4542 vs->gui_state->set_state (ARDOUR_UI::instance()->gui_object_state->get_state());
4549 Editor::undo_visual_state ()
4551 if (undo_visual_stack.empty()) {
4555 VisualState* vs = undo_visual_stack.back();
4556 undo_visual_stack.pop_back();
4559 redo_visual_stack.push_back (current_visual_state (vs ? vs->gui_state != 0 : false));
4562 use_visual_state (*vs);
4567 Editor::redo_visual_state ()
4569 if (redo_visual_stack.empty()) {
4573 VisualState* vs = redo_visual_stack.back();
4574 redo_visual_stack.pop_back();
4576 /* XXX: can 'vs' really be 0? Is there a place that puts NULL pointers onto the stack? */
4577 undo_visual_stack.push_back (current_visual_state (vs ? (vs->gui_state != 0) : false));
4580 use_visual_state (*vs);
4585 Editor::swap_visual_state ()
4587 if (undo_visual_stack.empty()) {
4588 redo_visual_state ();
4590 undo_visual_state ();
4595 Editor::use_visual_state (VisualState& vs)
4597 PBD::Unwinder<bool> nsv (no_save_visual, true);
4598 DisplaySuspender ds;
4600 vertical_adjustment.set_value (vs.y_position);
4602 set_zoom_focus (vs.zoom_focus);
4603 reposition_and_zoom (vs._leftmost_sample, vs.samples_per_pixel);
4606 ARDOUR_UI::instance()->gui_object_state->set_state (vs.gui_state->get_state());
4608 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4609 (*i)->clear_property_cache();
4610 (*i)->reset_visual_state ();
4614 _routes->update_visibility ();
4617 /** This is the core function that controls the zoom level of the canvas. It is called
4618 * whenever one or more calls are made to reset_zoom(). It executes in an idle handler.
4619 * @param spp new number of samples per pixel
4622 Editor::set_samples_per_pixel (samplecnt_t spp)
4628 const samplecnt_t three_days = 3 * 24 * 60 * 60 * (_session ? _session->sample_rate() : 48000);
4629 const samplecnt_t lots_of_pixels = 4000;
4631 /* if the zoom level is greater than what you'd get trying to display 3
4632 * days of audio on a really big screen, then it's too big.
4635 if (spp * lots_of_pixels > three_days) {
4639 samples_per_pixel = spp;
4643 Editor::on_samples_per_pixel_changed ()
4645 bool const showing_time_selection = selection->time.length() > 0;
4647 if (showing_time_selection && selection->time.start () != selection->time.end_sample ()) {
4648 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4649 (*i)->reshow_selection (selection->time);
4653 ZoomChanged (); /* EMIT_SIGNAL */
4655 ArdourCanvas::GtkCanvasViewport* c;
4657 c = get_track_canvas();
4659 c->canvas()->zoomed ();
4662 if (playhead_cursor) {
4663 playhead_cursor->set_position (playhead_cursor->current_sample ());
4666 refresh_location_display();
4667 _summary->set_overlays_dirty ();
4669 update_marker_labels ();
4675 Editor::playhead_cursor_sample () const
4677 return playhead_cursor->current_sample();
4681 Editor::queue_visual_videotimeline_update ()
4683 pending_visual_change.add (VisualChange::VideoTimeline);
4684 ensure_visual_change_idle_handler ();
4688 Editor::ensure_visual_change_idle_handler ()
4690 if (pending_visual_change.idle_handler_id < 0) {
4691 /* see comment in add_to_idle_resize above. */
4692 pending_visual_change.idle_handler_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, _idle_visual_changer, this, NULL);
4693 pending_visual_change.being_handled = false;
4698 Editor::_idle_visual_changer (void* arg)
4700 return static_cast<Editor*>(arg)->idle_visual_changer ();
4704 Editor::pre_render ()
4706 visual_change_queued = false;
4708 if (pending_visual_change.pending != 0) {
4709 ensure_visual_change_idle_handler();
4714 Editor::idle_visual_changer ()
4716 pending_visual_change.idle_handler_id = -1;
4718 if (pending_visual_change.pending == 0) {
4722 /* set_horizontal_position() below (and maybe other calls) call
4723 gtk_main_iteration(), so it's possible that a signal will be handled
4724 half-way through this method. If this signal wants an
4725 idle_visual_changer we must schedule another one after this one, so
4726 mark the idle_handler_id as -1 here to allow that. Also make a note
4727 that we are doing the visual change, so that changes in response to
4728 super-rapid-screen-update can be dropped if we are still processing
4732 if (visual_change_queued) {
4736 pending_visual_change.being_handled = true;
4738 VisualChange vc = pending_visual_change;
4740 pending_visual_change.pending = (VisualChange::Type) 0;
4742 visual_changer (vc);
4744 pending_visual_change.being_handled = false;
4746 visual_change_queued = true;
4748 return 0; /* this is always a one-shot call */
4752 Editor::visual_changer (const VisualChange& vc)
4755 * Changed first so the correct horizontal canvas position is calculated in
4756 * Editor::set_horizontal_position
4758 if (vc.pending & VisualChange::ZoomLevel) {
4759 set_samples_per_pixel (vc.samples_per_pixel);
4762 if (vc.pending & VisualChange::TimeOrigin) {
4763 double new_time_origin = sample_to_pixel_unrounded (vc.time_origin);
4764 set_horizontal_position (new_time_origin);
4767 if (vc.pending & VisualChange::YOrigin) {
4768 vertical_adjustment.set_value (vc.y_origin);
4772 * Now the canvas is in the final state before render the canvas items that
4773 * support the Item::prepare_for_render interface can calculate the correct
4774 * item to visible canvas intersection.
4776 if (vc.pending & VisualChange::ZoomLevel) {
4777 on_samples_per_pixel_changed ();
4779 compute_fixed_ruler_scale ();
4781 compute_bbt_ruler_scale (vc.time_origin, pending_visual_change.time_origin + current_page_samples());
4782 update_tempo_based_rulers ();
4785 if (!(vc.pending & VisualChange::ZoomLevel)) {
4786 /* If the canvas is not being zoomed then the canvas items will not change
4787 * and cause Item::prepare_for_render to be called so do it here manually.
4788 * Not ideal, but I can't think of a better solution atm.
4790 _track_canvas->prepare_for_render();
4793 /* If we are only scrolling vertically there is no need to update these */
4794 if (vc.pending != VisualChange::YOrigin) {
4795 update_fixed_rulers ();
4796 redisplay_grid (true);
4798 /* video frames & position need to be updated for zoom, horiz-scroll
4799 * and (explicitly) VisualChange::VideoTimeline.
4801 update_video_timeline();
4804 _summary->set_overlays_dirty ();
4807 struct EditorOrderTimeAxisSorter {
4808 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4809 return a->order () < b->order ();
4814 Editor::sort_track_selection (TrackViewList& sel)
4816 EditorOrderTimeAxisSorter cmp;
4821 Editor::get_preferred_edit_position (EditIgnoreOption ignore, bool from_context_menu, bool from_outside_canvas)
4824 samplepos_t where = 0;
4825 EditPoint ep = _edit_point;
4827 if (Profile->get_mixbus()) {
4828 if (ep == EditAtSelectedMarker) {
4829 ep = EditAtPlayhead;
4833 if (from_outside_canvas && (ep == EditAtMouse)) {
4834 ep = EditAtPlayhead;
4835 } else if (from_context_menu && (ep == EditAtMouse)) {
4836 return canvas_event_sample (&context_click_event, 0, 0);
4839 if (entered_marker) {
4840 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4841 return entered_marker->position();
4844 if ((ignore == EDIT_IGNORE_PHEAD) && ep == EditAtPlayhead) {
4845 ep = EditAtSelectedMarker;
4848 if ((ignore == EDIT_IGNORE_MOUSE) && ep == EditAtMouse) {
4849 ep = EditAtPlayhead;
4852 MusicSample snap_mf (0, 0);
4855 case EditAtPlayhead:
4856 if (_dragging_playhead) {
4857 /* NOTE: since the user is dragging with the mouse, this operation will implicitly be Snapped */
4858 where = playhead_cursor->current_sample();
4860 where = _session->audible_sample();
4862 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4865 case EditAtSelectedMarker:
4866 if (!selection->markers.empty()) {
4868 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4871 where = loc->start();
4875 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4883 if (!mouse_sample (where, ignored)) {
4884 /* XXX not right but what can we do ? */
4887 snap_mf.sample = where;
4889 where = snap_mf.sample;
4890 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4898 Editor::set_loop_range (samplepos_t start, samplepos_t end, string cmd)
4900 if (!_session) return;
4902 begin_reversible_command (cmd);
4906 if ((tll = transport_loop_location()) == 0) {
4907 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop, get_grid_music_divisions(0));
4908 XMLNode &before = _session->locations()->get_state();
4909 _session->locations()->add (loc, true);
4910 _session->set_auto_loop_location (loc);
4911 XMLNode &after = _session->locations()->get_state();
4912 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4914 XMLNode &before = tll->get_state();
4915 tll->set_hidden (false, this);
4916 tll->set (start, end);
4917 XMLNode &after = tll->get_state();
4918 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4921 commit_reversible_command ();
4925 Editor::set_punch_range (samplepos_t start, samplepos_t end, string cmd)
4927 if (!_session) return;
4929 begin_reversible_command (cmd);
4933 if ((tpl = transport_punch_location()) == 0) {
4934 Location* loc = new Location (*_session, start, end, _("Punch"), Location::IsAutoPunch, get_grid_music_divisions(0));
4935 XMLNode &before = _session->locations()->get_state();
4936 _session->locations()->add (loc, true);
4937 _session->set_auto_punch_location (loc);
4938 XMLNode &after = _session->locations()->get_state();
4939 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4941 XMLNode &before = tpl->get_state();
4942 tpl->set_hidden (false, this);
4943 tpl->set (start, end);
4944 XMLNode &after = tpl->get_state();
4945 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4948 commit_reversible_command ();
4951 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4952 * @param rs List to which found regions are added.
4953 * @param where Time to look at.
4954 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4957 Editor::get_regions_at (RegionSelection& rs, samplepos_t where, const TrackViewList& ts) const
4959 const TrackViewList* tracks;
4962 tracks = &track_views;
4967 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4969 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4972 boost::shared_ptr<Track> tr;
4973 boost::shared_ptr<Playlist> pl;
4975 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4977 boost::shared_ptr<RegionList> regions = pl->regions_at (where);
4979 for (RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4980 RegionView* rv = rtv->view()->find_view (*i);
4991 Editor::get_regions_after (RegionSelection& rs, samplepos_t where, const TrackViewList& ts) const
4993 const TrackViewList* tracks;
4996 tracks = &track_views;
5001 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
5002 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
5004 boost::shared_ptr<Track> tr;
5005 boost::shared_ptr<Playlist> pl;
5007 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
5009 boost::shared_ptr<RegionList> regions = pl->regions_touched (where, max_samplepos);
5011 for (RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
5013 RegionView* rv = rtv->view()->find_view (*i);
5024 /** Get regions using the following method:
5026 * Make a region list using:
5027 * (a) any selected regions
5028 * (b) the intersection of any selected tracks and the edit point(*)
5029 * (c) if neither exists, and edit_point == mouse, then whatever region is under the mouse
5031 * (*) NOTE: in this case, if 'No Selection = All Tracks' is active, search all tracks
5033 * Note that we have forced the rule that selected regions and selected tracks are mutually exclusive
5037 Editor::get_regions_from_selection_and_edit_point (EditIgnoreOption ignore, bool from_context_menu, bool from_outside_canvas)
5039 RegionSelection regions;
5041 if (_edit_point == EditAtMouse && entered_regionview && selection->tracks.empty() && selection->regions.empty()) {
5042 regions.add (entered_regionview);
5044 regions = selection->regions;
5047 if (regions.empty()) {
5048 TrackViewList tracks = selection->tracks;
5050 if (!tracks.empty()) {
5051 /* no region selected or entered, but some selected tracks:
5052 * act on all regions on the selected tracks at the edit point
5054 samplepos_t const where = get_preferred_edit_position (ignore, from_context_menu, from_outside_canvas);
5055 get_regions_at(regions, where, tracks);
5062 /** Get regions using the following method:
5064 * Make a region list using:
5065 * (a) any selected regions
5066 * (b) the intersection of any selected tracks and the edit point(*)
5067 * (c) if neither exists, then whatever region is under the mouse
5069 * (*) NOTE: in this case, if 'No Selection = All Tracks' is active, search all tracks
5071 * Note that we have forced the rule that selected regions and selected tracks are mutually exclusive
5074 Editor::get_regions_from_selection_and_mouse (samplepos_t pos)
5076 RegionSelection regions;
5078 if (entered_regionview && selection->tracks.empty() && selection->regions.empty()) {
5079 regions.add (entered_regionview);
5081 regions = selection->regions;
5084 if (regions.empty()) {
5085 TrackViewList tracks = selection->tracks;
5087 if (!tracks.empty()) {
5088 /* no region selected or entered, but some selected tracks:
5089 * act on all regions on the selected tracks at the edit point
5091 get_regions_at(regions, pos, tracks);
5098 /** Start with regions that are selected, or the entered regionview if none are selected.
5099 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
5100 * of the regions that we started with.
5104 Editor::get_regions_from_selection_and_entered () const
5106 RegionSelection regions = selection->regions;
5108 if (regions.empty() && entered_regionview) {
5109 regions.add (entered_regionview);
5116 Editor::get_regionviews_by_id (PBD::ID const id, RegionSelection & regions) const
5118 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5119 RouteTimeAxisView* rtav;
5121 if ((rtav = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5122 boost::shared_ptr<Playlist> pl;
5123 std::vector<boost::shared_ptr<Region> > results;
5124 boost::shared_ptr<Track> tr;
5126 if ((tr = rtav->track()) == 0) {
5131 if ((pl = (tr->playlist())) != 0) {
5132 boost::shared_ptr<Region> r = pl->region_by_id (id);
5134 RegionView* rv = rtav->view()->find_view (r);
5136 regions.push_back (rv);
5145 Editor::get_per_region_note_selection (list<pair<PBD::ID, set<boost::shared_ptr<Evoral::Note<Temporal::Beats> > > > > &selection) const
5148 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5149 MidiTimeAxisView* mtav;
5151 if ((mtav = dynamic_cast<MidiTimeAxisView*> (*i)) != 0) {
5153 mtav->get_per_region_note_selection (selection);
5160 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions, bool src_comparison)
5162 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5164 RouteTimeAxisView* tatv;
5166 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5168 boost::shared_ptr<Playlist> pl;
5169 vector<boost::shared_ptr<Region> > results;
5171 boost::shared_ptr<Track> tr;
5173 if ((tr = tatv->track()) == 0) {
5178 if ((pl = (tr->playlist())) != 0) {
5179 if (src_comparison) {
5180 pl->get_source_equivalent_regions (region, results);
5182 pl->get_region_list_equivalent_regions (region, results);
5186 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
5187 if ((marv = tatv->view()->find_view (*ir)) != 0) {
5188 regions.push_back (marv);
5197 Editor::regionview_from_region (boost::shared_ptr<Region> region) const
5199 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5200 RouteTimeAxisView* tatv;
5201 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5202 if (!tatv->track()) {
5205 RegionView* marv = tatv->view()->find_view (region);
5215 Editor::rtav_from_route (boost::shared_ptr<Route> route) const
5217 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5218 RouteTimeAxisView* rtav;
5219 if ((rtav = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5220 if (rtav->route() == route) {
5229 Editor::show_rhythm_ferret ()
5231 if (rhythm_ferret == 0) {
5232 rhythm_ferret = new RhythmFerret(*this);
5235 rhythm_ferret->set_session (_session);
5236 rhythm_ferret->show ();
5237 rhythm_ferret->present ();
5241 Editor::first_idle ()
5243 MessageDialog* dialog = 0;
5245 if (track_views.size() > 1) {
5246 Timers::TimerSuspender t;
5247 dialog = new MessageDialog (
5248 string_compose (_("Please wait while %1 loads visual data."), PROGRAM_NAME),
5252 ARDOUR_UI::instance()->flush_pending (60);
5255 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
5259 /* now that all regionviews should exist, setup region selection */
5263 for (list<PBD::ID>::iterator pr = selection->regions.pending.begin (); pr != selection->regions.pending.end (); ++pr) {
5264 /* this is cumulative: rs is NOT cleared each time */
5265 get_regionviews_by_id (*pr, rs);
5268 selection->set (rs);
5270 /* first idle adds route children (automation tracks), so we need to redisplay here */
5271 _routes->redisplay ();
5275 if (_session->undo_depth() == 0) {
5276 undo_action->set_sensitive(false);
5278 redo_action->set_sensitive(false);
5279 begin_selection_op_history ();
5285 Editor::_idle_resize (gpointer arg)
5287 return ((Editor*)arg)->idle_resize ();
5291 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
5293 if (resize_idle_id < 0) {
5294 /* https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#G-PRIORITY-HIGH-IDLE:CAPS
5295 * GTK+ uses G_PRIORITY_HIGH_IDLE + 10 for resizing operations, and G_PRIORITY_HIGH_IDLE + 20 for redrawing operations.
5296 * (This is done to ensure that any pending resizes are processed before any pending redraws, so that widgets are not redrawn twice unnecessarily.)
5298 resize_idle_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, _idle_resize, this, NULL);
5299 _pending_resize_amount = 0;
5302 /* make a note of the smallest resulting height, so that we can clamp the
5303 lower limit at TimeAxisView::hSmall */
5305 int32_t min_resulting = INT32_MAX;
5307 _pending_resize_amount += h;
5308 _pending_resize_view = view;
5310 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
5312 if (selection->tracks.contains (_pending_resize_view)) {
5313 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5314 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
5318 if (min_resulting < 0) {
5323 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
5324 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
5328 /** Handle pending resizing of tracks */
5330 Editor::idle_resize ()
5332 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
5334 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
5335 selection->tracks.contains (_pending_resize_view)) {
5337 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5338 if (*i != _pending_resize_view) {
5339 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
5344 _pending_resize_amount = 0;
5345 _group_tabs->set_dirty ();
5346 resize_idle_id = -1;
5354 ENSURE_GUI_THREAD (*this, &Editor::located);
5357 playhead_cursor->set_position (_session->audible_sample ());
5358 if (_follow_playhead && !_pending_initial_locate) {
5359 reset_x_origin_to_follow_playhead ();
5363 _pending_locate_request = false;
5364 _pending_initial_locate = false;
5365 _last_update_time = 0;
5369 Editor::region_view_added (RegionView * rv)
5371 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (rv);
5373 list<pair<PBD::ID const, list<Evoral::event_id_t> > >::iterator rnote;
5374 for (rnote = selection->pending_midi_note_selection.begin(); rnote != selection->pending_midi_note_selection.end(); ++rnote) {
5375 if (rv->region()->id () == (*rnote).first) {
5376 mrv->select_notes ((*rnote).second);
5377 selection->pending_midi_note_selection.erase(rnote);
5383 _summary->set_background_dirty ();
5385 mark_region_boundary_cache_dirty ();
5389 Editor::region_view_removed ()
5391 _summary->set_background_dirty ();
5393 mark_region_boundary_cache_dirty ();
5397 Editor::axis_view_by_stripable (boost::shared_ptr<Stripable> s) const
5399 for (TrackViewList::const_iterator j = track_views.begin (); j != track_views.end(); ++j) {
5400 if ((*j)->stripable() == s) {
5409 Editor::axis_view_by_control (boost::shared_ptr<AutomationControl> c) const
5411 for (TrackViewList::const_iterator j = track_views.begin (); j != track_views.end(); ++j) {
5412 if ((*j)->control() == c) {
5416 TimeAxisView::Children kids = (*j)->get_child_list ();
5418 for (TimeAxisView::Children::iterator k = kids.begin(); k != kids.end(); ++k) {
5419 if ((*k)->control() == c) {
5429 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
5433 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
5434 TimeAxisView* tv = time_axis_view_from_stripable (*i);
5444 Editor::suspend_route_redisplay ()
5447 _routes->suspend_redisplay();
5452 Editor::resume_route_redisplay ()
5455 _routes->redisplay(); // queue redisplay
5456 _routes->resume_redisplay();
5461 Editor::add_vcas (VCAList& vlist)
5465 for (VCAList::iterator v = vlist.begin(); v != vlist.end(); ++v) {
5466 sl.push_back (boost::dynamic_pointer_cast<Stripable> (*v));
5469 add_stripables (sl);
5473 Editor::add_routes (RouteList& rlist)
5477 for (RouteList::iterator r = rlist.begin(); r != rlist.end(); ++r) {
5481 add_stripables (sl);
5485 Editor::add_stripables (StripableList& sl)
5487 list<TimeAxisView*> new_views;
5488 boost::shared_ptr<VCA> v;
5489 boost::shared_ptr<Route> r;
5490 TrackViewList new_selection;
5491 bool from_scratch = (track_views.size() == 0);
5493 sl.sort (Stripable::Sorter());
5495 for (StripableList::iterator s = sl.begin(); s != sl.end(); ++s) {
5497 if ((v = boost::dynamic_pointer_cast<VCA> (*s)) != 0) {
5499 VCATimeAxisView* vtv = new VCATimeAxisView (*this, _session, *_track_canvas);
5501 new_views.push_back (vtv);
5503 } else if ((r = boost::dynamic_pointer_cast<Route> (*s)) != 0) {
5505 if (r->is_auditioner() || r->is_monitor()) {
5509 RouteTimeAxisView* rtv;
5510 DataType dt = r->input()->default_type();
5512 if (dt == ARDOUR::DataType::AUDIO) {
5513 rtv = new AudioTimeAxisView (*this, _session, *_track_canvas);
5515 } else if (dt == ARDOUR::DataType::MIDI) {
5516 rtv = new MidiTimeAxisView (*this, _session, *_track_canvas);
5519 throw unknown_type();
5522 new_views.push_back (rtv);
5523 track_views.push_back (rtv);
5524 new_selection.push_back (rtv);
5526 rtv->effective_gain_display ();
5528 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
5529 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
5533 if (new_views.size() > 0) {
5534 _routes->time_axis_views_added (new_views);
5535 //_summary->routes_added (new_selection); /* XXX requires RouteTimeAxisViewList */
5538 /* note: !new_selection.empty() means that we got some routes rather
5542 if (!from_scratch && !new_selection.empty()) {
5543 selection->set (new_selection);
5544 begin_selection_op_history();
5547 if (show_editor_mixer_when_tracks_arrive && !new_selection.empty()) {
5548 show_editor_mixer (true);
5551 editor_list_button.set_sensitive (true);
5555 Editor::timeaxisview_deleted (TimeAxisView *tv)
5557 if (tv == entered_track) {
5561 if (_session && _session->deletion_in_progress()) {
5562 /* the situation is under control */
5566 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
5568 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
5570 _routes->route_removed (tv);
5572 TimeAxisView::Children c = tv->get_child_list ();
5573 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
5574 if (entered_track == i->get()) {
5579 /* remove it from the list of track views */
5581 TrackViewList::iterator i;
5583 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
5584 i = track_views.erase (i);
5587 /* update whatever the current mixer strip is displaying, if revelant */
5589 boost::shared_ptr<Route> route;
5592 route = rtav->route ();
5595 if (current_mixer_strip && current_mixer_strip->route() == route) {
5597 TimeAxisView* next_tv;
5599 if (track_views.empty()) {
5601 } else if (i == track_views.end()) {
5602 next_tv = track_views.front();
5607 // skip VCAs (cannot be selected, n/a in editor-mixer)
5608 if (dynamic_cast<VCATimeAxisView*> (next_tv)) {
5609 /* VCAs are sorted last in line -- route_sorter.h, jump to top */
5610 next_tv = track_views.front();
5612 if (dynamic_cast<VCATimeAxisView*> (next_tv)) {
5613 /* just in case: no master, only a VCA remains */
5619 set_selected_mixer_strip (*next_tv);
5621 /* make the editor mixer strip go away setting the
5622 * button to inactive (which also unticks the menu option)
5625 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
5631 Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection)
5637 DisplaySuspender ds;
5638 PresentationInfo::ChangeSuspender cs;
5640 if (apply_to_selection) {
5641 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end();) {
5643 TrackSelection::iterator j = i;
5646 hide_track_in_display (*i, false);
5651 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
5653 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
5654 /* this will hide the mixer strip */
5655 set_selected_mixer_strip (*tv);
5658 _routes->hide_track_in_display (*tv);
5663 Editor::show_track_in_display (TimeAxisView* tv, bool move_into_view)
5668 _routes->show_track_in_display (*tv);
5669 if (move_into_view) {
5670 ensure_time_axis_view_is_visible (*tv, false);
5675 Editor::sync_track_view_list_and_routes ()
5677 track_views = TrackViewList (_routes->views ());
5679 _summary->set_background_dirty();
5680 _group_tabs->set_dirty ();
5682 return false; // do not call again (until needed)
5686 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5688 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5693 /** Find a StripableTimeAxisView by the ID of its stripable */
5694 StripableTimeAxisView*
5695 Editor::get_stripable_time_axis_by_id (const PBD::ID& id) const
5697 StripableTimeAxisView* v;
5699 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5700 if((v = dynamic_cast<StripableTimeAxisView*>(*i)) != 0) {
5701 if(v->stripable()->id() == id) {
5711 Editor::fit_route_group (RouteGroup *g)
5713 TrackViewList ts = axis_views_from_routes (g->route_list ());
5718 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5720 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5723 _session->cancel_audition ();
5727 if (_session->is_auditioning()) {
5728 _session->cancel_audition ();
5729 if (r == last_audition_region) {
5734 _session->audition_region (r);
5735 last_audition_region = r;
5740 Editor::hide_a_region (boost::shared_ptr<Region> r)
5742 r->set_hidden (true);
5746 Editor::show_a_region (boost::shared_ptr<Region> r)
5748 r->set_hidden (false);
5752 Editor::audition_region_from_region_list ()
5754 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5758 Editor::hide_region_from_region_list ()
5760 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5764 Editor::show_region_in_region_list ()
5766 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5770 Editor::step_edit_status_change (bool yn)
5773 start_step_editing ();
5775 stop_step_editing ();
5780 Editor::start_step_editing ()
5782 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5786 Editor::stop_step_editing ()
5788 step_edit_connection.disconnect ();
5792 Editor::check_step_edit ()
5794 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5795 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5797 mtv->check_step_edit ();
5801 return true; // do it again, till we stop
5805 Editor::scroll_press (Direction dir)
5807 ++_scroll_callbacks;
5809 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5810 /* delay the first auto-repeat */
5816 scroll_backward (1);
5824 scroll_up_one_track ();
5828 scroll_down_one_track ();
5832 /* do hacky auto-repeat */
5833 if (!_scroll_connection.connected ()) {
5835 _scroll_connection = Glib::signal_timeout().connect (
5836 sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100
5839 _scroll_callbacks = 0;
5846 Editor::scroll_release ()
5848 _scroll_connection.disconnect ();
5851 /** Queue a change for the Editor viewport x origin to follow the playhead */
5853 Editor::reset_x_origin_to_follow_playhead ()
5855 samplepos_t const sample = playhead_cursor->current_sample ();
5857 if (sample < _leftmost_sample || sample > _leftmost_sample + current_page_samples()) {
5859 if (_session->transport_speed() < 0) {
5861 if (sample > (current_page_samples() / 2)) {
5862 center_screen (sample-(current_page_samples()/2));
5864 center_screen (current_page_samples()/2);
5871 if (sample < _leftmost_sample) {
5873 if (_session->transport_rolling()) {
5874 /* rolling; end up with the playhead at the right of the page */
5875 l = sample - current_page_samples ();
5877 /* not rolling: end up with the playhead 1/4 of the way along the page */
5878 l = sample - current_page_samples() / 4;
5882 if (_session->transport_rolling()) {
5883 /* rolling: end up with the playhead on the left of the page */
5886 /* not rolling: end up with the playhead 3/4 of the way along the page */
5887 l = sample - 3 * current_page_samples() / 4;
5895 center_screen_internal (l + (current_page_samples() / 2), current_page_samples ());
5901 Editor::super_rapid_screen_update ()
5903 if (!_session || !_session->engine().running()) {
5907 /* METERING / MIXER STRIPS */
5909 /* update track meters, if required */
5910 if (contents().is_mapped() && meters_running) {
5911 RouteTimeAxisView* rtv;
5912 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5913 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5914 rtv->fast_update ();
5919 /* and any current mixer strip */
5920 if (current_mixer_strip) {
5921 current_mixer_strip->fast_update ();
5924 bool latent_locate = false;
5925 samplepos_t sample = _session->audible_sample (&latent_locate);
5926 const int64_t now = g_get_monotonic_time ();
5929 if (_session->exporting ()) {
5930 /* freewheel/export may be faster or slower than transport_speed() / SR.
5931 * Also exporting multiple ranges locates/jumps without a _pending_locate_request.
5933 _last_update_time = 0;
5936 if (!_session->transport_rolling () || _session->is_auditioning ()) {
5937 /* Do not interpolate the playhead position; just set it */
5938 _last_update_time = 0;
5941 if (_last_update_time > 0) {
5942 /* interpolate and smoothen playhead position */
5943 const double ds = (now - _last_update_time) * _session->transport_speed() * _session->nominal_sample_rate () * 1e-6;
5944 samplepos_t guess = playhead_cursor->current_sample () + rint (ds);
5945 err = sample - guess;
5947 guess += err * .12 + _err_screen_engine; // time-constant based on 25fps (super_rapid_screen_update)
5948 _err_screen_engine += .0144 * (err - _err_screen_engine); // tc^2
5951 printf ("eng: %ld gui:%ld (%+6.1f) diff: %6.1f (err: %7.2f)\n",
5953 err, _err_screen_engine);
5958 _err_screen_engine = 0;
5961 if (err > 8192 || latent_locate) {
5962 // in case of x-runs or freewheeling
5963 _last_update_time = 0;
5964 sample = _session->audible_sample ();
5966 _last_update_time = now;
5969 /* snapped cursor stuff (the snapped_cursor shows where an operation is going to occur) */
5971 MusicSample where (sample, 0);
5972 if (!UIConfiguration::instance().get_show_snapped_cursor()) {
5973 snapped_cursor->hide ();
5974 } else if (_edit_point == EditAtPlayhead && !_dragging_playhead) {
5975 /* EditAtPlayhead does not snap */
5976 } else if (_edit_point == EditAtSelectedMarker) {
5977 /* NOTE: I don't think EditAtSelectedMarker should snap. They are what they are.
5978 * however, the current editing code -does- snap so I'll draw it that way for now.
5980 if (!selection->markers.empty()) {
5981 MusicSample ms (selection->markers.front()->position(), 0);
5982 snap_to (ms); // should use snap_to_with_modifier?
5983 snapped_cursor->set_position (ms.sample);
5984 snapped_cursor->show ();
5986 } else if (mouse_sample (where.sample, ignored)) { // cursor is in the editing canvas. show it.
5987 snapped_cursor->show ();
5988 } else { // mouse is out of the editing canvas. hide the snapped_cursor
5989 snapped_cursor->hide ();
5992 /* There are a few reasons why we might not update the playhead / viewport stuff:
5994 * 1. we don't update things when there's a pending locate request, otherwise
5995 * when the editor requests a locate there is a chance that this method
5996 * will move the playhead before the locate request is processed, causing
5998 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5999 * 3. if we're still at the same frame that we were last time, there's nothing to do.
6001 if (_pending_locate_request) {
6002 _last_update_time = 0;
6006 if (_dragging_playhead) {
6007 _last_update_time = 0;
6011 if (playhead_cursor->current_sample () == sample) {
6015 playhead_cursor->set_position (sample);
6017 if (_session->requested_return_sample() >= 0) {
6018 _last_update_time = 0;
6022 if (!_follow_playhead || pending_visual_change.being_handled) {
6023 /* We only do this if we aren't already
6024 * handling a visual change (ie if
6025 * pending_visual_change.being_handled is
6026 * false) so that these requests don't stack
6027 * up there are too many of them to handle in
6033 if (!_stationary_playhead) {
6034 reset_x_origin_to_follow_playhead ();
6036 samplepos_t const sample = playhead_cursor->current_sample ();
6037 double target = ((double)sample - (double)current_page_samples() / 2.0);
6038 if (target <= 0.0) {
6041 /* compare to EditorCursor::set_position() */
6042 double const old_pos = sample_to_pixel_unrounded (_leftmost_sample);
6043 double const new_pos = sample_to_pixel_unrounded (target);
6044 if (rint (new_pos) != rint (old_pos)) {
6045 reset_x_origin (pixel_to_sample (new_pos));
6052 Editor::session_going_away ()
6054 _have_idled = false;
6056 _session_connections.drop_connections ();
6058 super_rapid_screen_update_connection.disconnect ();
6060 selection->clear ();
6061 cut_buffer->clear ();
6063 clicked_regionview = 0;
6064 clicked_axisview = 0;
6065 clicked_routeview = 0;
6066 entered_regionview = 0;
6068 _last_update_time = 0;
6071 playhead_cursor->hide ();
6073 /* rip everything out of the list displays */
6077 _route_groups->clear ();
6079 /* do this first so that deleting a track doesn't reset cms to null
6080 and thus cause a leak.
6083 if (current_mixer_strip) {
6084 if (current_mixer_strip->get_parent() != 0) {
6085 global_hpacker.remove (*current_mixer_strip);
6087 delete current_mixer_strip;
6088 current_mixer_strip = 0;
6091 /* delete all trackviews */
6093 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6096 track_views.clear ();
6098 nudge_clock->set_session (0);
6100 editor_list_button.set_active(false);
6101 editor_list_button.set_sensitive(false);
6103 /* clear tempo/meter rulers */
6104 remove_metric_marks ();
6105 clear_marker_display ();
6111 stop_step_editing ();
6115 /* get rid of any existing editor mixer strip */
6117 WindowTitle title(Glib::get_application_name());
6118 title += _("Editor");
6120 own_window()->set_title (title.get_string());
6123 SessionHandlePtr::session_going_away ();
6127 Editor::trigger_script (int i)
6129 LuaInstance::instance()-> call_action (i);
6133 Editor::show_editor_list (bool yn)
6136 _editor_list_vbox.show ();
6138 _editor_list_vbox.hide ();
6143 Editor::change_region_layering_order (bool from_context_menu)
6145 const samplepos_t position = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context_menu);
6147 if (!clicked_routeview) {
6148 if (layering_order_editor) {
6149 layering_order_editor->hide ();
6154 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
6160 boost::shared_ptr<Playlist> pl = track->playlist();
6166 if (layering_order_editor == 0) {
6167 layering_order_editor = new RegionLayeringOrderEditor (*this);
6170 layering_order_editor->set_context (clicked_routeview->name(), _session, clicked_routeview, pl, position);
6171 layering_order_editor->maybe_present ();
6175 Editor::update_region_layering_order_editor ()
6177 if (layering_order_editor && layering_order_editor->is_visible ()) {
6178 change_region_layering_order (true);
6183 Editor::setup_fade_images ()
6185 _xfade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadein-linear")));
6186 _xfade_in_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadein-symmetric")));
6187 _xfade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadein-fast-cut")));
6188 _xfade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadein-slow-cut")));
6189 _xfade_in_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadein-constant-power")));
6191 _xfade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadeout-linear")));
6192 _xfade_out_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-symmetric")));
6193 _xfade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut")));
6194 _xfade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut")));
6195 _xfade_out_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-constant-power")));
6199 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
6201 Editor::action_menu_item (std::string const & name)
6203 Glib::RefPtr<Action> a = editor_actions->get_action (name);
6206 return *manage (a->create_menu_item ());
6210 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
6212 EventBox* b = manage (new EventBox);
6213 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
6214 Label* l = manage (new Label (name));
6218 _the_notebook.append_page (widget, *b);
6222 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
6224 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
6225 _the_notebook.set_current_page (_the_notebook.page_num (*page));
6228 if (ev->type == GDK_2BUTTON_PRESS) {
6230 /* double-click on a notebook tab shrinks or expands the notebook */
6232 if (_notebook_shrunk) {
6233 if (pre_notebook_shrink_pane_width) {
6234 edit_pane.set_divider (0, *pre_notebook_shrink_pane_width);
6236 _notebook_shrunk = false;
6238 pre_notebook_shrink_pane_width = edit_pane.get_divider();
6240 /* this expands the LHS of the edit pane to cover the notebook
6241 PAGE but leaves the tabs visible.
6243 edit_pane.set_divider (0, edit_pane.get_divider() + page->get_width());
6244 _notebook_shrunk = true;
6252 Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
6254 using namespace Menu_Helpers;
6256 MenuList& items = _control_point_context_menu.items ();
6259 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &Editor::edit_control_point), item)));
6260 items.push_back (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun (*this, &Editor::remove_control_point), item)));
6261 if (!can_remove_control_point (item)) {
6262 items.back().set_sensitive (false);
6265 _control_point_context_menu.popup (event->button.button, event->button.time);
6269 Editor::popup_note_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
6271 using namespace Menu_Helpers;
6273 NoteBase* note = reinterpret_cast<NoteBase*>(item->get_data("notebase"));
6278 /* We need to get the selection here and pass it to the operations, since
6279 popping up the menu will cause a region leave event which clears
6280 entered_regionview. */
6282 MidiRegionView& mrv = note->region_view();
6283 const RegionSelection rs = get_regions_from_selection_and_entered ();
6284 const uint32_t sel_size = mrv.selection_size ();
6286 MenuList& items = _note_context_menu.items();
6290 items.push_back(MenuElem(_("Delete"),
6291 sigc::mem_fun(mrv, &MidiRegionView::delete_selection)));
6294 items.push_back(MenuElem(_("Edit..."),
6295 sigc::bind(sigc::mem_fun(*this, &Editor::edit_notes), &mrv)));
6296 if (sel_size != 1) {
6297 items.back().set_sensitive (false);
6300 items.push_back(MenuElem(_("Transpose..."),
6301 sigc::bind(sigc::mem_fun(*this, &Editor::transpose_regions), rs)));
6304 items.push_back(MenuElem(_("Legatize"),
6305 sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, false)));
6307 items.back().set_sensitive (false);
6310 items.push_back(MenuElem(_("Quantize..."),
6311 sigc::bind(sigc::mem_fun(*this, &Editor::quantize_regions), rs)));
6313 items.push_back(MenuElem(_("Remove Overlap"),
6314 sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, true)));
6316 items.back().set_sensitive (false);
6319 items.push_back(MenuElem(_("Transform..."),
6320 sigc::bind(sigc::mem_fun(*this, &Editor::transform_regions), rs)));
6322 _note_context_menu.popup (event->button.button, event->button.time);
6326 Editor::zoom_vertical_modifier_released()
6328 _stepping_axis_view = 0;
6332 Editor::ui_parameter_changed (string parameter)
6334 if (parameter == "icon-set") {
6335 while (!_cursor_stack.empty()) {
6336 _cursor_stack.pop_back();
6338 _cursors->set_cursor_set (UIConfiguration::instance().get_icon_set());
6339 _cursor_stack.push_back(_cursors->grabber);
6340 edit_pane.set_drag_cursor (*_cursors->expand_left_right);
6341 editor_summary_pane.set_drag_cursor (*_cursors->expand_up_down);
6343 } else if (parameter == "draggable-playhead") {
6344 if (_verbose_cursor) {
6345 playhead_cursor->set_sensitive (UIConfiguration::instance().get_draggable_playhead());
6347 } else if (parameter == "use-note-bars-for-velocity") {
6348 ArdourCanvas::Note::set_show_velocity_bars (UIConfiguration::instance().get_use_note_bars_for_velocity());
6349 _track_canvas->request_redraw (_track_canvas->visible_area());
6350 } else if (parameter == "use-note-color-for-velocity") {
6351 /* handled individually by each MidiRegionView */
6356 Editor::use_own_window (bool and_fill_it)
6358 bool new_window = !own_window();
6360 Gtk::Window* win = Tabbable::use_own_window (and_fill_it);
6362 if (win && new_window) {
6363 win->set_name ("EditorWindow");
6365 ARDOUR_UI::instance()->setup_toplevel_window (*win, _("Editor"), this);
6367 // win->signal_realize().connect (*this, &Editor::on_realize);
6368 win->signal_event().connect (sigc::bind (sigc::ptr_fun (&Keyboard::catch_user_event_for_pre_dialog_focus), win));
6369 win->signal_event().connect (sigc::mem_fun (*this, &Editor::generic_event_handler));
6370 win->set_data ("ardour-bindings", bindings);
6375 DisplaySuspender ds;
6376 contents().show_all ();
6378 /* XXX: this is a bit unfortunate; it would probably
6379 be nicer if we could just call show () above rather
6380 than needing the show_all ()
6383 /* re-hide stuff if necessary */
6384 editor_list_button_toggled ();
6385 parameter_changed ("show-summary");
6386 parameter_changed ("show-group-tabs");
6387 parameter_changed ("show-zoom-tools");
6389 /* now reset all audio_time_axis heights, because widgets might need
6395 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6396 tv = (static_cast<TimeAxisView*>(*i));
6397 tv->reset_height ();
6400 if (current_mixer_strip) {
6401 current_mixer_strip->hide_things ();
6402 current_mixer_strip->parameter_changed ("mixer-element-visibility");