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);
869 delete button_bindings;
871 delete _route_groups;
872 delete _track_canvas_viewport;
875 delete _verbose_cursor;
876 delete quantize_dialog;
882 delete _playlist_selector;
883 delete _time_info_box;
888 LuaInstance::destroy_instance ();
890 for (list<XMLNode *>::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) {
893 for (std::map<ARDOUR::FadeShape, Gtk::Image*>::const_iterator i = _xfade_in_images.begin(); i != _xfade_in_images.end (); ++i) {
896 for (std::map<ARDOUR::FadeShape, Gtk::Image*>::const_iterator i = _xfade_out_images.begin(); i != _xfade_out_images.end (); ++i) {
902 Editor::button_settings () const
904 XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
905 XMLNode* node = find_named_node (*settings, X_("Buttons"));
908 node = new XMLNode (X_("Buttons"));
915 Editor::get_smart_mode () const
917 return ((current_mouse_mode() == MouseObject) && smart_mode_action->get_active());
921 Editor::catch_vanishing_regionview (RegionView *rv)
923 /* note: the selection will take care of the vanishing
924 audioregionview by itself.
927 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
931 if (clicked_regionview == rv) {
932 clicked_regionview = 0;
935 if (entered_regionview == rv) {
936 set_entered_regionview (0);
939 if (!_all_region_actions_sensitized) {
940 sensitize_all_region_actions (true);
945 Editor::set_entered_regionview (RegionView* rv)
947 if (rv == entered_regionview) {
951 if (entered_regionview) {
952 entered_regionview->exited ();
955 entered_regionview = rv;
957 if (entered_regionview != 0) {
958 entered_regionview->entered ();
961 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
962 /* This RegionView entry might have changed what region actions
963 are allowed, so sensitize them all in case a key is pressed.
965 sensitize_all_region_actions (true);
970 Editor::set_entered_track (TimeAxisView* tav)
973 entered_track->exited ();
979 entered_track->entered ();
984 Editor::instant_save ()
986 if (!constructed || no_save_instant) {
991 _session->add_instant_xml(get_state());
993 Config->add_instant_xml(get_state());
998 Editor::control_vertical_zoom_in_all ()
1000 tav_zoom_smooth (false, true);
1004 Editor::control_vertical_zoom_out_all ()
1006 tav_zoom_smooth (true, true);
1010 Editor::control_vertical_zoom_in_selected ()
1012 tav_zoom_smooth (false, false);
1016 Editor::control_vertical_zoom_out_selected ()
1018 tav_zoom_smooth (true, false);
1022 Editor::control_view (uint32_t view)
1024 goto_visual_state (view);
1028 Editor::control_unselect ()
1030 selection->clear_tracks ();
1034 Editor::control_select (boost::shared_ptr<Stripable> s, Selection::Operation op)
1036 TimeAxisView* tav = time_axis_view_from_stripable (s);
1040 case Selection::Add:
1041 selection->add (tav);
1043 case Selection::Toggle:
1044 selection->toggle (tav);
1046 case Selection::Extend:
1048 case Selection::Set:
1049 selection->set (tav);
1053 selection->clear_tracks ();
1058 Editor::control_step_tracks_up ()
1060 scroll_tracks_up_line ();
1064 Editor::control_step_tracks_down ()
1066 scroll_tracks_down_line ();
1070 Editor::control_scroll (float fraction)
1072 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
1078 double step = fraction * current_page_samples();
1081 _control_scroll_target is an optional<T>
1083 it acts like a pointer to an samplepos_t, with
1084 a operator conversion to boolean to check
1085 that it has a value could possibly use
1086 playhead_cursor->current_sample to store the
1087 value and a boolean in the class to know
1088 when it's out of date
1091 if (!_control_scroll_target) {
1092 _control_scroll_target = _session->transport_sample();
1093 _dragging_playhead = true;
1096 if ((fraction < 0.0f) && (*_control_scroll_target <= (samplepos_t) fabs(step))) {
1097 *_control_scroll_target = 0;
1098 } else if ((fraction > 0.0f) && (max_samplepos - *_control_scroll_target < step)) {
1099 *_control_scroll_target = max_samplepos - (current_page_samples()*2); // allow room for slop in where the PH is on the screen
1101 *_control_scroll_target += (samplepos_t) trunc (step);
1104 /* move visuals, we'll catch up with it later */
1106 playhead_cursor->set_position (*_control_scroll_target);
1107 UpdateAllTransportClocks (*_control_scroll_target);
1109 if (*_control_scroll_target > (current_page_samples() / 2)) {
1110 /* try to center PH in window */
1111 reset_x_origin (*_control_scroll_target - (current_page_samples()/2));
1117 Now we do a timeout to actually bring the session to the right place
1118 according to the playhead. This is to avoid reading disk buffers on every
1119 call to control_scroll, which is driven by ScrollTimeline and therefore
1120 probably by a control surface wheel which can generate lots of events.
1122 /* cancel the existing timeout */
1124 control_scroll_connection.disconnect ();
1126 /* add the next timeout */
1128 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
1132 Editor::deferred_control_scroll (samplepos_t /*target*/)
1134 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
1135 /* reset for next stream */
1136 _control_scroll_target = boost::none;
1137 _dragging_playhead = false;
1142 Editor::access_action (const std::string& action_group, const std::string& action_item)
1148 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
1151 act = ActionManager::get_action (action_group.c_str(), action_item.c_str());
1159 Editor::set_toggleaction (const std::string& action_group, const std::string& action_item, bool s)
1161 ActionManager::set_toggleaction_state (action_group.c_str(), action_item.c_str(), s);
1165 Editor::on_realize ()
1169 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
1170 start_lock_event_timing ();
1175 Editor::start_lock_event_timing ()
1177 /* check if we should lock the GUI every 30 seconds */
1179 Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::lock_timeout_callback), 30 * 1000);
1183 Editor::generic_event_handler (GdkEvent* ev)
1186 case GDK_BUTTON_PRESS:
1187 case GDK_BUTTON_RELEASE:
1188 case GDK_MOTION_NOTIFY:
1190 case GDK_KEY_RELEASE:
1191 if (contents().is_mapped()) {
1192 gettimeofday (&last_event_time, 0);
1196 case GDK_LEAVE_NOTIFY:
1197 switch (ev->crossing.detail) {
1198 case GDK_NOTIFY_UNKNOWN:
1199 case GDK_NOTIFY_INFERIOR:
1200 case GDK_NOTIFY_ANCESTOR:
1202 case GDK_NOTIFY_VIRTUAL:
1203 case GDK_NOTIFY_NONLINEAR:
1204 case GDK_NOTIFY_NONLINEAR_VIRTUAL:
1205 /* leaving window, so reset focus, thus ending any and
1206 all text entry operations.
1208 ARDOUR_UI::instance()->reset_focus (&contents());
1221 Editor::lock_timeout_callback ()
1223 struct timeval now, delta;
1225 gettimeofday (&now, 0);
1227 timersub (&now, &last_event_time, &delta);
1229 if (delta.tv_sec > (time_t) UIConfiguration::instance().get_lock_gui_after_seconds()) {
1231 /* don't call again. Returning false will effectively
1232 disconnect us from the timer callback.
1234 unlock() will call start_lock_event_timing() to get things
1244 Editor::map_position_change (samplepos_t sample)
1246 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, sample)
1248 if (_session == 0) {
1252 if (_follow_playhead) {
1253 center_screen (sample);
1256 playhead_cursor->set_position (sample);
1260 Editor::center_screen (samplepos_t sample)
1262 samplecnt_t const page = _visible_canvas_width * samples_per_pixel;
1264 /* if we're off the page, then scroll.
1267 if (sample < _leftmost_sample || sample >= _leftmost_sample + page) {
1268 center_screen_internal (sample, page);
1273 Editor::center_screen_internal (samplepos_t sample, float page)
1277 if (sample > page) {
1278 sample -= (samplepos_t) page;
1283 reset_x_origin (sample);
1288 Editor::update_title ()
1290 ENSURE_GUI_THREAD (*this, &Editor::update_title);
1292 if (!own_window()) {
1297 bool dirty = _session->dirty();
1299 string session_name;
1301 if (_session->snap_name() != _session->name()) {
1302 session_name = _session->snap_name();
1304 session_name = _session->name();
1308 session_name = "*" + session_name;
1311 WindowTitle title(session_name);
1312 title += S_("Window|Editor");
1313 title += Glib::get_application_name();
1314 own_window()->set_title (title.get_string());
1316 /* ::session_going_away() will have taken care of it */
1321 Editor::set_session (Session *t)
1323 SessionHandlePtr::set_session (t);
1329 /* initialize _leftmost_sample to the extents of the session
1330 * this prevents a bogus setting of leftmost = "0" if the summary view asks for the leftmost sample
1331 * before the visible state has been loaded from instant.xml */
1332 _leftmost_sample = session_gui_extents().first;
1334 _playlist_selector->set_session (_session);
1335 nudge_clock->set_session (_session);
1336 _summary->set_session (_session);
1337 _group_tabs->set_session (_session);
1338 _route_groups->set_session (_session);
1339 _regions->set_session (_session);
1340 _snapshots->set_session (_session);
1341 _routes->set_session (_session);
1342 _locations->set_session (_session);
1343 _time_info_box->set_session (_session);
1345 if (rhythm_ferret) {
1346 rhythm_ferret->set_session (_session);
1349 if (analysis_window) {
1350 analysis_window->set_session (_session);
1354 sfbrowser->set_session (_session);
1357 compute_fixed_ruler_scale ();
1359 /* Make sure we have auto loop and auto punch ranges */
1361 Location* loc = _session->locations()->auto_loop_location();
1363 loc->set_name (_("Loop"));
1366 loc = _session->locations()->auto_punch_location();
1369 loc->set_name (_("Punch"));
1372 refresh_location_display ();
1374 /* This must happen after refresh_location_display(), as (amongst other things) we restore
1375 * the selected Marker; this needs the LocationMarker list to be available.
1377 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1378 set_state (*node, Stateful::loading_state_version);
1380 /* catch up on selection state, etc. */
1383 sc.add (Properties::selected);
1384 presentation_info_changed (sc);
1386 /* catch up with the playhead */
1388 _session->request_locate (playhead_cursor->current_sample ());
1389 _pending_initial_locate = true;
1393 /* These signals can all be emitted by a non-GUI thread. Therefore the
1394 handlers for them must not attempt to directly interact with the GUI,
1395 but use PBD::Signal<T>::connect() which accepts an event loop
1396 ("context") where the handler will be asked to run.
1399 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::step_edit_status_change, this, _1), gui_context());
1400 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1401 _session->TransportLooped.connect (_session_connections, invalidator (*this), boost::bind (&Editor::transport_looped, this), gui_context());
1402 _session->PositionChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_position_change, this, _1), gui_context());
1403 _session->vca_manager().VCAAdded.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_vcas, this, _1), gui_context());
1404 _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_routes, this, _1), gui_context());
1405 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1406 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::tempo_map_changed, this, _1), gui_context());
1407 _session->tempo_map().MetricPositionChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::tempometric_position_changed, this, _1), gui_context());
1408 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1409 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::parameter_changed, this, _1), gui_context());
1410 _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Editor::session_state_saved, this, _1), gui_context());
1411 _session->locations()->added.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_new_location, this, _1), gui_context());
1412 _session->locations()->removed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::location_gone, this, _1), gui_context());
1413 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1414 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1416 playhead_cursor->track_canvas_item().reparent ((ArdourCanvas::Item*) get_cursor_scroll_group());
1417 playhead_cursor->show ();
1419 snapped_cursor->track_canvas_item().reparent ((ArdourCanvas::Item*) get_cursor_scroll_group());
1420 snapped_cursor->set_color (UIConfiguration::instance().color ("edit point"));
1421 snapped_cursor->show ();
1423 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1424 Config->map_parameters (pc);
1425 _session->config.map_parameters (pc);
1427 restore_ruler_visibility ();
1428 //tempo_map_changed (PropertyChange (0));
1429 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1431 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1432 (static_cast<TimeAxisView*>(*i))->set_samples_per_pixel (samples_per_pixel);
1435 super_rapid_screen_update_connection = Timers::super_rapid_connect (
1436 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1439 /* register for undo history */
1440 _session->register_with_memento_command_factory(id(), this);
1441 _session->register_with_memento_command_factory(_selection_memento->id(), _selection_memento);
1443 LuaInstance::instance()->set_session(_session);
1445 start_updating_meters ();
1449 Editor::fill_xfade_menu (Menu_Helpers::MenuList& items, bool start)
1451 using namespace Menu_Helpers;
1453 void (Editor::*emf)(FadeShape);
1454 std::map<ARDOUR::FadeShape,Gtk::Image*>* images;
1457 images = &_xfade_in_images;
1458 emf = &Editor::set_fade_in_shape;
1460 images = &_xfade_out_images;
1461 emf = &Editor::set_fade_out_shape;
1466 _("Linear (for highly correlated material)"),
1467 *(*images)[FadeLinear],
1468 sigc::bind (sigc::mem_fun (*this, emf), FadeLinear)
1472 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1476 _("Constant power"),
1477 *(*images)[FadeConstantPower],
1478 sigc::bind (sigc::mem_fun (*this, emf), FadeConstantPower)
1481 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1486 *(*images)[FadeSymmetric],
1487 sigc::bind (sigc::mem_fun (*this, emf), FadeSymmetric)
1491 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1496 *(*images)[FadeSlow],
1497 sigc::bind (sigc::mem_fun (*this, emf), FadeSlow)
1500 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1505 *(*images)[FadeFast],
1506 sigc::bind (sigc::mem_fun (*this, emf), FadeFast)
1509 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1512 /** Pop up a context menu for when the user clicks on a start crossfade */
1514 Editor::popup_xfade_in_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType /*item_type*/)
1516 using namespace Menu_Helpers;
1517 AudioRegionView* arv = dynamic_cast<AudioRegionView*> ((RegionView*)item->get_data ("regionview"));
1522 MenuList& items (xfade_in_context_menu.items());
1525 if (arv->audio_region()->fade_in_active()) {
1526 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1528 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1531 items.push_back (SeparatorElem());
1532 fill_xfade_menu (items, true);
1534 xfade_in_context_menu.popup (button, time);
1537 /** Pop up a context menu for when the user clicks on an end crossfade */
1539 Editor::popup_xfade_out_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType /*item_type*/)
1541 using namespace Menu_Helpers;
1542 AudioRegionView* arv = dynamic_cast<AudioRegionView*> ((RegionView*)item->get_data ("regionview"));
1547 MenuList& items (xfade_out_context_menu.items());
1550 if (arv->audio_region()->fade_out_active()) {
1551 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1553 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1556 items.push_back (SeparatorElem());
1557 fill_xfade_menu (items, false);
1559 xfade_out_context_menu.popup (button, time);
1563 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1565 using namespace Menu_Helpers;
1566 Menu* (Editor::*build_menu_function)();
1569 switch (item_type) {
1571 case RegionViewName:
1572 case RegionViewNameHighlight:
1573 case LeftFrameHandle:
1574 case RightFrameHandle:
1575 if (with_selection) {
1576 build_menu_function = &Editor::build_track_selection_context_menu;
1578 build_menu_function = &Editor::build_track_region_context_menu;
1583 if (with_selection) {
1584 build_menu_function = &Editor::build_track_selection_context_menu;
1586 build_menu_function = &Editor::build_track_context_menu;
1591 if (clicked_routeview->track()) {
1592 build_menu_function = &Editor::build_track_context_menu;
1594 build_menu_function = &Editor::build_track_bus_context_menu;
1599 /* probably shouldn't happen but if it does, we don't care */
1603 menu = (this->*build_menu_function)();
1604 menu->set_name ("ArdourContextMenu");
1606 /* now handle specific situations */
1608 switch (item_type) {
1610 case RegionViewName:
1611 case RegionViewNameHighlight:
1612 case LeftFrameHandle:
1613 case RightFrameHandle:
1614 if (!with_selection) {
1615 if (region_edit_menu_split_item) {
1616 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1617 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1619 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1622 if (region_edit_menu_split_multichannel_item) {
1623 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1624 region_edit_menu_split_multichannel_item->set_sensitive (true);
1626 region_edit_menu_split_multichannel_item->set_sensitive (false);
1639 /* probably shouldn't happen but if it does, we don't care */
1643 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1645 /* Bounce to disk */
1647 using namespace Menu_Helpers;
1648 MenuList& edit_items = menu->items();
1650 edit_items.push_back (SeparatorElem());
1652 switch (clicked_routeview->audio_track()->freeze_state()) {
1653 case AudioTrack::NoFreeze:
1654 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1657 case AudioTrack::Frozen:
1658 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1661 case AudioTrack::UnFrozen:
1662 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1668 if (item_type == StreamItem && clicked_routeview) {
1669 clicked_routeview->build_underlay_menu(menu);
1672 /* When the region menu is opened, we setup the actions so that they look right
1675 sensitize_the_right_region_actions (false);
1676 _last_region_menu_was_main = false;
1678 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1679 menu->popup (button, time);
1683 Editor::build_track_context_menu ()
1685 using namespace Menu_Helpers;
1687 MenuList& edit_items = track_context_menu.items();
1690 add_dstream_context_items (edit_items);
1691 return &track_context_menu;
1695 Editor::build_track_bus_context_menu ()
1697 using namespace Menu_Helpers;
1699 MenuList& edit_items = track_context_menu.items();
1702 add_bus_context_items (edit_items);
1703 return &track_context_menu;
1707 Editor::build_track_region_context_menu ()
1709 using namespace Menu_Helpers;
1710 MenuList& edit_items = track_region_context_menu.items();
1713 /* we've just cleared the track region context menu, so the menu that these
1714 two items were on will have disappeared; stop them dangling.
1716 region_edit_menu_split_item = 0;
1717 region_edit_menu_split_multichannel_item = 0;
1719 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1722 boost::shared_ptr<Track> tr;
1723 boost::shared_ptr<Playlist> pl;
1725 if ((tr = rtv->track())) {
1726 add_region_context_items (edit_items, tr);
1730 add_dstream_context_items (edit_items);
1732 return &track_region_context_menu;
1736 Editor::loudness_analyze_region_selection ()
1741 Selection& s (PublicEditor::instance ().get_selection ());
1742 RegionSelection ars = s.regions;
1743 ARDOUR::AnalysisGraph ag (_session);
1744 samplecnt_t total_work = 0;
1746 for (RegionSelection::iterator j = ars.begin (); j != ars.end (); ++j) {
1747 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*j);
1751 if (!boost::dynamic_pointer_cast<AudioRegion> (arv->region ())) {
1754 assert (dynamic_cast<RouteTimeAxisView *> (&arv->get_time_axis_view ()));
1755 total_work += arv->region ()->length ();
1758 SimpleProgressDialog spd (_("Region Loudness Analysis"), sigc::mem_fun (ag, &AnalysisGraph::cancel));
1760 ag.set_total_samples (total_work);
1761 ag.Progress.connect_same_thread (c, boost::bind (&SimpleProgressDialog::update_progress, &spd, _1, _2));
1764 for (RegionSelection::iterator j = ars.begin (); j != ars.end (); ++j) {
1765 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*j);
1769 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (arv->region ());
1773 ag.analyze_region (ar);
1776 if (!ag.canceled ()) {
1777 ExportReport er (_("Audio Report/Analysis"), ag.results ());
1783 Editor::loudness_analyze_range_selection ()
1788 Selection& s (PublicEditor::instance ().get_selection ());
1789 TimeSelection ts = s.time;
1790 ARDOUR::AnalysisGraph ag (_session);
1791 samplecnt_t total_work = 0;
1793 for (TrackSelection::iterator i = s.tracks.begin (); i != s.tracks.end (); ++i) {
1794 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist> ((*i)->playlist ());
1798 RouteUI *rui = dynamic_cast<RouteUI *> (*i);
1802 for (std::list<AudioRange>::iterator j = ts.begin (); j != ts.end (); ++j) {
1803 total_work += j->length ();
1807 SimpleProgressDialog spd (_("Range Loudness Analysis"), sigc::mem_fun (ag, &AnalysisGraph::cancel));
1809 ag.set_total_samples (total_work);
1810 ag.Progress.connect_same_thread (c, boost::bind (&SimpleProgressDialog::update_progress, &spd, _1, _2));
1813 for (TrackSelection::iterator i = s.tracks.begin (); i != s.tracks.end (); ++i) {
1814 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist> ((*i)->playlist ());
1818 RouteUI *rui = dynamic_cast<RouteUI *> (*i);
1822 ag.analyze_range (rui->route (), pl, ts);
1825 if (!ag.canceled ()) {
1826 ExportReport er (_("Audio Report/Analysis"), ag.results ());
1832 Editor::spectral_analyze_region_selection ()
1834 if (analysis_window == 0) {
1835 analysis_window = new AnalysisWindow();
1838 analysis_window->set_session(_session);
1840 analysis_window->show_all();
1843 analysis_window->set_regionmode();
1844 analysis_window->analyze();
1846 analysis_window->present();
1850 Editor::spectral_analyze_range_selection()
1852 if (analysis_window == 0) {
1853 analysis_window = new AnalysisWindow();
1856 analysis_window->set_session(_session);
1858 analysis_window->show_all();
1861 analysis_window->set_rangemode();
1862 analysis_window->analyze();
1864 analysis_window->present();
1868 Editor::build_track_selection_context_menu ()
1870 using namespace Menu_Helpers;
1871 MenuList& edit_items = track_selection_context_menu.items();
1872 edit_items.clear ();
1874 add_selection_context_items (edit_items);
1875 // edit_items.push_back (SeparatorElem());
1876 // add_dstream_context_items (edit_items);
1878 return &track_selection_context_menu;
1882 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr<Track> track)
1884 using namespace Menu_Helpers;
1886 /* OK, stick the region submenu at the top of the list, and then add
1890 RegionSelection rs = get_regions_from_selection_and_entered ();
1892 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1894 if (_popup_region_menu_item == 0) {
1895 _popup_region_menu_item = new MenuItem (menu_item_name, false);
1896 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1897 _popup_region_menu_item->show ();
1899 _popup_region_menu_item->set_label (menu_item_name);
1902 /* No layering allowed in later is higher layering model */
1903 RefPtr<Action> act = ActionManager::get_action (X_("EditorMenu"), X_("RegionMenuLayering"));
1904 if (act && Config->get_layer_model() == LaterHigher) {
1905 act->set_sensitive (false);
1907 act->set_sensitive (true);
1910 const samplepos_t position = get_preferred_edit_position (EDIT_IGNORE_NONE, true);
1912 edit_items.push_back (*_popup_region_menu_item);
1913 if (Config->get_layer_model() == Manual && track->playlist()->count_regions_at (position) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1914 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region-context-menu")->create_menu_item ()));
1916 edit_items.push_back (SeparatorElem());
1919 /** Add context menu items relevant to selection ranges.
1920 * @param edit_items List to add the items to.
1923 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1925 using namespace Menu_Helpers;
1927 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1928 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1930 edit_items.push_back (SeparatorElem());
1931 edit_items.push_back (MenuElem (_("Zoom to Range"), sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_selection), Horizontal)));
1933 edit_items.push_back (SeparatorElem());
1934 edit_items.push_back (MenuElem (_("Loudness Analysis"), sigc::mem_fun(*this, &Editor::loudness_analyze_range_selection)));
1935 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::spectral_analyze_range_selection)));
1937 edit_items.push_back (SeparatorElem());
1939 edit_items.push_back (
1941 _("Move Range Start to Previous Region Boundary"),
1942 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, false)
1946 edit_items.push_back (
1948 _("Move Range Start to Next Region Boundary"),
1949 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, true)
1953 edit_items.push_back (
1955 _("Move Range End to Previous Region Boundary"),
1956 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, false)
1960 edit_items.push_back (
1962 _("Move Range End to Next Region Boundary"),
1963 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, true)
1967 edit_items.push_back (SeparatorElem());
1968 edit_items.push_back (MenuElem (_("Separate"), mem_fun(*this, &Editor::separate_region_from_selection)));
1969 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1971 edit_items.push_back (SeparatorElem());
1972 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1974 edit_items.push_back (SeparatorElem());
1975 edit_items.push_back (MenuElem (_("Set Loop from Selection"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1976 edit_items.push_back (MenuElem (_("Set Punch from Selection"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1977 edit_items.push_back (MenuElem (_("Set Session Start/End from Selection"), sigc::mem_fun(*this, &Editor::set_session_extents_from_selection)));
1979 edit_items.push_back (SeparatorElem());
1980 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1982 edit_items.push_back (SeparatorElem());
1983 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1984 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_range), false)));
1986 edit_items.push_back (SeparatorElem());
1987 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1988 edit_items.push_back (MenuElem (_("Consolidate Range with Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1989 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1990 edit_items.push_back (MenuElem (_("Bounce Range to Region List with Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1991 edit_items.push_back (MenuElem (_("Export Range..."), sigc::mem_fun(*this, &Editor::export_selection)));
1992 if (ARDOUR_UI::instance()->video_timeline->get_duration() > 0) {
1993 edit_items.push_back (MenuElem (_("Export Video Range..."), sigc::bind (sigc::mem_fun(*(ARDOUR_UI::instance()), &ARDOUR_UI::export_video), true)));
1999 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
2001 using namespace Menu_Helpers;
2005 Menu *play_menu = manage (new Menu);
2006 MenuList& play_items = play_menu->items();
2007 play_menu->set_name ("ArdourContextMenu");
2009 play_items.push_back (MenuElem (_("Play from Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
2010 play_items.push_back (MenuElem (_("Play from Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
2011 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
2012 play_items.push_back (SeparatorElem());
2013 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
2015 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2019 Menu *select_menu = manage (new Menu);
2020 MenuList& select_items = select_menu->items();
2021 select_menu->set_name ("ArdourContextMenu");
2023 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2024 select_items.push_back (MenuElem (_("Select All Objects"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_objects), Selection::Set)));
2025 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2026 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2027 select_items.push_back (SeparatorElem());
2028 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
2029 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
2030 select_items.push_back (MenuElem (_("Set Range to Selected Regions"), sigc::mem_fun(*this, &Editor::set_selection_from_region)));
2031 select_items.push_back (SeparatorElem());
2032 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true, true)));
2033 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false, true)));
2034 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2035 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2036 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
2037 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
2038 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
2040 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2044 Menu *cutnpaste_menu = manage (new Menu);
2045 MenuList& cutnpaste_items = cutnpaste_menu->items();
2046 cutnpaste_menu->set_name ("ArdourContextMenu");
2048 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2049 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2050 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f, true)));
2052 cutnpaste_items.push_back (SeparatorElem());
2054 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
2055 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
2057 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
2059 /* Adding new material */
2061 edit_items.push_back (SeparatorElem());
2062 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
2063 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
2067 Menu *nudge_menu = manage (new Menu());
2068 MenuList& nudge_items = nudge_menu->items();
2069 nudge_menu->set_name ("ArdourContextMenu");
2071 edit_items.push_back (SeparatorElem());
2072 nudge_items.push_back (MenuElem (_("Nudge Entire Track Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2073 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2074 nudge_items.push_back (MenuElem (_("Nudge Entire Track Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2075 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2077 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2081 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
2083 using namespace Menu_Helpers;
2087 Menu *play_menu = manage (new Menu);
2088 MenuList& play_items = play_menu->items();
2089 play_menu->set_name ("ArdourContextMenu");
2091 play_items.push_back (MenuElem (_("Play from Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
2092 play_items.push_back (MenuElem (_("Play from Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
2093 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2097 Menu *select_menu = manage (new Menu);
2098 MenuList& select_items = select_menu->items();
2099 select_menu->set_name ("ArdourContextMenu");
2101 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2102 select_items.push_back (MenuElem (_("Select All Objects"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_objects), Selection::Set)));
2103 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2104 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2105 select_items.push_back (SeparatorElem());
2106 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true, true)));
2107 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false, true)));
2108 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2109 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2111 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2115 Menu *cutnpaste_menu = manage (new Menu);
2116 MenuList& cutnpaste_items = cutnpaste_menu->items();
2117 cutnpaste_menu->set_name ("ArdourContextMenu");
2119 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2120 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2121 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f, true)));
2123 Menu *nudge_menu = manage (new Menu());
2124 MenuList& nudge_items = nudge_menu->items();
2125 nudge_menu->set_name ("ArdourContextMenu");
2127 edit_items.push_back (SeparatorElem());
2128 nudge_items.push_back (MenuElem (_("Nudge Entire Track Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2129 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2130 nudge_items.push_back (MenuElem (_("Nudge Entire Track Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2131 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2133 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2137 Editor::grid_type() const
2143 Editor::grid_musical() const
2145 switch (_grid_type) {
2146 case GridTypeBeatDiv32:
2147 case GridTypeBeatDiv28:
2148 case GridTypeBeatDiv24:
2149 case GridTypeBeatDiv20:
2150 case GridTypeBeatDiv16:
2151 case GridTypeBeatDiv14:
2152 case GridTypeBeatDiv12:
2153 case GridTypeBeatDiv10:
2154 case GridTypeBeatDiv8:
2155 case GridTypeBeatDiv7:
2156 case GridTypeBeatDiv6:
2157 case GridTypeBeatDiv5:
2158 case GridTypeBeatDiv4:
2159 case GridTypeBeatDiv3:
2160 case GridTypeBeatDiv2:
2165 case GridTypeTimecode:
2166 case GridTypeMinSec:
2167 case GridTypeCDFrame:
2174 Editor::grid_nonmusical() const
2176 switch (_grid_type) {
2177 case GridTypeTimecode:
2178 case GridTypeMinSec:
2179 case GridTypeCDFrame:
2181 case GridTypeBeatDiv32:
2182 case GridTypeBeatDiv28:
2183 case GridTypeBeatDiv24:
2184 case GridTypeBeatDiv20:
2185 case GridTypeBeatDiv16:
2186 case GridTypeBeatDiv14:
2187 case GridTypeBeatDiv12:
2188 case GridTypeBeatDiv10:
2189 case GridTypeBeatDiv8:
2190 case GridTypeBeatDiv7:
2191 case GridTypeBeatDiv6:
2192 case GridTypeBeatDiv5:
2193 case GridTypeBeatDiv4:
2194 case GridTypeBeatDiv3:
2195 case GridTypeBeatDiv2:
2204 Editor::snap_mode() const
2210 Editor::show_rulers_for_grid ()
2212 /* show appropriate rulers for this grid setting. */
2213 if (grid_musical()) {
2214 ruler_tempo_action->set_active(true);
2215 ruler_meter_action->set_active(true);
2216 ruler_bbt_action->set_active(true);
2218 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2219 ruler_timecode_action->set_active(false);
2220 ruler_minsec_action->set_active(false);
2221 ruler_samples_action->set_active(false);
2223 } else if (_grid_type == GridTypeTimecode) {
2224 ruler_timecode_action->set_active(true);
2226 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2227 ruler_tempo_action->set_active(false);
2228 ruler_meter_action->set_active(false);
2229 ruler_bbt_action->set_active(false);
2230 ruler_minsec_action->set_active(false);
2231 ruler_samples_action->set_active(false);
2233 } else if (_grid_type == GridTypeMinSec) {
2234 ruler_minsec_action->set_active(true);
2236 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2237 ruler_tempo_action->set_active(false);
2238 ruler_meter_action->set_active(false);
2239 ruler_bbt_action->set_active(false);
2240 ruler_timecode_action->set_active(false);
2241 ruler_samples_action->set_active(false);
2243 } else if (_grid_type == GridTypeCDFrame) {
2244 ruler_cd_marker_action->set_active(true);
2245 ruler_minsec_action->set_active(true);
2247 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2248 ruler_tempo_action->set_active(false);
2249 ruler_meter_action->set_active(false);
2250 ruler_bbt_action->set_active(false);
2251 ruler_timecode_action->set_active(false);
2252 ruler_samples_action->set_active(false);
2258 Editor::set_grid_to (GridType gt)
2260 if (_grid_type == gt) { // already set
2264 unsigned int grid_ind = (unsigned int)gt;
2266 if (internal_editing() && UIConfiguration::instance().get_grid_follows_internal()) {
2267 internal_grid_type = gt;
2269 pre_internal_grid_type = gt;
2274 if (grid_ind > grid_type_strings.size() - 1) {
2276 _grid_type = (GridType)grid_ind;
2279 string str = grid_type_strings[grid_ind];
2281 if (str != grid_type_selector.get_text()) {
2282 grid_type_selector.set_text (str);
2285 if (UIConfiguration::instance().get_show_grids_ruler()) {
2286 show_rulers_for_grid ();
2291 if (grid_musical()) {
2292 compute_bbt_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
2293 update_tempo_based_rulers ();
2296 mark_region_boundary_cache_dirty ();
2298 redisplay_grid (false);
2300 SnapChanged (); /* EMIT SIGNAL */
2304 Editor::set_snap_mode (SnapMode mode)
2306 if (internal_editing()) {
2307 internal_snap_mode = mode;
2309 pre_internal_snap_mode = mode;
2314 if (_snap_mode == SnapOff) {
2315 snap_mode_button.set_active_state (Gtkmm2ext::Off);
2317 snap_mode_button.set_active_state (Gtkmm2ext::ExplicitActive);
2324 Editor::set_edit_point_preference (EditPoint ep, bool force)
2326 bool changed = (_edit_point != ep);
2329 if (Profile->get_mixbus())
2330 if (ep == EditAtSelectedMarker)
2331 ep = EditAtPlayhead;
2333 string str = edit_point_strings[(int)ep];
2334 if (str != edit_point_selector.get_text ()) {
2335 edit_point_selector.set_text (str);
2338 update_all_enter_cursors();
2340 if (!force && !changed) {
2344 const char* action=NULL;
2346 switch (_edit_point) {
2347 case EditAtPlayhead:
2348 action = "edit-at-playhead";
2350 case EditAtSelectedMarker:
2351 action = "edit-at-marker";
2354 action = "edit-at-mouse";
2358 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2360 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2364 bool in_track_canvas;
2366 if (!mouse_sample (foo, in_track_canvas)) {
2367 in_track_canvas = false;
2370 reset_canvas_action_sensitivity (in_track_canvas);
2371 sensitize_the_right_region_actions (false);
2377 Editor::set_state (const XMLNode& node, int version)
2380 PBD::Unwinder<bool> nsi (no_save_instant, true);
2383 Tabbable::set_state (node, version);
2386 if (_session && node.get_property ("playhead", ph_pos)) {
2388 playhead_cursor->set_position (ph_pos);
2390 warning << _("Playhead position stored with a negative value - ignored (use zero instead)") << endmsg;
2391 playhead_cursor->set_position (0);
2394 playhead_cursor->set_position (0);
2397 node.get_property ("mixer-width", editor_mixer_strip_width);
2399 node.get_property ("zoom-focus", zoom_focus);
2400 zoom_focus_selection_done (zoom_focus);
2403 if (node.get_property ("zoom", z)) {
2404 /* older versions of ardour used floating point samples_per_pixel */
2405 reset_zoom (llrintf (z));
2407 reset_zoom (samples_per_pixel);
2411 if (node.get_property ("visible-track-count", cnt)) {
2412 set_visible_track_count (cnt);
2416 if (!node.get_property ("grid-type", grid_type)) {
2417 grid_type = _grid_type;
2419 set_grid_to (grid_type);
2422 if (node.get_property ("snap-mode", sm)) {
2423 snap_mode_selection_done(sm);
2424 /* set text of Dropdown. in case _snap_mode == SnapOff (default)
2425 * snap_mode_selection_done() will only mark an already active item as active
2426 * which does not trigger set_text().
2430 set_snap_mode (_snap_mode);
2433 node.get_property ("internal-grid-type", internal_grid_type);
2434 node.get_property ("internal-snap-mode", internal_snap_mode);
2435 node.get_property ("pre-internal-grid-type", pre_internal_grid_type);
2436 node.get_property ("pre-internal-snap-mode", pre_internal_snap_mode);
2439 if (node.get_property ("mouse-mode", mm_str)) {
2440 MouseMode m = str2mousemode(mm_str);
2441 set_mouse_mode (m, true);
2443 set_mouse_mode (MouseObject, true);
2447 if (node.get_property ("left-frame", lf_pos)) {
2451 reset_x_origin (lf_pos);
2455 if (node.get_property ("y-origin", y_origin)) {
2456 reset_y_origin (y_origin);
2459 if (node.get_property ("join-object-range", yn)) {
2460 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object-range"));
2462 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2463 tact->set_active (!yn);
2464 tact->set_active (yn);
2466 set_mouse_mode(mouse_mode, true);
2470 if (node.get_property ("edit-point", ep)) {
2471 set_edit_point_preference (ep, true);
2473 set_edit_point_preference (_edit_point);
2476 if (node.get_property ("follow-playhead", yn)) {
2477 set_follow_playhead (yn);
2480 if (node.get_property ("stationary-playhead", yn)) {
2481 set_stationary_playhead (yn);
2484 RegionListSortType sort_type;
2485 if (node.get_property ("region-list-sort-type", sort_type)) {
2486 _regions->reset_sort_type (sort_type, true);
2489 if (node.get_property ("show-editor-mixer", yn)) {
2491 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2494 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2496 /* do it twice to force the change */
2498 tact->set_active (!yn);
2499 tact->set_active (yn);
2502 if (node.get_property ("show-editor-list", yn)) {
2504 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2507 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2509 /* do it twice to force the change */
2511 tact->set_active (!yn);
2512 tact->set_active (yn);
2516 if (node.get_property (X_("editor-list-page"), el_page)) {
2517 _the_notebook.set_current_page (el_page);
2520 if (node.get_property (X_("show-marker-lines"), yn)) {
2521 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2523 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2525 tact->set_active (!yn);
2526 tact->set_active (yn);
2529 XMLNodeList children = node.children ();
2530 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2531 selection->set_state (**i, Stateful::current_state_version);
2532 _regions->set_state (**i);
2533 _locations->set_state (**i);
2536 if (node.get_property ("maximised", yn)) {
2537 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMaximalEditor"));
2539 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2540 bool fs = tact && tact->get_active();
2542 ActionManager::do_action ("Common", "ToggleMaximalEditor");
2546 samplepos_t nudge_clock_value;
2547 if (node.get_property ("nudge-clock-value", nudge_clock_value)) {
2548 nudge_clock->set (nudge_clock_value);
2550 nudge_clock->set_mode (AudioClock::Timecode);
2551 nudge_clock->set (_session->sample_rate() * 5, true);
2556 * Not all properties may have been in XML, but
2557 * those that are linked to a private variable may need changing
2561 act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2562 yn = _follow_playhead;
2564 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2565 if (tact->get_active() != yn) {
2566 tact->set_active (yn);
2570 act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2571 yn = _stationary_playhead;
2573 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2574 if (tact->get_active() != yn) {
2575 tact->set_active (yn);
2584 Editor::get_state ()
2586 XMLNode* node = new XMLNode (X_("Editor"));
2588 node->set_property ("id", id().to_s ());
2590 node->add_child_nocopy (Tabbable::get_state());
2592 node->set_property("edit-horizontal-pane-pos", edit_pane.get_divider ());
2593 node->set_property("notebook-shrunk", _notebook_shrunk);
2594 node->set_property("edit-vertical-pane-pos", editor_summary_pane.get_divider());
2596 maybe_add_mixer_strip_width (*node);
2598 node->set_property ("zoom-focus", zoom_focus);
2600 node->set_property ("zoom", samples_per_pixel);
2601 node->set_property ("grid-type", _grid_type);
2602 node->set_property ("snap-mode", _snap_mode);
2603 node->set_property ("internal-grid-type", internal_grid_type);
2604 node->set_property ("internal-snap-mode", internal_snap_mode);
2605 node->set_property ("pre-internal-grid-type", pre_internal_grid_type);
2606 node->set_property ("pre-internal-snap-mode", pre_internal_snap_mode);
2607 node->set_property ("edit-point", _edit_point);
2608 node->set_property ("visible-track-count", _visible_track_count);
2610 node->set_property ("playhead", playhead_cursor->current_sample ());
2611 node->set_property ("left-frame", _leftmost_sample);
2612 node->set_property ("y-origin", vertical_adjustment.get_value ());
2614 node->set_property ("maximised", _maximised);
2615 node->set_property ("follow-playhead", _follow_playhead);
2616 node->set_property ("stationary-playhead", _stationary_playhead);
2617 node->set_property ("region-list-sort-type", _regions->sort_type ());
2618 node->set_property ("mouse-mode", mouse_mode);
2619 node->set_property ("join-object-range", smart_mode_action->get_active ());
2621 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2623 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2624 node->set_property (X_("show-editor-mixer"), tact->get_active());
2627 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2629 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2630 node->set_property (X_("show-editor-list"), tact->get_active());
2633 node->set_property (X_("editor-list-page"), _the_notebook.get_current_page ());
2635 if (button_bindings) {
2636 XMLNode* bb = new XMLNode (X_("Buttons"));
2637 button_bindings->save (*bb);
2638 node->add_child_nocopy (*bb);
2641 node->set_property (X_("show-marker-lines"), _show_marker_lines);
2643 node->add_child_nocopy (selection->get_state ());
2644 node->add_child_nocopy (_regions->get_state ());
2646 node->set_property ("nudge-clock-value", nudge_clock->current_duration());
2648 node->add_child_nocopy (_locations->get_state ());
2653 /** if @param trackview_relative_offset is true, @param y y is an offset into the trackview area, in pixel units
2654 * if @param trackview_relative_offset is false, @param y y is a global canvas * coordinate, in pixel units
2656 * @return pair: TimeAxisView that y is over, layer index.
2658 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2659 * in stacked or expanded region display mode, otherwise 0.
2661 std::pair<TimeAxisView *, double>
2662 Editor::trackview_by_y_position (double y, bool trackview_relative_offset) const
2664 if (!trackview_relative_offset) {
2665 y -= _trackview_group->canvas_origin().y;
2669 return std::make_pair ((TimeAxisView *) 0, 0);
2672 for (TrackViewList::const_iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2674 std::pair<TimeAxisView*, double> const r = (*iter)->covers_y_position (y);
2681 return std::make_pair ((TimeAxisView *) 0, 0);
2685 Editor::set_snapped_cursor_position (samplepos_t pos)
2687 if (_edit_point == EditAtMouse) {
2688 snapped_cursor->set_position(pos);
2693 /** Snap a position to the grid, if appropriate, taking into account current
2694 * grid settings and also the state of any snap modifier keys that may be pressed.
2695 * @param start Position to snap.
2696 * @param event Event to get current key modifier information from, or 0.
2699 Editor::snap_to_with_modifier (MusicSample& start, GdkEvent const * event, RoundMode direction, SnapPref pref)
2701 if (!_session || !event) {
2705 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2706 if (_snap_mode == SnapOff) {
2707 snap_to_internal (start, direction, pref);
2709 start.set (start.sample, 0);
2712 if (_snap_mode != SnapOff) {
2713 snap_to_internal (start, direction, pref);
2714 } else if (ArdourKeyboard::indicates_snap_delta (event->button.state)) {
2715 /* SnapOff, but we pressed the snap_delta modifier */
2716 snap_to_internal (start, direction, pref);
2718 start.set (start.sample, 0);
2724 Editor::snap_to (MusicSample& start, RoundMode direction, SnapPref pref, bool ensure_snap)
2726 if (!_session || (_snap_mode == SnapOff && !ensure_snap)) {
2727 start.set (start.sample, 0);
2731 snap_to_internal (start, direction, pref, ensure_snap);
2735 check_best_snap (samplepos_t presnap, samplepos_t &test, samplepos_t &dist, samplepos_t &best)
2737 samplepos_t diff = abs (test - presnap);
2743 test = max_samplepos; // reset this so it doesn't get accidentally reused
2747 Editor::snap_to_timecode (MusicSample presnap, RoundMode direction, SnapPref gpref)
2749 samplepos_t start = presnap.sample;
2750 const samplepos_t one_timecode_second = (samplepos_t)(rint(_session->timecode_frames_per_second()) * _session->samples_per_timecode_frame());
2751 samplepos_t one_timecode_minute = (samplepos_t)(rint(_session->timecode_frames_per_second()) * _session->samples_per_timecode_frame() * 60);
2753 TimecodeRulerScale scale = (gpref != SnapToGrid_Unscaled) ? timecode_ruler_scale : timecode_show_samples;
2756 case timecode_show_bits:
2757 case timecode_show_samples:
2758 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2759 fmod((double)start, (double)_session->samples_per_timecode_frame()) == 0) {
2760 /* start is already on a whole timecode frame, do nothing */
2761 } else if (((direction == 0) && (fmod((double)start, (double)_session->samples_per_timecode_frame()) > (_session->samples_per_timecode_frame() / 2))) || (direction > 0)) {
2762 start = (samplepos_t) (ceil ((double) start / _session->samples_per_timecode_frame()) * _session->samples_per_timecode_frame());
2764 start = (samplepos_t) (floor ((double) start / _session->samples_per_timecode_frame()) * _session->samples_per_timecode_frame());
2768 case timecode_show_seconds:
2769 if (_session->config.get_timecode_offset_negative()) {
2770 start += _session->config.get_timecode_offset ();
2772 start -= _session->config.get_timecode_offset ();
2774 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2775 (start % one_timecode_second == 0)) {
2776 /* start is already on a whole second, do nothing */
2777 } else if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2778 start = (samplepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2780 start = (samplepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2783 if (_session->config.get_timecode_offset_negative()) {
2784 start -= _session->config.get_timecode_offset ();
2786 start += _session->config.get_timecode_offset ();
2790 case timecode_show_minutes:
2791 case timecode_show_hours:
2792 case timecode_show_many_hours:
2793 if (_session->config.get_timecode_offset_negative()) {
2794 start += _session->config.get_timecode_offset ();
2796 start -= _session->config.get_timecode_offset ();
2798 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2799 (start % one_timecode_minute == 0)) {
2800 /* start is already on a whole minute, do nothing */
2801 } else if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2802 start = (samplepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2804 start = (samplepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2806 if (_session->config.get_timecode_offset_negative()) {
2807 start -= _session->config.get_timecode_offset ();
2809 start += _session->config.get_timecode_offset ();
2813 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2816 MusicSample ret(start,0);
2821 Editor::snap_to_minsec (MusicSample presnap, RoundMode direction, SnapPref gpref)
2823 MusicSample ret(presnap);
2825 const samplepos_t one_second = _session->sample_rate();
2826 const samplepos_t one_minute = one_second * 60;
2827 const samplepos_t one_hour = one_minute * 60;
2829 MinsecRulerScale scale = (gpref != SnapToGrid_Unscaled) ? minsec_ruler_scale : minsec_show_seconds;
2832 case minsec_show_msecs:
2833 case minsec_show_seconds: {
2834 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2835 presnap.sample % one_second == 0) {
2836 /* start is already on a whole second, do nothing */
2837 } else if (((direction == 0) && (presnap.sample % one_second > one_second / 2)) || (direction > 0)) {
2838 ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_second) * one_second;
2840 ret.sample = (samplepos_t) floor ((double) presnap.sample / one_second) * one_second;
2844 case minsec_show_minutes: {
2845 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2846 presnap.sample % one_minute == 0) {
2847 /* start is already on a whole minute, do nothing */
2848 } else if (((direction == 0) && (presnap.sample % one_minute > one_minute / 2)) || (direction > 0)) {
2849 ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_minute) * one_minute;
2851 ret.sample = (samplepos_t) floor ((double) presnap.sample / one_minute) * one_minute;
2856 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2857 presnap.sample % one_hour == 0) {
2858 /* start is already on a whole hour, do nothing */
2859 } else if (((direction == 0) && (presnap.sample % one_hour > one_hour / 2)) || (direction > 0)) {
2860 ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_hour) * one_hour;
2862 ret.sample = (samplepos_t) floor ((double) presnap.sample / one_hour) * one_hour;
2871 Editor::snap_to_cd_frames (MusicSample presnap, RoundMode direction, SnapPref gpref)
2873 if ((gpref != SnapToGrid_Unscaled) && (minsec_ruler_scale != minsec_show_msecs)) {
2874 return snap_to_minsec (presnap, direction, gpref);
2877 const samplepos_t one_second = _session->sample_rate();
2879 MusicSample ret(presnap);
2881 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2882 presnap.sample % (one_second/75) == 0) {
2883 /* start is already on a whole CD sample, do nothing */
2884 } else if (((direction == 0) && (presnap.sample % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2885 ret.sample = (samplepos_t) ceil ((double) presnap.sample / (one_second / 75)) * (one_second / 75);
2887 ret.sample = (samplepos_t) floor ((double) presnap.sample / (one_second / 75)) * (one_second / 75);
2894 Editor::snap_to_bbt (MusicSample presnap, RoundMode direction, SnapPref gpref)
2896 MusicSample ret(presnap);
2898 if (gpref != SnapToGrid_Unscaled) { // use the visual grid lines which are limited by the zoom scale that the user selected
2901 switch (_grid_type) {
2902 case GridTypeBeatDiv3:
2903 case GridTypeBeatDiv6:
2904 case GridTypeBeatDiv12:
2905 case GridTypeBeatDiv24:
2908 case GridTypeBeatDiv5:
2909 case GridTypeBeatDiv10:
2910 case GridTypeBeatDiv20:
2913 case GridTypeBeatDiv7:
2914 case GridTypeBeatDiv14:
2915 case GridTypeBeatDiv28:
2922 BBTRulerScale scale = bbt_ruler_scale;
2929 ret = _session->tempo_map().round_to_bar (presnap.sample, direction);
2931 case bbt_show_quarters:
2932 ret = _session->tempo_map().round_to_beat (presnap.sample, direction);
2934 case bbt_show_eighths:
2935 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 1 * divisor, direction);
2937 case bbt_show_sixteenths:
2938 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 2 * divisor, direction);
2940 case bbt_show_thirtyseconds:
2941 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 4 * divisor, direction);
2945 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, get_grid_beat_divisions(_grid_type), direction);
2952 Editor::snap_to_grid (MusicSample presnap, RoundMode direction, SnapPref gpref)
2954 MusicSample ret(presnap);
2956 if (grid_musical()) {
2957 ret = snap_to_bbt (presnap, direction, gpref);
2960 switch (_grid_type) {
2961 case GridTypeTimecode:
2962 ret = snap_to_timecode(presnap, direction, gpref);
2964 case GridTypeMinSec:
2965 ret = snap_to_minsec(presnap, direction, gpref);
2967 case GridTypeCDFrame:
2968 ret = snap_to_cd_frames(presnap, direction, gpref);
2978 Editor::snap_to_marker (samplepos_t presnap, RoundMode direction)
2984 _session->locations()->marks_either_side (presnap, before, after);
2986 if (before == max_samplepos && after == max_samplepos) {
2987 /* No marks to snap to, so just don't snap */
2989 } else if (before == max_samplepos) {
2991 } else if (after == max_samplepos) {
2994 if ((direction == RoundUpMaybe || direction == RoundUpAlways)) {
2996 } else if ((direction == RoundDownMaybe || direction == RoundDownAlways)) {
2998 } else if (direction == 0) {
2999 if ((presnap - before) < (after - presnap)) {
3011 Editor::snap_to_internal (MusicSample& start, RoundMode direction, SnapPref pref, bool ensure_snap)
3013 const samplepos_t presnap = start.sample;
3015 samplepos_t test = max_samplepos; // for each snap, we'll use this value
3016 samplepos_t dist = max_samplepos; // this records the distance of the best snap result we've found so far
3017 samplepos_t best = max_samplepos; // this records the best snap-result we've found so far
3019 /* check snap-to-marker */
3020 if ((pref == SnapToAny_Visual) && UIConfiguration::instance().get_snap_to_marks()) {
3021 test = snap_to_marker (presnap, direction);
3022 check_best_snap(presnap, test, dist, best);
3025 /* check snap-to-region-{start/end/sync} */
3027 (pref == SnapToAny_Visual) &&
3028 (UIConfiguration::instance().get_snap_to_region_start() || UIConfiguration::instance().get_snap_to_region_end() || UIConfiguration::instance().get_snap_to_region_sync())
3030 if (!region_boundary_cache.empty()) {
3032 vector<samplepos_t>::iterator prev = region_boundary_cache.begin();
3033 vector<samplepos_t>::iterator next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), presnap);
3034 if (next != region_boundary_cache.begin ()) {
3039 if ((direction == RoundUpMaybe || direction == RoundUpAlways))
3041 else if ((direction == RoundDownMaybe || direction == RoundDownAlways))
3043 else if (direction == 0) {
3044 if ((presnap - *prev) < (*next - presnap)) {
3053 check_best_snap(presnap, test, dist, best);
3057 if (UIConfiguration::instance().get_snap_to_grid() && (_grid_type != GridTypeNone)) {
3058 MusicSample pre(presnap, 0);
3059 MusicSample post = snap_to_grid (pre, direction, pref);
3060 check_best_snap(presnap, post.sample, dist, best);
3063 /* now check "magnetic" state: is the grid within reasonable on-screen distance to trigger a snap?
3064 * this also helps to avoid snapping to somewhere the user can't see. (i.e.: I clicked on a region and it disappeared!!)
3065 * ToDo: Perhaps this should only occur if EditPointMouse?
3067 int snap_threshold_s = pixel_to_sample(UIConfiguration::instance().get_snap_threshold());
3069 start.set (best, 0);
3071 } else if (presnap > best) {
3072 if (presnap > (best+ snap_threshold_s)) {
3075 } else if (presnap < best) {
3076 if (presnap < (best - snap_threshold_s)) {
3081 start.set (best, 0);
3086 Editor::setup_toolbar ()
3088 HBox* mode_box = manage(new HBox);
3089 mode_box->set_border_width (2);
3090 mode_box->set_spacing(2);
3092 HBox* mouse_mode_box = manage (new HBox);
3093 HBox* mouse_mode_hbox = manage (new HBox);
3094 VBox* mouse_mode_vbox = manage (new VBox);
3095 Alignment* mouse_mode_align = manage (new Alignment);
3097 Glib::RefPtr<SizeGroup> mouse_mode_size_group = SizeGroup::create (SIZE_GROUP_VERTICAL);
3098 mouse_mode_size_group->add_widget (smart_mode_button);
3099 mouse_mode_size_group->add_widget (mouse_move_button);
3100 mouse_mode_size_group->add_widget (mouse_cut_button);
3101 mouse_mode_size_group->add_widget (mouse_select_button);
3102 mouse_mode_size_group->add_widget (mouse_timefx_button);
3103 mouse_mode_size_group->add_widget (mouse_audition_button);
3104 mouse_mode_size_group->add_widget (mouse_draw_button);
3105 mouse_mode_size_group->add_widget (mouse_content_button);
3107 if (!Profile->get_mixbus()) {
3108 mouse_mode_size_group->add_widget (zoom_in_button);
3109 mouse_mode_size_group->add_widget (zoom_out_button);
3110 mouse_mode_size_group->add_widget (zoom_out_full_button);
3111 mouse_mode_size_group->add_widget (zoom_focus_selector);
3112 mouse_mode_size_group->add_widget (tav_shrink_button);
3113 mouse_mode_size_group->add_widget (tav_expand_button);
3115 mouse_mode_size_group->add_widget (zoom_preset_selector);
3116 mouse_mode_size_group->add_widget (visible_tracks_selector);
3119 mouse_mode_size_group->add_widget (grid_type_selector);
3120 mouse_mode_size_group->add_widget (snap_mode_button);
3122 mouse_mode_size_group->add_widget (edit_point_selector);
3123 mouse_mode_size_group->add_widget (edit_mode_selector);
3125 mouse_mode_size_group->add_widget (*nudge_clock);
3126 mouse_mode_size_group->add_widget (nudge_forward_button);
3127 mouse_mode_size_group->add_widget (nudge_backward_button);
3129 mouse_mode_hbox->set_spacing (2);
3131 if (!ARDOUR::Profile->get_trx()) {
3132 mouse_mode_hbox->pack_start (smart_mode_button, false, false);
3135 mouse_mode_hbox->pack_start (mouse_move_button, false, false);
3136 mouse_mode_hbox->pack_start (mouse_select_button, false, false);
3138 if (!ARDOUR::Profile->get_mixbus()) {
3139 mouse_mode_hbox->pack_start (mouse_cut_button, false, false);
3140 mouse_mode_hbox->pack_start (mouse_audition_button, false, false);
3143 if (!ARDOUR::Profile->get_trx()) {
3144 mouse_mode_hbox->pack_start (mouse_timefx_button, false, false);
3145 mouse_mode_hbox->pack_start (mouse_draw_button, false, false);
3146 mouse_mode_hbox->pack_start (mouse_content_button, false, false);
3149 mouse_mode_vbox->pack_start (*mouse_mode_hbox);
3151 mouse_mode_align->add (*mouse_mode_vbox);
3152 mouse_mode_align->set (0.5, 1.0, 0.0, 0.0);
3154 mouse_mode_box->pack_start (*mouse_mode_align, false, false);
3156 edit_mode_selector.set_name ("mouse mode button");
3158 if (!ARDOUR::Profile->get_trx()) {
3159 mode_box->pack_start (edit_mode_selector, false, false);
3160 mode_box->pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3161 mode_box->pack_start (edit_point_selector, false, false);
3162 mode_box->pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3165 mode_box->pack_start (*mouse_mode_box, false, false);
3169 _zoom_box.set_spacing (2);
3170 _zoom_box.set_border_width (2);
3174 zoom_preset_selector.set_name ("zoom button");
3175 zoom_preset_selector.set_icon (ArdourIcon::ZoomExpand);
3177 zoom_in_button.set_name ("zoom button");
3178 zoom_in_button.set_icon (ArdourIcon::ZoomIn);
3179 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-in"));
3180 zoom_in_button.set_related_action (act);
3182 zoom_out_button.set_name ("zoom button");
3183 zoom_out_button.set_icon (ArdourIcon::ZoomOut);
3184 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-out"));
3185 zoom_out_button.set_related_action (act);
3187 zoom_out_full_button.set_name ("zoom button");
3188 zoom_out_full_button.set_icon (ArdourIcon::ZoomFull);
3189 act = ActionManager::get_action (X_("Editor"), X_("zoom-to-session"));
3190 zoom_out_full_button.set_related_action (act);
3192 zoom_focus_selector.set_name ("zoom button");
3194 if (ARDOUR::Profile->get_mixbus()) {
3195 _zoom_box.pack_start (zoom_preset_selector, false, false);
3196 } else if (ARDOUR::Profile->get_trx()) {
3197 mode_box->pack_start (zoom_out_button, false, false);
3198 mode_box->pack_start (zoom_in_button, false, false);
3200 _zoom_box.pack_start (zoom_out_button, false, false);
3201 _zoom_box.pack_start (zoom_in_button, false, false);
3202 _zoom_box.pack_start (zoom_out_full_button, false, false);
3203 _zoom_box.pack_start (zoom_focus_selector, false, false);
3206 /* Track zoom buttons */
3207 _track_box.set_spacing (2);
3208 _track_box.set_border_width (2);
3210 visible_tracks_selector.set_name ("zoom button");
3211 if (Profile->get_mixbus()) {
3212 visible_tracks_selector.set_icon (ArdourIcon::TimeAxisExpand);
3214 set_size_request_to_display_given_text (visible_tracks_selector, _("All"), 30, 2);
3217 tav_expand_button.set_name ("zoom button");
3218 tav_expand_button.set_icon (ArdourIcon::TimeAxisExpand);
3219 act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
3220 tav_expand_button.set_related_action (act);
3222 tav_shrink_button.set_name ("zoom button");
3223 tav_shrink_button.set_icon (ArdourIcon::TimeAxisShrink);
3224 act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
3225 tav_shrink_button.set_related_action (act);
3227 if (ARDOUR::Profile->get_mixbus()) {
3228 _track_box.pack_start (visible_tracks_selector);
3229 } else if (ARDOUR::Profile->get_trx()) {
3230 _track_box.pack_start (tav_shrink_button);
3231 _track_box.pack_start (tav_expand_button);
3233 _track_box.pack_start (visible_tracks_selector);
3234 _track_box.pack_start (tav_shrink_button);
3235 _track_box.pack_start (tav_expand_button);
3238 snap_box.set_spacing (2);
3239 snap_box.set_border_width (2);
3241 grid_type_selector.set_name ("mouse mode button");
3243 snap_mode_button.set_name ("mouse mode button");
3245 edit_point_selector.set_name ("mouse mode button");
3247 snap_box.pack_start (snap_mode_button, false, false);
3248 snap_box.pack_start (grid_type_selector, false, false);
3252 HBox *nudge_box = manage (new HBox);
3253 nudge_box->set_spacing (2);
3254 nudge_box->set_border_width (2);
3256 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
3257 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
3259 nudge_box->pack_start (nudge_backward_button, false, false);
3260 nudge_box->pack_start (nudge_forward_button, false, false);
3261 nudge_box->pack_start (*nudge_clock, false, false);
3264 /* Pack everything in... */
3266 toolbar_hbox.set_spacing (2);
3267 toolbar_hbox.set_border_width (2);
3269 ArdourWidgets::ArdourDropShadow *tool_shadow = manage (new (ArdourWidgets::ArdourDropShadow));
3270 tool_shadow->set_size_request (4, -1);
3271 tool_shadow->show();
3273 ebox_hpacker.pack_start (*tool_shadow, false, false);
3274 ebox_hpacker.pack_start(ebox_vpacker, true, true);
3276 Gtk::EventBox* spacer = manage (new Gtk::EventBox); // extra space under the mouse toolbar, for aesthetics
3277 spacer->set_name("EditorWindow");
3278 spacer->set_size_request(-1,4);
3281 ebox_vpacker.pack_start(toolbar_hbox, false, false);
3282 ebox_vpacker.pack_start(*spacer, false, false);
3283 ebox_vpacker.show();
3285 toolbar_hbox.pack_start (*mode_box, false, false);
3287 if (!ARDOUR::Profile->get_trx()) {
3289 toolbar_hbox.pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3291 toolbar_hbox.pack_start (snap_box, false, false);
3293 toolbar_hbox.pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3295 toolbar_hbox.pack_start (*nudge_box, false, false);
3297 toolbar_hbox.pack_end (_zoom_box, false, false, 2);
3299 toolbar_hbox.pack_end (*(manage (new ArdourVSpacer ())), false, false, 3);
3301 toolbar_hbox.pack_end (_track_box, false, false);
3305 toolbar_hbox.show_all ();
3309 Editor::build_edit_point_menu ()
3311 using namespace Menu_Helpers;
3313 edit_point_selector.AddMenuElem (MenuElem (edit_point_strings[(int)EditAtPlayhead], sigc::bind (sigc::mem_fun(*this, &Editor::edit_point_selection_done), (EditPoint) EditAtPlayhead)));
3314 if(!Profile->get_mixbus())
3315 edit_point_selector.AddMenuElem (MenuElem (edit_point_strings[(int)EditAtSelectedMarker], sigc::bind (sigc::mem_fun(*this, &Editor::edit_point_selection_done), (EditPoint) EditAtSelectedMarker)));
3316 edit_point_selector.AddMenuElem (MenuElem (edit_point_strings[(int)EditAtMouse], sigc::bind (sigc::mem_fun(*this, &Editor::edit_point_selection_done), (EditPoint) EditAtMouse)));
3318 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_TRIANGLE_WIDTH, 2);
3322 Editor::build_edit_mode_menu ()
3324 using namespace Menu_Helpers;
3326 edit_mode_selector.AddMenuElem (MenuElem (edit_mode_strings[(int)Slide], sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Slide)));
3327 edit_mode_selector.AddMenuElem (MenuElem (edit_mode_strings[(int)Ripple], sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Ripple)));
3328 edit_mode_selector.AddMenuElem (MenuElem (edit_mode_strings[(int)Lock], sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Lock)));
3329 /* Note: Splice was removed */
3331 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_TRIANGLE_WIDTH, 2);
3335 Editor::build_grid_type_menu ()
3337 using namespace Menu_Helpers;
3339 /* main grid: bars, quarter-notes, etc */
3340 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeNone], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeNone)));
3341 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBar], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBar)));
3342 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeat], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeat)));
3343 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv2], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv2)));
3344 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv4], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv4)));
3345 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv8], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv8)));
3346 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv16], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv16)));
3347 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv32], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv32)));
3350 grid_type_selector.AddMenuElem(SeparatorElem());
3351 Gtk::Menu *_triplet_menu = manage (new Menu);
3352 MenuList& triplet_items (_triplet_menu->items());
3354 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv3], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv3)));
3355 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv6], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv6)));
3356 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv12], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv12)));
3357 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv24], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv24)));
3359 grid_type_selector.AddMenuElem (Menu_Helpers::MenuElem (_("Triplets"), *_triplet_menu));
3361 /* quintuplet grid */
3362 Gtk::Menu *_quintuplet_menu = manage (new Menu);
3363 MenuList& quintuplet_items (_quintuplet_menu->items());
3365 quintuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv5], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv5)));
3366 quintuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv10], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv10)));
3367 quintuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv20], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv20)));
3369 grid_type_selector.AddMenuElem (Menu_Helpers::MenuElem (_("Quintuplets"), *_quintuplet_menu));
3371 /* septuplet grid */
3372 Gtk::Menu *_septuplet_menu = manage (new Menu);
3373 MenuList& septuplet_items (_septuplet_menu->items());
3375 septuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv7], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv7)));
3376 septuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv14], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv14)));
3377 septuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv28], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv28)));
3379 grid_type_selector.AddMenuElem (Menu_Helpers::MenuElem (_("Septuplets"), *_septuplet_menu));
3381 grid_type_selector.AddMenuElem(SeparatorElem());
3382 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeTimecode], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeTimecode)));
3383 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeMinSec], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeMinSec)));
3384 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeCDFrame], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeCDFrame)));
3388 Editor::setup_tooltips ()
3390 set_tooltip (smart_mode_button, _("Smart Mode (add range functions to Grab Mode)"));
3391 set_tooltip (mouse_move_button, _("Grab Mode (select/move objects)"));
3392 set_tooltip (mouse_cut_button, _("Cut Mode (split regions)"));
3393 set_tooltip (mouse_select_button, _("Range Mode (select time ranges)"));
3394 set_tooltip (mouse_draw_button, _("Draw Mode (draw and edit gain/notes/automation)"));
3395 set_tooltip (mouse_timefx_button, _("Stretch Mode (time-stretch audio and midi regions, preserving pitch)"));
3396 set_tooltip (mouse_audition_button, _("Audition Mode (listen to regions)"));
3397 set_tooltip (mouse_content_button, _("Internal Edit Mode (edit notes and automation points)"));
3398 set_tooltip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
3399 set_tooltip (nudge_forward_button, _("Nudge Region/Selection Later"));
3400 set_tooltip (nudge_backward_button, _("Nudge Region/Selection Earlier"));
3401 set_tooltip (zoom_in_button, _("Zoom In"));
3402 set_tooltip (zoom_out_button, _("Zoom Out"));
3403 set_tooltip (zoom_preset_selector, _("Zoom to Time Scale"));
3404 set_tooltip (zoom_out_full_button, _("Zoom to Session"));
3405 set_tooltip (zoom_focus_selector, _("Zoom Focus"));
3406 set_tooltip (tav_expand_button, _("Expand Tracks"));
3407 set_tooltip (tav_shrink_button, _("Shrink Tracks"));
3408 set_tooltip (visible_tracks_selector, _("Number of visible tracks"));
3409 set_tooltip (grid_type_selector, _("Grid Mode"));
3410 set_tooltip (snap_mode_button, _("Snap Mode\n\nRight-click to visit Snap preferences."));
3411 set_tooltip (edit_point_selector, _("Edit Point"));
3412 set_tooltip (edit_mode_selector, _("Edit Mode"));
3413 set_tooltip (nudge_clock, _("Nudge Clock\n(controls distance used to nudge regions and selections)"));
3417 Editor::convert_drop_to_paths (
3418 vector<string>& paths,
3419 const RefPtr<Gdk::DragContext>& /*context*/,
3422 const SelectionData& data,
3426 if (_session == 0) {
3430 vector<string> uris = data.get_uris();
3434 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3435 are actually URI lists. So do it by hand.
3438 if (data.get_target() != "text/plain") {
3442 /* Parse the "uri-list" format that Nautilus provides,
3443 where each pathname is delimited by \r\n.
3445 THERE MAY BE NO NULL TERMINATING CHAR!!!
3448 string txt = data.get_text();
3452 p = (char *) malloc (txt.length() + 1);
3453 txt.copy (p, txt.length(), 0);
3454 p[txt.length()] = '\0';
3460 while (g_ascii_isspace (*p))
3464 while (*q && (*q != '\n') && (*q != '\r')) {
3471 while (q > p && g_ascii_isspace (*q))
3476 uris.push_back (string (p, q - p + 1));
3480 p = strchr (p, '\n');
3492 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3493 if ((*i).substr (0,7) == "file://") {
3494 paths.push_back (Glib::filename_from_uri (*i));
3502 Editor::new_tempo_section ()
3507 Editor::map_transport_state ()
3509 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state);
3511 if (_session && _session->transport_stopped()) {
3512 have_pending_keyboard_selection = false;
3515 update_loop_range_view ();
3519 Editor::transport_looped ()
3521 /* reset Playhead position interpolation.
3522 * see Editor::super_rapid_screen_update
3524 _last_update_time = 0;
3530 Editor::begin_selection_op_history ()
3532 selection_op_cmd_depth = 0;
3533 selection_op_history_it = 0;
3535 while(!selection_op_history.empty()) {
3536 delete selection_op_history.front();
3537 selection_op_history.pop_front();
3540 selection_undo_action->set_sensitive (false);
3541 selection_redo_action->set_sensitive (false);
3542 selection_op_history.push_front (&_selection_memento->get_state ());
3546 Editor::begin_reversible_selection_op (string name)
3549 //cerr << name << endl;
3550 /* begin/commit pairs can be nested */
3551 selection_op_cmd_depth++;
3556 Editor::commit_reversible_selection_op ()
3559 if (selection_op_cmd_depth == 1) {
3561 if (selection_op_history_it > 0 && selection_op_history_it < selection_op_history.size()) {
3562 /* The user has undone some selection ops and then made a new one,
3563 * making anything earlier in the list invalid.
3566 list<XMLNode *>::iterator it = selection_op_history.begin();
3567 list<XMLNode *>::iterator e_it = it;
3568 advance (e_it, selection_op_history_it);
3570 for (; it != e_it; ++it) {
3573 selection_op_history.erase (selection_op_history.begin(), e_it);
3576 selection_op_history.push_front (&_selection_memento->get_state ());
3577 selection_op_history_it = 0;
3579 selection_undo_action->set_sensitive (true);
3580 selection_redo_action->set_sensitive (false);
3583 if (selection_op_cmd_depth > 0) {
3584 selection_op_cmd_depth--;
3590 Editor::undo_selection_op ()
3593 selection_op_history_it++;
3595 for (std::list<XMLNode *>::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) {
3596 if (n == selection_op_history_it) {
3597 _selection_memento->set_state (*(*i), Stateful::current_state_version);
3598 selection_redo_action->set_sensitive (true);
3602 /* is there an earlier entry? */
3603 if ((selection_op_history_it + 1) >= selection_op_history.size()) {
3604 selection_undo_action->set_sensitive (false);
3610 Editor::redo_selection_op ()
3613 if (selection_op_history_it > 0) {
3614 selection_op_history_it--;
3617 for (std::list<XMLNode *>::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) {
3618 if (n == selection_op_history_it) {
3619 _selection_memento->set_state (*(*i), Stateful::current_state_version);
3620 selection_undo_action->set_sensitive (true);
3625 if (selection_op_history_it == 0) {
3626 selection_redo_action->set_sensitive (false);
3632 Editor::begin_reversible_command (string name)
3635 before.push_back (&_selection_memento->get_state ());
3636 _session->begin_reversible_command (name);
3641 Editor::begin_reversible_command (GQuark q)
3644 before.push_back (&_selection_memento->get_state ());
3645 _session->begin_reversible_command (q);
3650 Editor::abort_reversible_command ()
3653 while(!before.empty()) {
3654 delete before.front();
3657 _session->abort_reversible_command ();
3662 Editor::commit_reversible_command ()
3665 if (before.size() == 1) {
3666 _session->add_command (new MementoCommand<SelectionMemento>(*(_selection_memento), before.front(), &_selection_memento->get_state ()));
3667 redo_action->set_sensitive(false);
3668 undo_action->set_sensitive(true);
3669 begin_selection_op_history ();
3672 if (before.empty()) {
3673 cerr << "Please call begin_reversible_command() before commit_reversible_command()." << endl;
3678 _session->commit_reversible_command ();
3683 Editor::history_changed ()
3687 if (undo_action && _session) {
3688 if (_session->undo_depth() == 0) {
3689 label = S_("Command|Undo");
3691 label = string_compose(S_("Command|Undo (%1)"), _session->next_undo());
3693 undo_action->property_label() = label;
3696 if (redo_action && _session) {
3697 if (_session->redo_depth() == 0) {
3699 redo_action->set_sensitive (false);
3701 label = string_compose(_("Redo (%1)"), _session->next_redo());
3702 redo_action->set_sensitive (true);
3704 redo_action->property_label() = label;
3709 Editor::duplicate_range (bool with_dialog)
3713 RegionSelection rs = get_regions_from_selection_and_entered ();
3715 if (selection->time.length() == 0 && rs.empty()) {
3721 ArdourDialog win (_("Duplicate"));
3722 Label label (_("Number of duplications:"));
3723 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3724 SpinButton spinner (adjustment, 0.0, 1);
3727 win.get_vbox()->set_spacing (12);
3728 win.get_vbox()->pack_start (hbox);
3729 hbox.set_border_width (6);
3730 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3732 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3733 place, visually. so do this by hand.
3736 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3737 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3738 spinner.grab_focus();
3744 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3745 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3746 win.set_default_response (RESPONSE_ACCEPT);
3748 spinner.grab_focus ();
3750 switch (win.run ()) {
3751 case RESPONSE_ACCEPT:
3757 times = adjustment.get_value();
3760 if ((current_mouse_mode() == MouseRange)) {
3761 if (selection->time.length()) {
3762 duplicate_selection (times);
3764 } else if (get_smart_mode()) {
3765 if (selection->time.length()) {
3766 duplicate_selection (times);
3768 duplicate_some_regions (rs, times);
3770 duplicate_some_regions (rs, times);
3775 Editor::set_edit_mode (EditMode m)
3777 Config->set_edit_mode (m);
3781 Editor::cycle_edit_mode ()
3783 switch (Config->get_edit_mode()) {
3785 Config->set_edit_mode (Ripple);
3789 Config->set_edit_mode (Lock);
3792 Config->set_edit_mode (Slide);
3798 Editor::edit_mode_selection_done (EditMode m)
3800 Config->set_edit_mode (m);
3804 Editor::grid_type_selection_done (GridType gridtype)
3806 RefPtr<RadioAction> ract = grid_type_action (gridtype);
3808 ract->set_active ();
3813 Editor::snap_mode_selection_done (SnapMode mode)
3815 RefPtr<RadioAction> ract = snap_mode_action (mode);
3818 ract->set_active (true);
3823 Editor::cycle_edit_point (bool with_marker)
3825 if(Profile->get_mixbus())
3826 with_marker = false;
3828 switch (_edit_point) {
3830 set_edit_point_preference (EditAtPlayhead);
3832 case EditAtPlayhead:
3834 set_edit_point_preference (EditAtSelectedMarker);
3836 set_edit_point_preference (EditAtMouse);
3839 case EditAtSelectedMarker:
3840 set_edit_point_preference (EditAtMouse);
3846 Editor::edit_point_selection_done (EditPoint ep)
3848 set_edit_point_preference (ep);
3852 Editor::build_zoom_focus_menu ()
3854 using namespace Menu_Helpers;
3856 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusLeft], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusLeft)));
3857 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusRight], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusRight)));
3858 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusCenter], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusCenter)));
3859 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusPlayhead], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusPlayhead)));
3860 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusMouse], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusMouse)));
3861 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusEdit], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusEdit)));
3863 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_TRIANGLE_WIDTH, 2);
3867 Editor::zoom_focus_selection_done (ZoomFocus f)
3869 RefPtr<RadioAction> ract = zoom_focus_action (f);
3871 ract->set_active ();
3876 Editor::build_track_count_menu ()
3878 using namespace Menu_Helpers;
3880 if (!Profile->get_mixbus()) {
3881 visible_tracks_selector.AddMenuElem (MenuElem (X_("1"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 1)));
3882 visible_tracks_selector.AddMenuElem (MenuElem (X_("2"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 2)));
3883 visible_tracks_selector.AddMenuElem (MenuElem (X_("3"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 3)));
3884 visible_tracks_selector.AddMenuElem (MenuElem (X_("4"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 4)));
3885 visible_tracks_selector.AddMenuElem (MenuElem (X_("8"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 8)));
3886 visible_tracks_selector.AddMenuElem (MenuElem (X_("12"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 12)));
3887 visible_tracks_selector.AddMenuElem (MenuElem (X_("16"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 16)));
3888 visible_tracks_selector.AddMenuElem (MenuElem (X_("20"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 20)));
3889 visible_tracks_selector.AddMenuElem (MenuElem (X_("24"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 24)));
3890 visible_tracks_selector.AddMenuElem (MenuElem (X_("32"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 32)));
3891 visible_tracks_selector.AddMenuElem (MenuElem (X_("64"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 64)));
3892 visible_tracks_selector.AddMenuElem (MenuElem (_("Selection"), sigc::mem_fun(*this, &Editor::fit_selection)));
3893 visible_tracks_selector.AddMenuElem (MenuElem (_("All"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 0)));
3895 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 1 track"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 1)));
3896 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 2 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 2)));
3897 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 4 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 4)));
3898 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 8 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 8)));
3899 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 16 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 16)));
3900 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 24 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 24)));
3901 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 32 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 32)));
3902 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 48 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 48)));
3903 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit All tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 0)));
3904 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit Selection"), sigc::mem_fun(*this, &Editor::fit_selection)));
3906 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 10 ms"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 10)));
3907 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 100 ms"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 100)));
3908 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 1 sec"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 1 * 1000)));
3909 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 10 sec"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 10 * 1000)));
3910 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 1 min"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 60 * 1000)));
3911 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 10 min"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 10 * 60 * 1000)));
3912 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 1 hour"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 60 * 60 * 1000)));
3913 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 8 hours"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 8 * 60 * 60 * 1000)));
3914 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 24 hours"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 24 * 60 * 60 * 1000)));
3915 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Session"), sigc::mem_fun(*this, &Editor::temporal_zoom_session)));
3916 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Extents"), sigc::mem_fun(*this, &Editor::temporal_zoom_extents)));
3917 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Range/Region Selection"), sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_selection), Horizontal)));
3922 Editor::set_zoom_preset (int64_t ms)
3925 temporal_zoom_session();
3929 ARDOUR::samplecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
3930 temporal_zoom ((sample_rate * ms / 1000) / _visible_canvas_width);
3934 Editor::set_visible_track_count (int32_t n)
3936 _visible_track_count = n;
3938 /* if the canvas hasn't really been allocated any size yet, just
3939 record the desired number of visible tracks and return. when canvas
3940 allocation happens, we will get called again and then we can do the
3944 if (_visible_canvas_height <= 1) {
3950 DisplaySuspender ds;
3952 if (_visible_track_count > 0) {
3953 h = trackviews_height() / _visible_track_count;
3954 std::ostringstream s;
3955 s << _visible_track_count;
3957 } else if (_visible_track_count == 0) {
3959 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
3960 if ((*i)->marked_for_display()) {
3962 TimeAxisView::Children cl ((*i)->get_child_list ());
3963 for (TimeAxisView::Children::const_iterator j = cl.begin(); j != cl.end(); ++j) {
3964 if ((*j)->marked_for_display()) {
3971 visible_tracks_selector.set_text (X_("*"));
3974 h = trackviews_height() / n;
3977 /* negative value means that the visible track count has
3978 been overridden by explicit track height changes.
3980 visible_tracks_selector.set_text (X_("*"));
3984 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3985 (*i)->set_height (h, TimeAxisView::HeightPerLane);
3988 if (str != visible_tracks_selector.get_text()) {
3989 visible_tracks_selector.set_text (str);
3994 Editor::override_visible_track_count ()
3996 _visible_track_count = -1;
3997 visible_tracks_selector.set_text (_("*"));
4001 Editor::edit_controls_button_release (GdkEventButton* ev)
4003 if (Keyboard::is_context_menu_event (ev)) {
4004 ARDOUR_UI::instance()->add_route ();
4005 } else if (ev->button == 1) {
4006 selection->clear_tracks ();
4013 Editor::mouse_select_button_release (GdkEventButton* ev)
4015 /* this handles just right-clicks */
4017 if (ev->button != 3) {
4025 Editor::set_zoom_focus (ZoomFocus f)
4027 string str = zoom_focus_strings[(int)f];
4029 if (str != zoom_focus_selector.get_text()) {
4030 zoom_focus_selector.set_text (str);
4033 if (zoom_focus != f) {
4040 Editor::cycle_zoom_focus ()
4042 switch (zoom_focus) {
4044 set_zoom_focus (ZoomFocusRight);
4046 case ZoomFocusRight:
4047 set_zoom_focus (ZoomFocusCenter);
4049 case ZoomFocusCenter:
4050 set_zoom_focus (ZoomFocusPlayhead);
4052 case ZoomFocusPlayhead:
4053 set_zoom_focus (ZoomFocusMouse);
4055 case ZoomFocusMouse:
4056 set_zoom_focus (ZoomFocusEdit);
4059 set_zoom_focus (ZoomFocusLeft);
4065 Editor::update_grid ()
4067 if (grid_musical()) {
4068 std::vector<TempoMap::BBTPoint> grid;
4069 if (bbt_ruler_scale != bbt_show_many) {
4070 compute_current_bbt_points (grid, _leftmost_sample, _leftmost_sample + current_page_samples());
4072 maybe_draw_grid_lines ();
4073 } else if (grid_nonmusical()) {
4074 maybe_draw_grid_lines ();
4081 Editor::toggle_follow_playhead ()
4083 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
4085 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
4086 set_follow_playhead (tact->get_active());
4090 /** @param yn true to follow playhead, otherwise false.
4091 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
4094 Editor::set_follow_playhead (bool yn, bool catch_up)
4096 if (_follow_playhead != yn) {
4097 if ((_follow_playhead = yn) == true && catch_up) {
4099 reset_x_origin_to_follow_playhead ();
4106 Editor::toggle_stationary_playhead ()
4108 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
4110 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
4111 set_stationary_playhead (tact->get_active());
4116 Editor::set_stationary_playhead (bool yn)
4118 if (_stationary_playhead != yn) {
4119 if ((_stationary_playhead = yn) == true) {
4120 /* catch up -- FIXME need a 3.0 equivalent of this 2.X call */
4121 // update_current_screen ();
4128 Editor::playlist_selector () const
4130 return *_playlist_selector;
4134 Editor::get_paste_offset (samplepos_t pos, unsigned paste_count, samplecnt_t duration)
4136 if (paste_count == 0) {
4137 /* don't bother calculating an offset that will be zero anyway */
4141 /* calculate basic unsnapped multi-paste offset */
4142 samplecnt_t offset = paste_count * duration;
4144 /* snap offset so pos + offset is aligned to the grid */
4145 MusicSample offset_pos (pos + offset, 0);
4146 snap_to(offset_pos, RoundUpMaybe);
4147 offset = offset_pos.sample - pos;
4153 Editor::get_grid_beat_divisions(samplepos_t position)
4155 switch (_grid_type) {
4156 case GridTypeBeatDiv32: return 32;
4157 case GridTypeBeatDiv28: return 28;
4158 case GridTypeBeatDiv24: return 24;
4159 case GridTypeBeatDiv20: return 20;
4160 case GridTypeBeatDiv16: return 16;
4161 case GridTypeBeatDiv14: return 14;
4162 case GridTypeBeatDiv12: return 12;
4163 case GridTypeBeatDiv10: return 10;
4164 case GridTypeBeatDiv8: return 8;
4165 case GridTypeBeatDiv7: return 7;
4166 case GridTypeBeatDiv6: return 6;
4167 case GridTypeBeatDiv5: return 5;
4168 case GridTypeBeatDiv4: return 4;
4169 case GridTypeBeatDiv3: return 3;
4170 case GridTypeBeatDiv2: return 2;
4171 case GridTypeBeat: return 1;
4172 case GridTypeBar: return 1;
4174 case GridTypeNone: return 0;
4175 case GridTypeTimecode: return 0;
4176 case GridTypeMinSec: return 0;
4177 case GridTypeCDFrame: return 0;
4183 /** returns the current musical grid divisiions using the supplied modifier mask from a GtkEvent.
4184 if the grid is non-musical, returns 0.
4185 if the grid is snapped to bars, returns -1.
4186 @param event_state the current keyboard modifier mask.
4189 Editor::get_grid_music_divisions (uint32_t event_state)
4191 if (snap_mode() == SnapOff && !ArdourKeyboard::indicates_snap (event_state)) {
4195 if (snap_mode() != SnapOff && ArdourKeyboard::indicates_snap (event_state)) {
4199 switch (_grid_type) {
4200 case GridTypeBeatDiv32: return 32;
4201 case GridTypeBeatDiv28: return 28;
4202 case GridTypeBeatDiv24: return 24;
4203 case GridTypeBeatDiv20: return 20;
4204 case GridTypeBeatDiv16: return 16;
4205 case GridTypeBeatDiv14: return 14;
4206 case GridTypeBeatDiv12: return 12;
4207 case GridTypeBeatDiv10: return 10;
4208 case GridTypeBeatDiv8: return 8;
4209 case GridTypeBeatDiv7: return 7;
4210 case GridTypeBeatDiv6: return 6;
4211 case GridTypeBeatDiv5: return 5;
4212 case GridTypeBeatDiv4: return 4;
4213 case GridTypeBeatDiv3: return 3;
4214 case GridTypeBeatDiv2: return 2;
4215 case GridTypeBeat: return 1;
4216 case GridTypeBar : return -1;
4218 case GridTypeNone: return 0;
4219 case GridTypeTimecode: return 0;
4220 case GridTypeMinSec: return 0;
4221 case GridTypeCDFrame: return 0;
4227 Editor::get_grid_type_as_beats (bool& success, samplepos_t position)
4231 const unsigned divisions = get_grid_beat_divisions(position);
4233 return Temporal::Beats(1.0 / (double)get_grid_beat_divisions(position));
4236 switch (_grid_type) {
4238 return Temporal::Beats(4.0 / _session->tempo_map().meter_at_sample (position).note_divisor());
4241 const Meter& m = _session->tempo_map().meter_at_sample (position);
4242 return Temporal::Beats((4.0 * m.divisions_per_bar()) / m.note_divisor());
4250 return Temporal::Beats();
4254 Editor::get_nudge_distance (samplepos_t pos, samplecnt_t& next)
4258 ret = nudge_clock->current_duration (pos);
4259 next = ret + 1; /* XXXX fix me */
4265 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
4267 ArdourDialog dialog (_("Playlist Deletion"));
4268 Label label (string_compose (_("Playlist %1 is currently unused.\n"
4269 "If it is kept, its audio files will not be cleaned.\n"
4270 "If it is deleted, audio files used by it alone will be cleaned."),
4273 dialog.set_position (WIN_POS_CENTER);
4274 dialog.get_vbox()->pack_start (label);
4278 dialog.add_button (_("Delete All Unused"), RESPONSE_YES); // needs clarification. this and all remaining ones
4279 dialog.add_button (_("Delete Playlist"), RESPONSE_ACCEPT);
4280 Button* keep = dialog.add_button (_("Keep Playlist"), RESPONSE_REJECT);
4281 dialog.add_button (_("Keep Remaining"), RESPONSE_NO); // ditto
4282 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
4284 /* by default gtk uses the left most button */
4285 keep->grab_focus ();
4287 switch (dialog.run ()) {
4289 /* keep this and all remaining ones */
4294 /* delete this and all others */
4298 case RESPONSE_ACCEPT:
4299 /* delete the playlist */
4303 case RESPONSE_REJECT:
4304 /* keep the playlist */
4316 Editor::audio_region_selection_covers (samplepos_t where)
4318 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
4319 if ((*a)->region()->covers (where)) {
4328 Editor::prepare_for_cleanup ()
4330 cut_buffer->clear_regions ();
4331 cut_buffer->clear_playlists ();
4333 selection->clear_regions ();
4334 selection->clear_playlists ();
4336 _regions->suspend_redisplay ();
4340 Editor::finish_cleanup ()
4342 _regions->resume_redisplay ();
4346 Editor::transport_loop_location()
4349 return _session->locations()->auto_loop_location();
4356 Editor::transport_punch_location()
4359 return _session->locations()->auto_punch_location();
4366 Editor::control_layout_scroll (GdkEventScroll* ev)
4368 /* Just forward to the normal canvas scroll method. The coordinate
4369 systems are different but since the canvas is always larger than the
4370 track headers, and aligned with the trackview area, this will work.
4372 In the not too distant future this layout is going away anyway and
4373 headers will be on the canvas.
4375 return canvas_scroll_event (ev, false);
4379 Editor::session_state_saved (string)
4382 _snapshots->redisplay ();
4386 Editor::maximise_editing_space ()
4392 Gtk::Window* toplevel = current_toplevel();
4395 toplevel->fullscreen ();
4401 Editor::restore_editing_space ()
4407 Gtk::Window* toplevel = current_toplevel();
4410 toplevel->unfullscreen();
4416 * Make new playlists for a given track and also any others that belong
4417 * to the same active route group with the `select' property.
4422 Editor::new_playlists (TimeAxisView* v)
4424 begin_reversible_command (_("new playlists"));
4425 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4426 _session->playlists->get (playlists);
4427 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::group_select.property_id);
4428 commit_reversible_command ();
4432 * Use a copy of the current playlist for a given track and also any others that belong
4433 * to the same active route group with the `select' property.
4438 Editor::copy_playlists (TimeAxisView* v)
4440 begin_reversible_command (_("copy playlists"));
4441 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4442 _session->playlists->get (playlists);
4443 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::group_select.property_id);
4444 commit_reversible_command ();
4447 /** Clear the current playlist for a given track and also any others that belong
4448 * to the same active route group with the `select' property.
4453 Editor::clear_playlists (TimeAxisView* v)
4455 begin_reversible_command (_("clear playlists"));
4456 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4457 _session->playlists->get (playlists);
4458 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::group_select.property_id);
4459 commit_reversible_command ();
4463 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4465 atv.use_new_playlist (sz > 1 ? false : true, playlists, false);
4469 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4471 atv.use_new_playlist (sz > 1 ? false : true, playlists, true);
4475 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4477 atv.clear_playlist ();
4481 Editor::get_y_origin () const
4483 return vertical_adjustment.get_value ();
4486 /** Queue up a change to the viewport x origin.
4487 * @param sample New x origin.
4490 Editor::reset_x_origin (samplepos_t sample)
4492 pending_visual_change.add (VisualChange::TimeOrigin);
4493 pending_visual_change.time_origin = sample;
4494 ensure_visual_change_idle_handler ();
4498 Editor::reset_y_origin (double y)
4500 pending_visual_change.add (VisualChange::YOrigin);
4501 pending_visual_change.y_origin = y;
4502 ensure_visual_change_idle_handler ();
4506 Editor::reset_zoom (samplecnt_t spp)
4508 if (spp == samples_per_pixel) {
4512 pending_visual_change.add (VisualChange::ZoomLevel);
4513 pending_visual_change.samples_per_pixel = spp;
4514 ensure_visual_change_idle_handler ();
4518 Editor::reposition_and_zoom (samplepos_t sample, double fpu)
4520 reset_x_origin (sample);
4523 if (!no_save_visual) {
4524 undo_visual_stack.push_back (current_visual_state(false));
4528 Editor::VisualState::VisualState (bool with_tracks)
4529 : gui_state (with_tracks ? new GUIObjectState : 0)
4533 Editor::VisualState::~VisualState ()
4538 Editor::VisualState*
4539 Editor::current_visual_state (bool with_tracks)
4541 VisualState* vs = new VisualState (with_tracks);
4542 vs->y_position = vertical_adjustment.get_value();
4543 vs->samples_per_pixel = samples_per_pixel;
4544 vs->_leftmost_sample = _leftmost_sample;
4545 vs->zoom_focus = zoom_focus;
4548 vs->gui_state->set_state (ARDOUR_UI::instance()->gui_object_state->get_state());
4555 Editor::undo_visual_state ()
4557 if (undo_visual_stack.empty()) {
4561 VisualState* vs = undo_visual_stack.back();
4562 undo_visual_stack.pop_back();
4565 redo_visual_stack.push_back (current_visual_state (vs ? vs->gui_state != 0 : false));
4568 use_visual_state (*vs);
4573 Editor::redo_visual_state ()
4575 if (redo_visual_stack.empty()) {
4579 VisualState* vs = redo_visual_stack.back();
4580 redo_visual_stack.pop_back();
4582 /* XXX: can 'vs' really be 0? Is there a place that puts NULL pointers onto the stack? */
4583 undo_visual_stack.push_back (current_visual_state (vs ? (vs->gui_state != 0) : false));
4586 use_visual_state (*vs);
4591 Editor::swap_visual_state ()
4593 if (undo_visual_stack.empty()) {
4594 redo_visual_state ();
4596 undo_visual_state ();
4601 Editor::use_visual_state (VisualState& vs)
4603 PBD::Unwinder<bool> nsv (no_save_visual, true);
4604 DisplaySuspender ds;
4606 vertical_adjustment.set_value (vs.y_position);
4608 set_zoom_focus (vs.zoom_focus);
4609 reposition_and_zoom (vs._leftmost_sample, vs.samples_per_pixel);
4612 ARDOUR_UI::instance()->gui_object_state->set_state (vs.gui_state->get_state());
4614 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4615 (*i)->clear_property_cache();
4616 (*i)->reset_visual_state ();
4620 _routes->update_visibility ();
4623 /** This is the core function that controls the zoom level of the canvas. It is called
4624 * whenever one or more calls are made to reset_zoom(). It executes in an idle handler.
4625 * @param spp new number of samples per pixel
4628 Editor::set_samples_per_pixel (samplecnt_t spp)
4634 const samplecnt_t three_days = 3 * 24 * 60 * 60 * (_session ? _session->sample_rate() : 48000);
4635 const samplecnt_t lots_of_pixels = 4000;
4637 /* if the zoom level is greater than what you'd get trying to display 3
4638 * days of audio on a really big screen, then it's too big.
4641 if (spp * lots_of_pixels > three_days) {
4645 samples_per_pixel = spp;
4649 Editor::on_samples_per_pixel_changed ()
4651 bool const showing_time_selection = selection->time.length() > 0;
4653 if (showing_time_selection && selection->time.start () != selection->time.end_sample ()) {
4654 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4655 (*i)->reshow_selection (selection->time);
4659 ZoomChanged (); /* EMIT_SIGNAL */
4661 ArdourCanvas::GtkCanvasViewport* c;
4663 c = get_track_canvas();
4665 c->canvas()->zoomed ();
4668 if (playhead_cursor) {
4669 playhead_cursor->set_position (playhead_cursor->current_sample ());
4672 refresh_location_display();
4673 _summary->set_overlays_dirty ();
4675 update_marker_labels ();
4681 Editor::playhead_cursor_sample () const
4683 return playhead_cursor->current_sample();
4687 Editor::queue_visual_videotimeline_update ()
4689 pending_visual_change.add (VisualChange::VideoTimeline);
4690 ensure_visual_change_idle_handler ();
4694 Editor::ensure_visual_change_idle_handler ()
4696 if (pending_visual_change.idle_handler_id < 0) {
4697 /* see comment in add_to_idle_resize above. */
4698 pending_visual_change.idle_handler_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, _idle_visual_changer, this, NULL);
4699 pending_visual_change.being_handled = false;
4704 Editor::_idle_visual_changer (void* arg)
4706 return static_cast<Editor*>(arg)->idle_visual_changer ();
4710 Editor::pre_render ()
4712 visual_change_queued = false;
4714 if (pending_visual_change.pending != 0) {
4715 ensure_visual_change_idle_handler();
4720 Editor::idle_visual_changer ()
4722 pending_visual_change.idle_handler_id = -1;
4724 if (pending_visual_change.pending == 0) {
4728 /* set_horizontal_position() below (and maybe other calls) call
4729 gtk_main_iteration(), so it's possible that a signal will be handled
4730 half-way through this method. If this signal wants an
4731 idle_visual_changer we must schedule another one after this one, so
4732 mark the idle_handler_id as -1 here to allow that. Also make a note
4733 that we are doing the visual change, so that changes in response to
4734 super-rapid-screen-update can be dropped if we are still processing
4738 if (visual_change_queued) {
4742 pending_visual_change.being_handled = true;
4744 VisualChange vc = pending_visual_change;
4746 pending_visual_change.pending = (VisualChange::Type) 0;
4748 visual_changer (vc);
4750 pending_visual_change.being_handled = false;
4752 visual_change_queued = true;
4754 return 0; /* this is always a one-shot call */
4758 Editor::visual_changer (const VisualChange& vc)
4761 * Changed first so the correct horizontal canvas position is calculated in
4762 * Editor::set_horizontal_position
4764 if (vc.pending & VisualChange::ZoomLevel) {
4765 set_samples_per_pixel (vc.samples_per_pixel);
4768 if (vc.pending & VisualChange::TimeOrigin) {
4769 double new_time_origin = sample_to_pixel_unrounded (vc.time_origin);
4770 set_horizontal_position (new_time_origin);
4773 if (vc.pending & VisualChange::YOrigin) {
4774 vertical_adjustment.set_value (vc.y_origin);
4778 * Now the canvas is in the final state before render the canvas items that
4779 * support the Item::prepare_for_render interface can calculate the correct
4780 * item to visible canvas intersection.
4782 if (vc.pending & VisualChange::ZoomLevel) {
4783 on_samples_per_pixel_changed ();
4785 compute_fixed_ruler_scale ();
4787 compute_bbt_ruler_scale (vc.time_origin, pending_visual_change.time_origin + current_page_samples());
4788 update_tempo_based_rulers ();
4791 if (!(vc.pending & VisualChange::ZoomLevel)) {
4792 /* If the canvas is not being zoomed then the canvas items will not change
4793 * and cause Item::prepare_for_render to be called so do it here manually.
4794 * Not ideal, but I can't think of a better solution atm.
4796 _track_canvas->prepare_for_render();
4799 /* If we are only scrolling vertically there is no need to update these */
4800 if (vc.pending != VisualChange::YOrigin) {
4801 update_fixed_rulers ();
4802 redisplay_grid (true);
4804 /* video frames & position need to be updated for zoom, horiz-scroll
4805 * and (explicitly) VisualChange::VideoTimeline.
4807 update_video_timeline();
4810 _summary->set_overlays_dirty ();
4813 struct EditorOrderTimeAxisSorter {
4814 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4815 return a->order () < b->order ();
4820 Editor::sort_track_selection (TrackViewList& sel)
4822 EditorOrderTimeAxisSorter cmp;
4827 Editor::get_preferred_edit_position (EditIgnoreOption ignore, bool from_context_menu, bool from_outside_canvas)
4830 samplepos_t where = 0;
4831 EditPoint ep = _edit_point;
4833 if (Profile->get_mixbus()) {
4834 if (ep == EditAtSelectedMarker) {
4835 ep = EditAtPlayhead;
4839 if (from_outside_canvas && (ep == EditAtMouse)) {
4840 ep = EditAtPlayhead;
4841 } else if (from_context_menu && (ep == EditAtMouse)) {
4842 return canvas_event_sample (&context_click_event, 0, 0);
4845 if (entered_marker) {
4846 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4847 return entered_marker->position();
4850 if ((ignore == EDIT_IGNORE_PHEAD) && ep == EditAtPlayhead) {
4851 ep = EditAtSelectedMarker;
4854 if ((ignore == EDIT_IGNORE_MOUSE) && ep == EditAtMouse) {
4855 ep = EditAtPlayhead;
4858 MusicSample snap_mf (0, 0);
4861 case EditAtPlayhead:
4862 if (_dragging_playhead) {
4863 /* NOTE: since the user is dragging with the mouse, this operation will implicitly be Snapped */
4864 where = playhead_cursor->current_sample();
4866 where = _session->audible_sample();
4868 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4871 case EditAtSelectedMarker:
4872 if (!selection->markers.empty()) {
4874 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4877 where = loc->start();
4881 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4889 if (!mouse_sample (where, ignored)) {
4890 /* XXX not right but what can we do ? */
4893 snap_mf.sample = where;
4895 where = snap_mf.sample;
4896 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4904 Editor::set_loop_range (samplepos_t start, samplepos_t end, string cmd)
4906 if (!_session) return;
4908 begin_reversible_command (cmd);
4912 if ((tll = transport_loop_location()) == 0) {
4913 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop, get_grid_music_divisions(0));
4914 XMLNode &before = _session->locations()->get_state();
4915 _session->locations()->add (loc, true);
4916 _session->set_auto_loop_location (loc);
4917 XMLNode &after = _session->locations()->get_state();
4918 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4920 XMLNode &before = tll->get_state();
4921 tll->set_hidden (false, this);
4922 tll->set (start, end);
4923 XMLNode &after = tll->get_state();
4924 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4927 commit_reversible_command ();
4931 Editor::set_punch_range (samplepos_t start, samplepos_t end, string cmd)
4933 if (!_session) return;
4935 begin_reversible_command (cmd);
4939 if ((tpl = transport_punch_location()) == 0) {
4940 Location* loc = new Location (*_session, start, end, _("Punch"), Location::IsAutoPunch, get_grid_music_divisions(0));
4941 XMLNode &before = _session->locations()->get_state();
4942 _session->locations()->add (loc, true);
4943 _session->set_auto_punch_location (loc);
4944 XMLNode &after = _session->locations()->get_state();
4945 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4947 XMLNode &before = tpl->get_state();
4948 tpl->set_hidden (false, this);
4949 tpl->set (start, end);
4950 XMLNode &after = tpl->get_state();
4951 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4954 commit_reversible_command ();
4957 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4958 * @param rs List to which found regions are added.
4959 * @param where Time to look at.
4960 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4963 Editor::get_regions_at (RegionSelection& rs, samplepos_t where, const TrackViewList& ts) const
4965 const TrackViewList* tracks;
4968 tracks = &track_views;
4973 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4975 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4978 boost::shared_ptr<Track> tr;
4979 boost::shared_ptr<Playlist> pl;
4981 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4983 boost::shared_ptr<RegionList> regions = pl->regions_at (where);
4985 for (RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4986 RegionView* rv = rtv->view()->find_view (*i);
4997 Editor::get_regions_after (RegionSelection& rs, samplepos_t where, const TrackViewList& ts) const
4999 const TrackViewList* tracks;
5002 tracks = &track_views;
5007 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
5008 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
5010 boost::shared_ptr<Track> tr;
5011 boost::shared_ptr<Playlist> pl;
5013 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
5015 boost::shared_ptr<RegionList> regions = pl->regions_touched (where, max_samplepos);
5017 for (RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
5019 RegionView* rv = rtv->view()->find_view (*i);
5030 /** Get regions using the following method:
5032 * Make a region list using:
5033 * (a) any selected regions
5034 * (b) the intersection of any selected tracks and the edit point(*)
5035 * (c) if neither exists, and edit_point == mouse, then whatever region is under the mouse
5037 * (*) NOTE: in this case, if 'No Selection = All Tracks' is active, search all tracks
5039 * Note that we have forced the rule that selected regions and selected tracks are mutually exclusive
5043 Editor::get_regions_from_selection_and_edit_point (EditIgnoreOption ignore, bool from_context_menu, bool from_outside_canvas)
5045 RegionSelection regions;
5047 if (_edit_point == EditAtMouse && entered_regionview && selection->tracks.empty() && selection->regions.empty()) {
5048 regions.add (entered_regionview);
5050 regions = selection->regions;
5053 if (regions.empty()) {
5054 TrackViewList tracks = selection->tracks;
5056 if (!tracks.empty()) {
5057 /* no region selected or entered, but some selected tracks:
5058 * act on all regions on the selected tracks at the edit point
5060 samplepos_t const where = get_preferred_edit_position (ignore, from_context_menu, from_outside_canvas);
5061 get_regions_at(regions, where, tracks);
5068 /** Get regions using the following method:
5070 * Make a region list using:
5071 * (a) any selected regions
5072 * (b) the intersection of any selected tracks and the edit point(*)
5073 * (c) if neither exists, then whatever region is under the mouse
5075 * (*) NOTE: in this case, if 'No Selection = All Tracks' is active, search all tracks
5077 * Note that we have forced the rule that selected regions and selected tracks are mutually exclusive
5080 Editor::get_regions_from_selection_and_mouse (samplepos_t pos)
5082 RegionSelection regions;
5084 if (entered_regionview && selection->tracks.empty() && selection->regions.empty()) {
5085 regions.add (entered_regionview);
5087 regions = selection->regions;
5090 if (regions.empty()) {
5091 TrackViewList tracks = selection->tracks;
5093 if (!tracks.empty()) {
5094 /* no region selected or entered, but some selected tracks:
5095 * act on all regions on the selected tracks at the edit point
5097 get_regions_at(regions, pos, tracks);
5104 /** Start with regions that are selected, or the entered regionview if none are selected.
5105 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
5106 * of the regions that we started with.
5110 Editor::get_regions_from_selection_and_entered () const
5112 RegionSelection regions = selection->regions;
5114 if (regions.empty() && entered_regionview) {
5115 regions.add (entered_regionview);
5122 Editor::get_regionviews_by_id (PBD::ID const id, RegionSelection & regions) const
5124 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5125 RouteTimeAxisView* rtav;
5127 if ((rtav = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5128 boost::shared_ptr<Playlist> pl;
5129 std::vector<boost::shared_ptr<Region> > results;
5130 boost::shared_ptr<Track> tr;
5132 if ((tr = rtav->track()) == 0) {
5137 if ((pl = (tr->playlist())) != 0) {
5138 boost::shared_ptr<Region> r = pl->region_by_id (id);
5140 RegionView* rv = rtav->view()->find_view (r);
5142 regions.push_back (rv);
5151 Editor::get_per_region_note_selection (list<pair<PBD::ID, set<boost::shared_ptr<Evoral::Note<Temporal::Beats> > > > > &selection) const
5154 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5155 MidiTimeAxisView* mtav;
5157 if ((mtav = dynamic_cast<MidiTimeAxisView*> (*i)) != 0) {
5159 mtav->get_per_region_note_selection (selection);
5166 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions, bool src_comparison)
5168 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5170 RouteTimeAxisView* tatv;
5172 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5174 boost::shared_ptr<Playlist> pl;
5175 vector<boost::shared_ptr<Region> > results;
5177 boost::shared_ptr<Track> tr;
5179 if ((tr = tatv->track()) == 0) {
5184 if ((pl = (tr->playlist())) != 0) {
5185 if (src_comparison) {
5186 pl->get_source_equivalent_regions (region, results);
5188 pl->get_region_list_equivalent_regions (region, results);
5192 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
5193 if ((marv = tatv->view()->find_view (*ir)) != 0) {
5194 regions.push_back (marv);
5203 Editor::regionview_from_region (boost::shared_ptr<Region> region) const
5205 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5206 RouteTimeAxisView* tatv;
5207 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5208 if (!tatv->track()) {
5211 RegionView* marv = tatv->view()->find_view (region);
5221 Editor::rtav_from_route (boost::shared_ptr<Route> route) const
5223 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5224 RouteTimeAxisView* rtav;
5225 if ((rtav = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5226 if (rtav->route() == route) {
5235 Editor::show_rhythm_ferret ()
5237 if (rhythm_ferret == 0) {
5238 rhythm_ferret = new RhythmFerret(*this);
5241 rhythm_ferret->set_session (_session);
5242 rhythm_ferret->show ();
5243 rhythm_ferret->present ();
5247 Editor::first_idle ()
5249 MessageDialog* dialog = 0;
5251 if (track_views.size() > 1) {
5252 Timers::TimerSuspender t;
5253 dialog = new MessageDialog (
5254 string_compose (_("Please wait while %1 loads visual data."), PROGRAM_NAME),
5258 ARDOUR_UI::instance()->flush_pending (60);
5261 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
5265 /* now that all regionviews should exist, setup region selection */
5269 for (list<PBD::ID>::iterator pr = selection->regions.pending.begin (); pr != selection->regions.pending.end (); ++pr) {
5270 /* this is cumulative: rs is NOT cleared each time */
5271 get_regionviews_by_id (*pr, rs);
5274 selection->set (rs);
5276 /* first idle adds route children (automation tracks), so we need to redisplay here */
5277 _routes->redisplay ();
5281 if (_session->undo_depth() == 0) {
5282 undo_action->set_sensitive(false);
5284 redo_action->set_sensitive(false);
5285 begin_selection_op_history ();
5291 Editor::_idle_resize (gpointer arg)
5293 return ((Editor*)arg)->idle_resize ();
5297 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
5299 if (resize_idle_id < 0) {
5300 /* https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#G-PRIORITY-HIGH-IDLE:CAPS
5301 * GTK+ uses G_PRIORITY_HIGH_IDLE + 10 for resizing operations, and G_PRIORITY_HIGH_IDLE + 20 for redrawing operations.
5302 * (This is done to ensure that any pending resizes are processed before any pending redraws, so that widgets are not redrawn twice unnecessarily.)
5304 resize_idle_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, _idle_resize, this, NULL);
5305 _pending_resize_amount = 0;
5308 /* make a note of the smallest resulting height, so that we can clamp the
5309 lower limit at TimeAxisView::hSmall */
5311 int32_t min_resulting = INT32_MAX;
5313 _pending_resize_amount += h;
5314 _pending_resize_view = view;
5316 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
5318 if (selection->tracks.contains (_pending_resize_view)) {
5319 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5320 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
5324 if (min_resulting < 0) {
5329 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
5330 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
5334 /** Handle pending resizing of tracks */
5336 Editor::idle_resize ()
5338 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
5340 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
5341 selection->tracks.contains (_pending_resize_view)) {
5343 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5344 if (*i != _pending_resize_view) {
5345 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
5350 _pending_resize_amount = 0;
5351 _group_tabs->set_dirty ();
5352 resize_idle_id = -1;
5360 ENSURE_GUI_THREAD (*this, &Editor::located);
5363 playhead_cursor->set_position (_session->audible_sample ());
5364 if (_follow_playhead && !_pending_initial_locate) {
5365 reset_x_origin_to_follow_playhead ();
5369 _pending_locate_request = false;
5370 _pending_initial_locate = false;
5371 _last_update_time = 0;
5375 Editor::region_view_added (RegionView * rv)
5377 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (rv);
5379 list<pair<PBD::ID const, list<Evoral::event_id_t> > >::iterator rnote;
5380 for (rnote = selection->pending_midi_note_selection.begin(); rnote != selection->pending_midi_note_selection.end(); ++rnote) {
5381 if (rv->region()->id () == (*rnote).first) {
5382 mrv->select_notes ((*rnote).second);
5383 selection->pending_midi_note_selection.erase(rnote);
5389 _summary->set_background_dirty ();
5391 mark_region_boundary_cache_dirty ();
5395 Editor::region_view_removed ()
5397 _summary->set_background_dirty ();
5399 mark_region_boundary_cache_dirty ();
5403 Editor::axis_view_by_stripable (boost::shared_ptr<Stripable> s) const
5405 for (TrackViewList::const_iterator j = track_views.begin (); j != track_views.end(); ++j) {
5406 if ((*j)->stripable() == s) {
5415 Editor::axis_view_by_control (boost::shared_ptr<AutomationControl> c) const
5417 for (TrackViewList::const_iterator j = track_views.begin (); j != track_views.end(); ++j) {
5418 if ((*j)->control() == c) {
5422 TimeAxisView::Children kids = (*j)->get_child_list ();
5424 for (TimeAxisView::Children::iterator k = kids.begin(); k != kids.end(); ++k) {
5425 if ((*k)->control() == c) {
5435 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
5439 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
5440 TimeAxisView* tv = time_axis_view_from_stripable (*i);
5450 Editor::suspend_route_redisplay ()
5453 _routes->suspend_redisplay();
5458 Editor::resume_route_redisplay ()
5461 _routes->redisplay(); // queue redisplay
5462 _routes->resume_redisplay();
5467 Editor::add_vcas (VCAList& vlist)
5471 for (VCAList::iterator v = vlist.begin(); v != vlist.end(); ++v) {
5472 sl.push_back (boost::dynamic_pointer_cast<Stripable> (*v));
5475 add_stripables (sl);
5479 Editor::add_routes (RouteList& rlist)
5483 for (RouteList::iterator r = rlist.begin(); r != rlist.end(); ++r) {
5487 add_stripables (sl);
5491 Editor::add_stripables (StripableList& sl)
5493 list<TimeAxisView*> new_views;
5494 boost::shared_ptr<VCA> v;
5495 boost::shared_ptr<Route> r;
5496 TrackViewList new_selection;
5497 bool from_scratch = (track_views.size() == 0);
5499 sl.sort (Stripable::Sorter());
5501 for (StripableList::iterator s = sl.begin(); s != sl.end(); ++s) {
5503 if ((v = boost::dynamic_pointer_cast<VCA> (*s)) != 0) {
5505 VCATimeAxisView* vtv = new VCATimeAxisView (*this, _session, *_track_canvas);
5507 new_views.push_back (vtv);
5509 } else if ((r = boost::dynamic_pointer_cast<Route> (*s)) != 0) {
5511 if (r->is_auditioner() || r->is_monitor()) {
5515 RouteTimeAxisView* rtv;
5516 DataType dt = r->input()->default_type();
5518 if (dt == ARDOUR::DataType::AUDIO) {
5519 rtv = new AudioTimeAxisView (*this, _session, *_track_canvas);
5521 } else if (dt == ARDOUR::DataType::MIDI) {
5522 rtv = new MidiTimeAxisView (*this, _session, *_track_canvas);
5525 throw unknown_type();
5528 new_views.push_back (rtv);
5529 track_views.push_back (rtv);
5530 new_selection.push_back (rtv);
5532 rtv->effective_gain_display ();
5534 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
5535 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
5539 if (new_views.size() > 0) {
5540 _routes->time_axis_views_added (new_views);
5541 //_summary->routes_added (new_selection); /* XXX requires RouteTimeAxisViewList */
5544 /* note: !new_selection.empty() means that we got some routes rather
5548 if (!from_scratch && !new_selection.empty()) {
5549 selection->set (new_selection);
5550 begin_selection_op_history();
5553 if (show_editor_mixer_when_tracks_arrive && !new_selection.empty()) {
5554 show_editor_mixer (true);
5557 editor_list_button.set_sensitive (true);
5561 Editor::timeaxisview_deleted (TimeAxisView *tv)
5563 if (tv == entered_track) {
5567 if (_session && _session->deletion_in_progress()) {
5568 /* the situation is under control */
5572 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
5574 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
5576 _routes->route_removed (tv);
5578 TimeAxisView::Children c = tv->get_child_list ();
5579 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
5580 if (entered_track == i->get()) {
5585 /* remove it from the list of track views */
5587 TrackViewList::iterator i;
5589 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
5590 i = track_views.erase (i);
5593 /* update whatever the current mixer strip is displaying, if revelant */
5595 boost::shared_ptr<Route> route;
5598 route = rtav->route ();
5601 if (current_mixer_strip && current_mixer_strip->route() == route) {
5603 TimeAxisView* next_tv;
5605 if (track_views.empty()) {
5607 } else if (i == track_views.end()) {
5608 next_tv = track_views.front();
5613 // skip VCAs (cannot be selected, n/a in editor-mixer)
5614 if (dynamic_cast<VCATimeAxisView*> (next_tv)) {
5615 /* VCAs are sorted last in line -- route_sorter.h, jump to top */
5616 next_tv = track_views.front();
5618 if (dynamic_cast<VCATimeAxisView*> (next_tv)) {
5619 /* just in case: no master, only a VCA remains */
5625 set_selected_mixer_strip (*next_tv);
5627 /* make the editor mixer strip go away setting the
5628 * button to inactive (which also unticks the menu option)
5631 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
5637 Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection)
5643 DisplaySuspender ds;
5644 PresentationInfo::ChangeSuspender cs;
5646 if (apply_to_selection) {
5647 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end();) {
5649 TrackSelection::iterator j = i;
5652 hide_track_in_display (*i, false);
5657 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
5659 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
5660 /* this will hide the mixer strip */
5661 set_selected_mixer_strip (*tv);
5664 _routes->hide_track_in_display (*tv);
5669 Editor::show_track_in_display (TimeAxisView* tv, bool move_into_view)
5674 _routes->show_track_in_display (*tv);
5675 if (move_into_view) {
5676 ensure_time_axis_view_is_visible (*tv, false);
5681 Editor::sync_track_view_list_and_routes ()
5683 track_views = TrackViewList (_routes->views ());
5685 _summary->set_background_dirty();
5686 _group_tabs->set_dirty ();
5688 return false; // do not call again (until needed)
5692 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5694 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5699 /** Find a StripableTimeAxisView by the ID of its stripable */
5700 StripableTimeAxisView*
5701 Editor::get_stripable_time_axis_by_id (const PBD::ID& id) const
5703 StripableTimeAxisView* v;
5705 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5706 if((v = dynamic_cast<StripableTimeAxisView*>(*i)) != 0) {
5707 if(v->stripable()->id() == id) {
5717 Editor::fit_route_group (RouteGroup *g)
5719 TrackViewList ts = axis_views_from_routes (g->route_list ());
5724 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5726 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5729 _session->cancel_audition ();
5733 if (_session->is_auditioning()) {
5734 _session->cancel_audition ();
5735 if (r == last_audition_region) {
5740 _session->audition_region (r);
5741 last_audition_region = r;
5746 Editor::hide_a_region (boost::shared_ptr<Region> r)
5748 r->set_hidden (true);
5752 Editor::show_a_region (boost::shared_ptr<Region> r)
5754 r->set_hidden (false);
5758 Editor::audition_region_from_region_list ()
5760 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5764 Editor::hide_region_from_region_list ()
5766 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5770 Editor::show_region_in_region_list ()
5772 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5776 Editor::step_edit_status_change (bool yn)
5779 start_step_editing ();
5781 stop_step_editing ();
5786 Editor::start_step_editing ()
5788 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5792 Editor::stop_step_editing ()
5794 step_edit_connection.disconnect ();
5798 Editor::check_step_edit ()
5800 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5801 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5803 mtv->check_step_edit ();
5807 return true; // do it again, till we stop
5811 Editor::scroll_press (Direction dir)
5813 ++_scroll_callbacks;
5815 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5816 /* delay the first auto-repeat */
5822 scroll_backward (1);
5830 scroll_up_one_track ();
5834 scroll_down_one_track ();
5838 /* do hacky auto-repeat */
5839 if (!_scroll_connection.connected ()) {
5841 _scroll_connection = Glib::signal_timeout().connect (
5842 sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100
5845 _scroll_callbacks = 0;
5852 Editor::scroll_release ()
5854 _scroll_connection.disconnect ();
5857 /** Queue a change for the Editor viewport x origin to follow the playhead */
5859 Editor::reset_x_origin_to_follow_playhead ()
5861 samplepos_t const sample = playhead_cursor->current_sample ();
5863 if (sample < _leftmost_sample || sample > _leftmost_sample + current_page_samples()) {
5865 if (_session->transport_speed() < 0) {
5867 if (sample > (current_page_samples() / 2)) {
5868 center_screen (sample-(current_page_samples()/2));
5870 center_screen (current_page_samples()/2);
5877 if (sample < _leftmost_sample) {
5879 if (_session->transport_rolling()) {
5880 /* rolling; end up with the playhead at the right of the page */
5881 l = sample - current_page_samples ();
5883 /* not rolling: end up with the playhead 1/4 of the way along the page */
5884 l = sample - current_page_samples() / 4;
5888 if (_session->transport_rolling()) {
5889 /* rolling: end up with the playhead on the left of the page */
5892 /* not rolling: end up with the playhead 3/4 of the way along the page */
5893 l = sample - 3 * current_page_samples() / 4;
5901 center_screen_internal (l + (current_page_samples() / 2), current_page_samples ());
5907 Editor::super_rapid_screen_update ()
5909 if (!_session || !_session->engine().running()) {
5913 /* METERING / MIXER STRIPS */
5915 /* update track meters, if required */
5916 if (contents().is_mapped() && meters_running) {
5917 RouteTimeAxisView* rtv;
5918 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5919 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5920 rtv->fast_update ();
5925 /* and any current mixer strip */
5926 if (current_mixer_strip) {
5927 current_mixer_strip->fast_update ();
5930 bool latent_locate = false;
5931 samplepos_t sample = _session->audible_sample (&latent_locate);
5932 const int64_t now = g_get_monotonic_time ();
5935 if (_session->exporting ()) {
5936 /* freewheel/export may be faster or slower than transport_speed() / SR.
5937 * Also exporting multiple ranges locates/jumps without a _pending_locate_request.
5939 _last_update_time = 0;
5942 if (!_session->transport_rolling () || _session->is_auditioning ()) {
5943 /* Do not interpolate the playhead position; just set it */
5944 _last_update_time = 0;
5947 if (_last_update_time > 0) {
5948 /* interpolate and smoothen playhead position */
5949 const double ds = (now - _last_update_time) * _session->transport_speed() * _session->nominal_sample_rate () * 1e-6;
5950 samplepos_t guess = playhead_cursor->current_sample () + rint (ds);
5951 err = sample - guess;
5953 guess += err * .12 + _err_screen_engine; // time-constant based on 25fps (super_rapid_screen_update)
5954 _err_screen_engine += .0144 * (err - _err_screen_engine); // tc^2
5957 printf ("eng: %ld gui:%ld (%+6.1f) diff: %6.1f (err: %7.2f)\n",
5959 err, _err_screen_engine);
5964 _err_screen_engine = 0;
5967 if (err > 8192 || latent_locate) {
5968 // in case of x-runs or freewheeling
5969 _last_update_time = 0;
5970 sample = _session->audible_sample ();
5972 _last_update_time = now;
5975 /* snapped cursor stuff (the snapped_cursor shows where an operation is going to occur) */
5977 MusicSample where (sample, 0);
5978 if (!UIConfiguration::instance().get_show_snapped_cursor()) {
5979 snapped_cursor->hide ();
5980 } else if (_edit_point == EditAtPlayhead && !_dragging_playhead) {
5981 /* EditAtPlayhead does not snap */
5982 } else if (_edit_point == EditAtSelectedMarker) {
5983 /* NOTE: I don't think EditAtSelectedMarker should snap. They are what they are.
5984 * however, the current editing code -does- snap so I'll draw it that way for now.
5986 if (!selection->markers.empty()) {
5987 MusicSample ms (selection->markers.front()->position(), 0);
5988 snap_to (ms); // should use snap_to_with_modifier?
5989 snapped_cursor->set_position (ms.sample);
5990 snapped_cursor->show ();
5992 } else if (mouse_sample (where.sample, ignored)) { // cursor is in the editing canvas. show it.
5993 snapped_cursor->show ();
5994 } else { // mouse is out of the editing canvas. hide the snapped_cursor
5995 snapped_cursor->hide ();
5998 /* There are a few reasons why we might not update the playhead / viewport stuff:
6000 * 1. we don't update things when there's a pending locate request, otherwise
6001 * when the editor requests a locate there is a chance that this method
6002 * will move the playhead before the locate request is processed, causing
6004 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
6005 * 3. if we're still at the same frame that we were last time, there's nothing to do.
6007 if (_pending_locate_request) {
6008 _last_update_time = 0;
6012 if (_dragging_playhead) {
6013 _last_update_time = 0;
6017 if (playhead_cursor->current_sample () == sample) {
6021 playhead_cursor->set_position (sample);
6023 if (_session->requested_return_sample() >= 0) {
6024 _last_update_time = 0;
6028 if (!_follow_playhead || pending_visual_change.being_handled) {
6029 /* We only do this if we aren't already
6030 * handling a visual change (ie if
6031 * pending_visual_change.being_handled is
6032 * false) so that these requests don't stack
6033 * up there are too many of them to handle in
6039 if (!_stationary_playhead) {
6040 reset_x_origin_to_follow_playhead ();
6042 samplepos_t const sample = playhead_cursor->current_sample ();
6043 double target = ((double)sample - (double)current_page_samples() / 2.0);
6044 if (target <= 0.0) {
6047 /* compare to EditorCursor::set_position() */
6048 double const old_pos = sample_to_pixel_unrounded (_leftmost_sample);
6049 double const new_pos = sample_to_pixel_unrounded (target);
6050 if (rint (new_pos) != rint (old_pos)) {
6051 reset_x_origin (pixel_to_sample (new_pos));
6058 Editor::session_going_away ()
6060 _have_idled = false;
6062 _session_connections.drop_connections ();
6064 super_rapid_screen_update_connection.disconnect ();
6066 selection->clear ();
6067 cut_buffer->clear ();
6069 clicked_regionview = 0;
6070 clicked_axisview = 0;
6071 clicked_routeview = 0;
6072 entered_regionview = 0;
6074 _last_update_time = 0;
6077 playhead_cursor->hide ();
6079 /* rip everything out of the list displays */
6083 _route_groups->clear ();
6085 /* do this first so that deleting a track doesn't reset cms to null
6086 and thus cause a leak.
6089 if (current_mixer_strip) {
6090 if (current_mixer_strip->get_parent() != 0) {
6091 global_hpacker.remove (*current_mixer_strip);
6093 delete current_mixer_strip;
6094 current_mixer_strip = 0;
6097 /* delete all trackviews */
6099 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6102 track_views.clear ();
6104 nudge_clock->set_session (0);
6106 editor_list_button.set_active(false);
6107 editor_list_button.set_sensitive(false);
6109 /* clear tempo/meter rulers */
6110 remove_metric_marks ();
6111 clear_marker_display ();
6117 stop_step_editing ();
6121 /* get rid of any existing editor mixer strip */
6123 WindowTitle title(Glib::get_application_name());
6124 title += _("Editor");
6126 own_window()->set_title (title.get_string());
6129 SessionHandlePtr::session_going_away ();
6133 Editor::trigger_script (int i)
6135 LuaInstance::instance()-> call_action (i);
6139 Editor::show_editor_list (bool yn)
6142 _editor_list_vbox.show ();
6144 _editor_list_vbox.hide ();
6149 Editor::change_region_layering_order (bool from_context_menu)
6151 const samplepos_t position = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context_menu);
6153 if (!clicked_routeview) {
6154 if (layering_order_editor) {
6155 layering_order_editor->hide ();
6160 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
6166 boost::shared_ptr<Playlist> pl = track->playlist();
6172 if (layering_order_editor == 0) {
6173 layering_order_editor = new RegionLayeringOrderEditor (*this);
6176 layering_order_editor->set_context (clicked_routeview->name(), _session, clicked_routeview, pl, position);
6177 layering_order_editor->maybe_present ();
6181 Editor::update_region_layering_order_editor ()
6183 if (layering_order_editor && layering_order_editor->is_visible ()) {
6184 change_region_layering_order (true);
6189 Editor::setup_fade_images ()
6191 _xfade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadein-linear")));
6192 _xfade_in_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadein-symmetric")));
6193 _xfade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadein-fast-cut")));
6194 _xfade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadein-slow-cut")));
6195 _xfade_in_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadein-constant-power")));
6197 _xfade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadeout-linear")));
6198 _xfade_out_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-symmetric")));
6199 _xfade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut")));
6200 _xfade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut")));
6201 _xfade_out_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-constant-power")));
6205 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
6207 Editor::action_menu_item (std::string const & name)
6209 Glib::RefPtr<Action> a = editor_actions->get_action (name);
6212 return *manage (a->create_menu_item ());
6216 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
6218 EventBox* b = manage (new EventBox);
6219 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
6220 Label* l = manage (new Label (name));
6224 _the_notebook.append_page (widget, *b);
6228 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
6230 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
6231 _the_notebook.set_current_page (_the_notebook.page_num (*page));
6234 if (ev->type == GDK_2BUTTON_PRESS) {
6236 /* double-click on a notebook tab shrinks or expands the notebook */
6238 if (_notebook_shrunk) {
6239 if (pre_notebook_shrink_pane_width) {
6240 edit_pane.set_divider (0, *pre_notebook_shrink_pane_width);
6242 _notebook_shrunk = false;
6244 pre_notebook_shrink_pane_width = edit_pane.get_divider();
6246 /* this expands the LHS of the edit pane to cover the notebook
6247 PAGE but leaves the tabs visible.
6249 edit_pane.set_divider (0, edit_pane.get_divider() + page->get_width());
6250 _notebook_shrunk = true;
6258 Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
6260 using namespace Menu_Helpers;
6262 MenuList& items = _control_point_context_menu.items ();
6265 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &Editor::edit_control_point), item)));
6266 items.push_back (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun (*this, &Editor::remove_control_point), item)));
6267 if (!can_remove_control_point (item)) {
6268 items.back().set_sensitive (false);
6271 _control_point_context_menu.popup (event->button.button, event->button.time);
6275 Editor::popup_note_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
6277 using namespace Menu_Helpers;
6279 NoteBase* note = reinterpret_cast<NoteBase*>(item->get_data("notebase"));
6284 /* We need to get the selection here and pass it to the operations, since
6285 popping up the menu will cause a region leave event which clears
6286 entered_regionview. */
6288 MidiRegionView& mrv = note->region_view();
6289 const RegionSelection rs = get_regions_from_selection_and_entered ();
6290 const uint32_t sel_size = mrv.selection_size ();
6292 MenuList& items = _note_context_menu.items();
6296 items.push_back(MenuElem(_("Delete"),
6297 sigc::mem_fun(mrv, &MidiRegionView::delete_selection)));
6300 items.push_back(MenuElem(_("Edit..."),
6301 sigc::bind(sigc::mem_fun(*this, &Editor::edit_notes), &mrv)));
6302 if (sel_size != 1) {
6303 items.back().set_sensitive (false);
6306 items.push_back(MenuElem(_("Transpose..."),
6307 sigc::bind(sigc::mem_fun(*this, &Editor::transpose_regions), rs)));
6310 items.push_back(MenuElem(_("Legatize"),
6311 sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, false)));
6313 items.back().set_sensitive (false);
6316 items.push_back(MenuElem(_("Quantize..."),
6317 sigc::bind(sigc::mem_fun(*this, &Editor::quantize_regions), rs)));
6319 items.push_back(MenuElem(_("Remove Overlap"),
6320 sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, true)));
6322 items.back().set_sensitive (false);
6325 items.push_back(MenuElem(_("Transform..."),
6326 sigc::bind(sigc::mem_fun(*this, &Editor::transform_regions), rs)));
6328 _note_context_menu.popup (event->button.button, event->button.time);
6332 Editor::zoom_vertical_modifier_released()
6334 _stepping_axis_view = 0;
6338 Editor::ui_parameter_changed (string parameter)
6340 if (parameter == "icon-set") {
6341 while (!_cursor_stack.empty()) {
6342 _cursor_stack.pop_back();
6344 _cursors->set_cursor_set (UIConfiguration::instance().get_icon_set());
6345 _cursor_stack.push_back(_cursors->grabber);
6346 edit_pane.set_drag_cursor (*_cursors->expand_left_right);
6347 editor_summary_pane.set_drag_cursor (*_cursors->expand_up_down);
6349 } else if (parameter == "draggable-playhead") {
6350 if (_verbose_cursor) {
6351 playhead_cursor->set_sensitive (UIConfiguration::instance().get_draggable_playhead());
6353 } else if (parameter == "use-note-bars-for-velocity") {
6354 ArdourCanvas::Note::set_show_velocity_bars (UIConfiguration::instance().get_use_note_bars_for_velocity());
6355 _track_canvas->request_redraw (_track_canvas->visible_area());
6356 } else if (parameter == "use-note-color-for-velocity") {
6357 /* handled individually by each MidiRegionView */
6362 Editor::use_own_window (bool and_fill_it)
6364 bool new_window = !own_window();
6366 Gtk::Window* win = Tabbable::use_own_window (and_fill_it);
6368 if (win && new_window) {
6369 win->set_name ("EditorWindow");
6371 ARDOUR_UI::instance()->setup_toplevel_window (*win, _("Editor"), this);
6373 // win->signal_realize().connect (*this, &Editor::on_realize);
6374 win->signal_event().connect (sigc::bind (sigc::ptr_fun (&Keyboard::catch_user_event_for_pre_dialog_focus), win));
6375 win->signal_event().connect (sigc::mem_fun (*this, &Editor::generic_event_handler));
6376 win->set_data ("ardour-bindings", bindings);
6381 DisplaySuspender ds;
6382 contents().show_all ();
6384 /* XXX: this is a bit unfortunate; it would probably
6385 be nicer if we could just call show () above rather
6386 than needing the show_all ()
6389 /* re-hide stuff if necessary */
6390 editor_list_button_toggled ();
6391 parameter_changed ("show-summary");
6392 parameter_changed ("show-group-tabs");
6393 parameter_changed ("show-zoom-tools");
6395 /* now reset all audio_time_axis heights, because widgets might need
6401 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6402 tv = (static_cast<TimeAxisView*>(*i));
6403 tv->reset_height ();
6406 if (current_mixer_strip) {
6407 current_mixer_strip->hide_things ();
6408 current_mixer_strip->parameter_changed ("mixer-element-visibility");