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 || !_session || no_save_instant) {
990 _session->add_instant_xml(get_state());
994 Editor::control_vertical_zoom_in_all ()
996 tav_zoom_smooth (false, true);
1000 Editor::control_vertical_zoom_out_all ()
1002 tav_zoom_smooth (true, true);
1006 Editor::control_vertical_zoom_in_selected ()
1008 tav_zoom_smooth (false, false);
1012 Editor::control_vertical_zoom_out_selected ()
1014 tav_zoom_smooth (true, false);
1018 Editor::control_view (uint32_t view)
1020 goto_visual_state (view);
1024 Editor::control_unselect ()
1026 selection->clear_tracks ();
1030 Editor::control_select (boost::shared_ptr<Stripable> s, Selection::Operation op)
1032 TimeAxisView* tav = time_axis_view_from_stripable (s);
1036 case Selection::Add:
1037 selection->add (tav);
1039 case Selection::Toggle:
1040 selection->toggle (tav);
1042 case Selection::Extend:
1044 case Selection::Set:
1045 selection->set (tav);
1049 selection->clear_tracks ();
1054 Editor::control_step_tracks_up ()
1056 scroll_tracks_up_line ();
1060 Editor::control_step_tracks_down ()
1062 scroll_tracks_down_line ();
1066 Editor::control_scroll (float fraction)
1068 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
1074 double step = fraction * current_page_samples();
1077 _control_scroll_target is an optional<T>
1079 it acts like a pointer to an samplepos_t, with
1080 a operator conversion to boolean to check
1081 that it has a value could possibly use
1082 playhead_cursor->current_sample to store the
1083 value and a boolean in the class to know
1084 when it's out of date
1087 if (!_control_scroll_target) {
1088 _control_scroll_target = _session->transport_sample();
1089 _dragging_playhead = true;
1092 if ((fraction < 0.0f) && (*_control_scroll_target <= (samplepos_t) fabs(step))) {
1093 *_control_scroll_target = 0;
1094 } else if ((fraction > 0.0f) && (max_samplepos - *_control_scroll_target < step)) {
1095 *_control_scroll_target = max_samplepos - (current_page_samples()*2); // allow room for slop in where the PH is on the screen
1097 *_control_scroll_target += (samplepos_t) trunc (step);
1100 /* move visuals, we'll catch up with it later */
1102 playhead_cursor->set_position (*_control_scroll_target);
1103 UpdateAllTransportClocks (*_control_scroll_target);
1105 if (*_control_scroll_target > (current_page_samples() / 2)) {
1106 /* try to center PH in window */
1107 reset_x_origin (*_control_scroll_target - (current_page_samples()/2));
1113 Now we do a timeout to actually bring the session to the right place
1114 according to the playhead. This is to avoid reading disk buffers on every
1115 call to control_scroll, which is driven by ScrollTimeline and therefore
1116 probably by a control surface wheel which can generate lots of events.
1118 /* cancel the existing timeout */
1120 control_scroll_connection.disconnect ();
1122 /* add the next timeout */
1124 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
1128 Editor::deferred_control_scroll (samplepos_t /*target*/)
1130 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
1131 /* reset for next stream */
1132 _control_scroll_target = boost::none;
1133 _dragging_playhead = false;
1138 Editor::access_action (const std::string& action_group, const std::string& action_item)
1144 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
1147 act = ActionManager::get_action (action_group.c_str(), action_item.c_str());
1155 Editor::set_toggleaction (const std::string& action_group, const std::string& action_item, bool s)
1157 ActionManager::set_toggleaction_state (action_group.c_str(), action_item.c_str(), s);
1161 Editor::on_realize ()
1165 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
1166 start_lock_event_timing ();
1171 Editor::start_lock_event_timing ()
1173 /* check if we should lock the GUI every 30 seconds */
1175 Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::lock_timeout_callback), 30 * 1000);
1179 Editor::generic_event_handler (GdkEvent* ev)
1182 case GDK_BUTTON_PRESS:
1183 case GDK_BUTTON_RELEASE:
1184 case GDK_MOTION_NOTIFY:
1186 case GDK_KEY_RELEASE:
1187 if (contents().is_mapped()) {
1188 gettimeofday (&last_event_time, 0);
1192 case GDK_LEAVE_NOTIFY:
1193 switch (ev->crossing.detail) {
1194 case GDK_NOTIFY_UNKNOWN:
1195 case GDK_NOTIFY_INFERIOR:
1196 case GDK_NOTIFY_ANCESTOR:
1198 case GDK_NOTIFY_VIRTUAL:
1199 case GDK_NOTIFY_NONLINEAR:
1200 case GDK_NOTIFY_NONLINEAR_VIRTUAL:
1201 /* leaving window, so reset focus, thus ending any and
1202 all text entry operations.
1204 ARDOUR_UI::instance()->reset_focus (&contents());
1217 Editor::lock_timeout_callback ()
1219 struct timeval now, delta;
1221 gettimeofday (&now, 0);
1223 timersub (&now, &last_event_time, &delta);
1225 if (delta.tv_sec > (time_t) UIConfiguration::instance().get_lock_gui_after_seconds()) {
1227 /* don't call again. Returning false will effectively
1228 disconnect us from the timer callback.
1230 unlock() will call start_lock_event_timing() to get things
1240 Editor::map_position_change (samplepos_t sample)
1242 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, sample)
1244 if (_session == 0) {
1248 if (_follow_playhead) {
1249 center_screen (sample);
1252 playhead_cursor->set_position (sample);
1256 Editor::center_screen (samplepos_t sample)
1258 samplecnt_t const page = _visible_canvas_width * samples_per_pixel;
1260 /* if we're off the page, then scroll.
1263 if (sample < _leftmost_sample || sample >= _leftmost_sample + page) {
1264 center_screen_internal (sample, page);
1269 Editor::center_screen_internal (samplepos_t sample, float page)
1273 if (sample > page) {
1274 sample -= (samplepos_t) page;
1279 reset_x_origin (sample);
1284 Editor::update_title ()
1286 ENSURE_GUI_THREAD (*this, &Editor::update_title);
1288 if (!own_window()) {
1293 bool dirty = _session->dirty();
1295 string session_name;
1297 if (_session->snap_name() != _session->name()) {
1298 session_name = _session->snap_name();
1300 session_name = _session->name();
1304 session_name = "*" + session_name;
1307 WindowTitle title(session_name);
1308 title += S_("Window|Editor");
1309 title += Glib::get_application_name();
1310 own_window()->set_title (title.get_string());
1312 /* ::session_going_away() will have taken care of it */
1317 Editor::set_session (Session *t)
1319 SessionHandlePtr::set_session (t);
1325 /* initialize _leftmost_sample to the extents of the session
1326 * this prevents a bogus setting of leftmost = "0" if the summary view asks for the leftmost sample
1327 * before the visible state has been loaded from instant.xml */
1328 _leftmost_sample = session_gui_extents().first;
1330 _playlist_selector->set_session (_session);
1331 nudge_clock->set_session (_session);
1332 _summary->set_session (_session);
1333 _group_tabs->set_session (_session);
1334 _route_groups->set_session (_session);
1335 _regions->set_session (_session);
1336 _snapshots->set_session (_session);
1337 _routes->set_session (_session);
1338 _locations->set_session (_session);
1339 _time_info_box->set_session (_session);
1341 if (rhythm_ferret) {
1342 rhythm_ferret->set_session (_session);
1345 if (analysis_window) {
1346 analysis_window->set_session (_session);
1350 sfbrowser->set_session (_session);
1353 compute_fixed_ruler_scale ();
1355 /* Make sure we have auto loop and auto punch ranges */
1357 Location* loc = _session->locations()->auto_loop_location();
1359 loc->set_name (_("Loop"));
1362 loc = _session->locations()->auto_punch_location();
1365 loc->set_name (_("Punch"));
1368 refresh_location_display ();
1370 /* This must happen after refresh_location_display(), as (amongst other things) we restore
1371 * the selected Marker; this needs the LocationMarker list to be available.
1373 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1374 set_state (*node, Stateful::loading_state_version);
1376 /* catch up on selection state, etc. */
1379 sc.add (Properties::selected);
1380 presentation_info_changed (sc);
1382 /* catch up with the playhead */
1384 _session->request_locate (playhead_cursor->current_sample ());
1385 _pending_initial_locate = true;
1389 /* These signals can all be emitted by a non-GUI thread. Therefore the
1390 handlers for them must not attempt to directly interact with the GUI,
1391 but use PBD::Signal<T>::connect() which accepts an event loop
1392 ("context") where the handler will be asked to run.
1395 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::step_edit_status_change, this, _1), gui_context());
1396 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1397 _session->TransportLooped.connect (_session_connections, invalidator (*this), boost::bind (&Editor::transport_looped, this), gui_context());
1398 _session->PositionChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_position_change, this, _1), gui_context());
1399 _session->vca_manager().VCAAdded.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_vcas, this, _1), gui_context());
1400 _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_routes, this, _1), gui_context());
1401 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1402 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::tempo_map_changed, this, _1), gui_context());
1403 _session->tempo_map().MetricPositionChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::tempometric_position_changed, this, _1), gui_context());
1404 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1405 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::parameter_changed, this, _1), gui_context());
1406 _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Editor::session_state_saved, this, _1), gui_context());
1407 _session->locations()->added.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_new_location, this, _1), gui_context());
1408 _session->locations()->removed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::location_gone, this, _1), gui_context());
1409 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1410 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1412 playhead_cursor->track_canvas_item().reparent ((ArdourCanvas::Item*) get_cursor_scroll_group());
1413 playhead_cursor->show ();
1415 snapped_cursor->track_canvas_item().reparent ((ArdourCanvas::Item*) get_cursor_scroll_group());
1416 snapped_cursor->set_color (UIConfiguration::instance().color ("edit point"));
1417 snapped_cursor->show ();
1419 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1420 Config->map_parameters (pc);
1421 _session->config.map_parameters (pc);
1423 restore_ruler_visibility ();
1424 //tempo_map_changed (PropertyChange (0));
1425 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1427 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1428 (static_cast<TimeAxisView*>(*i))->set_samples_per_pixel (samples_per_pixel);
1431 super_rapid_screen_update_connection = Timers::super_rapid_connect (
1432 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1435 /* register for undo history */
1436 _session->register_with_memento_command_factory(id(), this);
1437 _session->register_with_memento_command_factory(_selection_memento->id(), _selection_memento);
1439 LuaInstance::instance()->set_session(_session);
1441 start_updating_meters ();
1445 Editor::fill_xfade_menu (Menu_Helpers::MenuList& items, bool start)
1447 using namespace Menu_Helpers;
1449 void (Editor::*emf)(FadeShape);
1450 std::map<ARDOUR::FadeShape,Gtk::Image*>* images;
1453 images = &_xfade_in_images;
1454 emf = &Editor::set_fade_in_shape;
1456 images = &_xfade_out_images;
1457 emf = &Editor::set_fade_out_shape;
1462 _("Linear (for highly correlated material)"),
1463 *(*images)[FadeLinear],
1464 sigc::bind (sigc::mem_fun (*this, emf), FadeLinear)
1468 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1472 _("Constant power"),
1473 *(*images)[FadeConstantPower],
1474 sigc::bind (sigc::mem_fun (*this, emf), FadeConstantPower)
1477 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1482 *(*images)[FadeSymmetric],
1483 sigc::bind (sigc::mem_fun (*this, emf), FadeSymmetric)
1487 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1492 *(*images)[FadeSlow],
1493 sigc::bind (sigc::mem_fun (*this, emf), FadeSlow)
1496 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1501 *(*images)[FadeFast],
1502 sigc::bind (sigc::mem_fun (*this, emf), FadeFast)
1505 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1508 /** Pop up a context menu for when the user clicks on a start crossfade */
1510 Editor::popup_xfade_in_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType /*item_type*/)
1512 using namespace Menu_Helpers;
1513 AudioRegionView* arv = dynamic_cast<AudioRegionView*> ((RegionView*)item->get_data ("regionview"));
1518 MenuList& items (xfade_in_context_menu.items());
1521 if (arv->audio_region()->fade_in_active()) {
1522 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1524 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1527 items.push_back (SeparatorElem());
1528 fill_xfade_menu (items, true);
1530 xfade_in_context_menu.popup (button, time);
1533 /** Pop up a context menu for when the user clicks on an end crossfade */
1535 Editor::popup_xfade_out_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType /*item_type*/)
1537 using namespace Menu_Helpers;
1538 AudioRegionView* arv = dynamic_cast<AudioRegionView*> ((RegionView*)item->get_data ("regionview"));
1543 MenuList& items (xfade_out_context_menu.items());
1546 if (arv->audio_region()->fade_out_active()) {
1547 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1549 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1552 items.push_back (SeparatorElem());
1553 fill_xfade_menu (items, false);
1555 xfade_out_context_menu.popup (button, time);
1559 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1561 using namespace Menu_Helpers;
1562 Menu* (Editor::*build_menu_function)();
1565 switch (item_type) {
1567 case RegionViewName:
1568 case RegionViewNameHighlight:
1569 case LeftFrameHandle:
1570 case RightFrameHandle:
1571 if (with_selection) {
1572 build_menu_function = &Editor::build_track_selection_context_menu;
1574 build_menu_function = &Editor::build_track_region_context_menu;
1579 if (with_selection) {
1580 build_menu_function = &Editor::build_track_selection_context_menu;
1582 build_menu_function = &Editor::build_track_context_menu;
1587 if (clicked_routeview->track()) {
1588 build_menu_function = &Editor::build_track_context_menu;
1590 build_menu_function = &Editor::build_track_bus_context_menu;
1595 /* probably shouldn't happen but if it does, we don't care */
1599 menu = (this->*build_menu_function)();
1600 menu->set_name ("ArdourContextMenu");
1602 /* now handle specific situations */
1604 switch (item_type) {
1606 case RegionViewName:
1607 case RegionViewNameHighlight:
1608 case LeftFrameHandle:
1609 case RightFrameHandle:
1610 if (!with_selection) {
1611 if (region_edit_menu_split_item) {
1612 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1613 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1615 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1618 if (region_edit_menu_split_multichannel_item) {
1619 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1620 region_edit_menu_split_multichannel_item->set_sensitive (true);
1622 region_edit_menu_split_multichannel_item->set_sensitive (false);
1635 /* probably shouldn't happen but if it does, we don't care */
1639 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1641 /* Bounce to disk */
1643 using namespace Menu_Helpers;
1644 MenuList& edit_items = menu->items();
1646 edit_items.push_back (SeparatorElem());
1648 switch (clicked_routeview->audio_track()->freeze_state()) {
1649 case AudioTrack::NoFreeze:
1650 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1653 case AudioTrack::Frozen:
1654 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1657 case AudioTrack::UnFrozen:
1658 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1664 if (item_type == StreamItem && clicked_routeview) {
1665 clicked_routeview->build_underlay_menu(menu);
1668 /* When the region menu is opened, we setup the actions so that they look right
1671 sensitize_the_right_region_actions (false);
1672 _last_region_menu_was_main = false;
1674 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1675 menu->popup (button, time);
1679 Editor::build_track_context_menu ()
1681 using namespace Menu_Helpers;
1683 MenuList& edit_items = track_context_menu.items();
1686 add_dstream_context_items (edit_items);
1687 return &track_context_menu;
1691 Editor::build_track_bus_context_menu ()
1693 using namespace Menu_Helpers;
1695 MenuList& edit_items = track_context_menu.items();
1698 add_bus_context_items (edit_items);
1699 return &track_context_menu;
1703 Editor::build_track_region_context_menu ()
1705 using namespace Menu_Helpers;
1706 MenuList& edit_items = track_region_context_menu.items();
1709 /* we've just cleared the track region context menu, so the menu that these
1710 two items were on will have disappeared; stop them dangling.
1712 region_edit_menu_split_item = 0;
1713 region_edit_menu_split_multichannel_item = 0;
1715 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1718 boost::shared_ptr<Track> tr;
1719 boost::shared_ptr<Playlist> pl;
1721 if ((tr = rtv->track())) {
1722 add_region_context_items (edit_items, tr);
1726 add_dstream_context_items (edit_items);
1728 return &track_region_context_menu;
1732 Editor::loudness_analyze_region_selection ()
1737 Selection& s (PublicEditor::instance ().get_selection ());
1738 RegionSelection ars = s.regions;
1739 ARDOUR::AnalysisGraph ag (_session);
1740 samplecnt_t total_work = 0;
1742 for (RegionSelection::iterator j = ars.begin (); j != ars.end (); ++j) {
1743 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*j);
1747 if (!boost::dynamic_pointer_cast<AudioRegion> (arv->region ())) {
1750 assert (dynamic_cast<RouteTimeAxisView *> (&arv->get_time_axis_view ()));
1751 total_work += arv->region ()->length ();
1754 SimpleProgressDialog spd (_("Region Loudness Analysis"), sigc::mem_fun (ag, &AnalysisGraph::cancel));
1756 ag.set_total_samples (total_work);
1757 ag.Progress.connect_same_thread (c, boost::bind (&SimpleProgressDialog::update_progress, &spd, _1, _2));
1760 for (RegionSelection::iterator j = ars.begin (); j != ars.end (); ++j) {
1761 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*j);
1765 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (arv->region ());
1769 ag.analyze_region (ar);
1772 if (!ag.canceled ()) {
1773 ExportReport er (_("Audio Report/Analysis"), ag.results ());
1779 Editor::loudness_analyze_range_selection ()
1784 Selection& s (PublicEditor::instance ().get_selection ());
1785 TimeSelection ts = s.time;
1786 ARDOUR::AnalysisGraph ag (_session);
1787 samplecnt_t total_work = 0;
1789 for (TrackSelection::iterator i = s.tracks.begin (); i != s.tracks.end (); ++i) {
1790 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist> ((*i)->playlist ());
1794 RouteUI *rui = dynamic_cast<RouteUI *> (*i);
1798 for (std::list<AudioRange>::iterator j = ts.begin (); j != ts.end (); ++j) {
1799 total_work += j->length ();
1803 SimpleProgressDialog spd (_("Range Loudness Analysis"), sigc::mem_fun (ag, &AnalysisGraph::cancel));
1805 ag.set_total_samples (total_work);
1806 ag.Progress.connect_same_thread (c, boost::bind (&SimpleProgressDialog::update_progress, &spd, _1, _2));
1809 for (TrackSelection::iterator i = s.tracks.begin (); i != s.tracks.end (); ++i) {
1810 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist> ((*i)->playlist ());
1814 RouteUI *rui = dynamic_cast<RouteUI *> (*i);
1818 ag.analyze_range (rui->route (), pl, ts);
1821 if (!ag.canceled ()) {
1822 ExportReport er (_("Audio Report/Analysis"), ag.results ());
1828 Editor::spectral_analyze_region_selection ()
1830 if (analysis_window == 0) {
1831 analysis_window = new AnalysisWindow();
1834 analysis_window->set_session(_session);
1836 analysis_window->show_all();
1839 analysis_window->set_regionmode();
1840 analysis_window->analyze();
1842 analysis_window->present();
1846 Editor::spectral_analyze_range_selection()
1848 if (analysis_window == 0) {
1849 analysis_window = new AnalysisWindow();
1852 analysis_window->set_session(_session);
1854 analysis_window->show_all();
1857 analysis_window->set_rangemode();
1858 analysis_window->analyze();
1860 analysis_window->present();
1864 Editor::build_track_selection_context_menu ()
1866 using namespace Menu_Helpers;
1867 MenuList& edit_items = track_selection_context_menu.items();
1868 edit_items.clear ();
1870 add_selection_context_items (edit_items);
1871 // edit_items.push_back (SeparatorElem());
1872 // add_dstream_context_items (edit_items);
1874 return &track_selection_context_menu;
1878 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr<Track> track)
1880 using namespace Menu_Helpers;
1882 /* OK, stick the region submenu at the top of the list, and then add
1886 RegionSelection rs = get_regions_from_selection_and_entered ();
1888 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1890 if (_popup_region_menu_item == 0) {
1891 _popup_region_menu_item = new MenuItem (menu_item_name, false);
1892 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1893 _popup_region_menu_item->show ();
1895 _popup_region_menu_item->set_label (menu_item_name);
1898 /* No layering allowed in later is higher layering model */
1899 RefPtr<Action> act = ActionManager::get_action (X_("EditorMenu"), X_("RegionMenuLayering"));
1900 if (act && Config->get_layer_model() == LaterHigher) {
1901 act->set_sensitive (false);
1903 act->set_sensitive (true);
1906 const samplepos_t position = get_preferred_edit_position (EDIT_IGNORE_NONE, true);
1908 edit_items.push_back (*_popup_region_menu_item);
1909 if (Config->get_layer_model() == Manual && track->playlist()->count_regions_at (position) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1910 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region-context-menu")->create_menu_item ()));
1912 edit_items.push_back (SeparatorElem());
1915 /** Add context menu items relevant to selection ranges.
1916 * @param edit_items List to add the items to.
1919 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1921 using namespace Menu_Helpers;
1923 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1924 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1926 edit_items.push_back (SeparatorElem());
1927 edit_items.push_back (MenuElem (_("Zoom to Range"), sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_selection), Horizontal)));
1929 edit_items.push_back (SeparatorElem());
1930 edit_items.push_back (MenuElem (_("Loudness Analysis"), sigc::mem_fun(*this, &Editor::loudness_analyze_range_selection)));
1931 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::spectral_analyze_range_selection)));
1933 edit_items.push_back (SeparatorElem());
1935 edit_items.push_back (
1937 _("Move Range Start to Previous Region Boundary"),
1938 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, false)
1942 edit_items.push_back (
1944 _("Move Range Start to Next Region Boundary"),
1945 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, true)
1949 edit_items.push_back (
1951 _("Move Range End to Previous Region Boundary"),
1952 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, false)
1956 edit_items.push_back (
1958 _("Move Range End to Next Region Boundary"),
1959 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, true)
1963 edit_items.push_back (SeparatorElem());
1964 edit_items.push_back (MenuElem (_("Separate"), mem_fun(*this, &Editor::separate_region_from_selection)));
1965 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1967 edit_items.push_back (SeparatorElem());
1968 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1970 edit_items.push_back (SeparatorElem());
1971 edit_items.push_back (MenuElem (_("Set Loop from Selection"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1972 edit_items.push_back (MenuElem (_("Set Punch from Selection"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1973 edit_items.push_back (MenuElem (_("Set Session Start/End from Selection"), sigc::mem_fun(*this, &Editor::set_session_extents_from_selection)));
1975 edit_items.push_back (SeparatorElem());
1976 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1978 edit_items.push_back (SeparatorElem());
1979 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1980 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_range), false)));
1982 edit_items.push_back (SeparatorElem());
1983 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1984 edit_items.push_back (MenuElem (_("Consolidate Range with Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1985 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1986 edit_items.push_back (MenuElem (_("Bounce Range to Region List with Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1987 edit_items.push_back (MenuElem (_("Export Range..."), sigc::mem_fun(*this, &Editor::export_selection)));
1988 if (ARDOUR_UI::instance()->video_timeline->get_duration() > 0) {
1989 edit_items.push_back (MenuElem (_("Export Video Range..."), sigc::bind (sigc::mem_fun(*(ARDOUR_UI::instance()), &ARDOUR_UI::export_video), true)));
1995 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1997 using namespace Menu_Helpers;
2001 Menu *play_menu = manage (new Menu);
2002 MenuList& play_items = play_menu->items();
2003 play_menu->set_name ("ArdourContextMenu");
2005 play_items.push_back (MenuElem (_("Play from Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
2006 play_items.push_back (MenuElem (_("Play from Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
2007 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
2008 play_items.push_back (SeparatorElem());
2009 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
2011 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2015 Menu *select_menu = manage (new Menu);
2016 MenuList& select_items = select_menu->items();
2017 select_menu->set_name ("ArdourContextMenu");
2019 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2020 select_items.push_back (MenuElem (_("Select All Objects"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_objects), Selection::Set)));
2021 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2022 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2023 select_items.push_back (SeparatorElem());
2024 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
2025 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
2026 select_items.push_back (MenuElem (_("Set Range to Selected Regions"), sigc::mem_fun(*this, &Editor::set_selection_from_region)));
2027 select_items.push_back (SeparatorElem());
2028 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true, true)));
2029 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false, true)));
2030 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2031 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2032 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
2033 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
2034 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
2036 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2040 Menu *cutnpaste_menu = manage (new Menu);
2041 MenuList& cutnpaste_items = cutnpaste_menu->items();
2042 cutnpaste_menu->set_name ("ArdourContextMenu");
2044 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2045 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2046 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f, true)));
2048 cutnpaste_items.push_back (SeparatorElem());
2050 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
2051 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
2053 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
2055 /* Adding new material */
2057 edit_items.push_back (SeparatorElem());
2058 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
2059 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
2063 Menu *nudge_menu = manage (new Menu());
2064 MenuList& nudge_items = nudge_menu->items();
2065 nudge_menu->set_name ("ArdourContextMenu");
2067 edit_items.push_back (SeparatorElem());
2068 nudge_items.push_back (MenuElem (_("Nudge Entire Track Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2069 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2070 nudge_items.push_back (MenuElem (_("Nudge Entire Track Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2071 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2073 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2077 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
2079 using namespace Menu_Helpers;
2083 Menu *play_menu = manage (new Menu);
2084 MenuList& play_items = play_menu->items();
2085 play_menu->set_name ("ArdourContextMenu");
2087 play_items.push_back (MenuElem (_("Play from Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
2088 play_items.push_back (MenuElem (_("Play from Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
2089 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2093 Menu *select_menu = manage (new Menu);
2094 MenuList& select_items = select_menu->items();
2095 select_menu->set_name ("ArdourContextMenu");
2097 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2098 select_items.push_back (MenuElem (_("Select All Objects"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_objects), Selection::Set)));
2099 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2100 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2101 select_items.push_back (SeparatorElem());
2102 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true, true)));
2103 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false, true)));
2104 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2105 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2107 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2111 Menu *cutnpaste_menu = manage (new Menu);
2112 MenuList& cutnpaste_items = cutnpaste_menu->items();
2113 cutnpaste_menu->set_name ("ArdourContextMenu");
2115 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2116 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2117 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f, true)));
2119 Menu *nudge_menu = manage (new Menu());
2120 MenuList& nudge_items = nudge_menu->items();
2121 nudge_menu->set_name ("ArdourContextMenu");
2123 edit_items.push_back (SeparatorElem());
2124 nudge_items.push_back (MenuElem (_("Nudge Entire Track Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2125 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2126 nudge_items.push_back (MenuElem (_("Nudge Entire Track Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2127 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2129 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2133 Editor::grid_type() const
2139 Editor::grid_musical() const
2141 switch (_grid_type) {
2142 case GridTypeBeatDiv32:
2143 case GridTypeBeatDiv28:
2144 case GridTypeBeatDiv24:
2145 case GridTypeBeatDiv20:
2146 case GridTypeBeatDiv16:
2147 case GridTypeBeatDiv14:
2148 case GridTypeBeatDiv12:
2149 case GridTypeBeatDiv10:
2150 case GridTypeBeatDiv8:
2151 case GridTypeBeatDiv7:
2152 case GridTypeBeatDiv6:
2153 case GridTypeBeatDiv5:
2154 case GridTypeBeatDiv4:
2155 case GridTypeBeatDiv3:
2156 case GridTypeBeatDiv2:
2161 case GridTypeTimecode:
2162 case GridTypeMinSec:
2163 case GridTypeCDFrame:
2170 Editor::grid_nonmusical() const
2172 switch (_grid_type) {
2173 case GridTypeTimecode:
2174 case GridTypeMinSec:
2175 case GridTypeCDFrame:
2177 case GridTypeBeatDiv32:
2178 case GridTypeBeatDiv28:
2179 case GridTypeBeatDiv24:
2180 case GridTypeBeatDiv20:
2181 case GridTypeBeatDiv16:
2182 case GridTypeBeatDiv14:
2183 case GridTypeBeatDiv12:
2184 case GridTypeBeatDiv10:
2185 case GridTypeBeatDiv8:
2186 case GridTypeBeatDiv7:
2187 case GridTypeBeatDiv6:
2188 case GridTypeBeatDiv5:
2189 case GridTypeBeatDiv4:
2190 case GridTypeBeatDiv3:
2191 case GridTypeBeatDiv2:
2200 Editor::snap_mode() const
2206 Editor::show_rulers_for_grid ()
2208 /* show appropriate rulers for this grid setting. */
2209 if (grid_musical()) {
2210 ruler_tempo_action->set_active(true);
2211 ruler_meter_action->set_active(true);
2212 ruler_bbt_action->set_active(true);
2214 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2215 ruler_timecode_action->set_active(false);
2216 ruler_minsec_action->set_active(false);
2217 ruler_samples_action->set_active(false);
2219 } else if (_grid_type == GridTypeTimecode) {
2220 ruler_timecode_action->set_active(true);
2222 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2223 ruler_tempo_action->set_active(false);
2224 ruler_meter_action->set_active(false);
2225 ruler_bbt_action->set_active(false);
2226 ruler_minsec_action->set_active(false);
2227 ruler_samples_action->set_active(false);
2229 } else if (_grid_type == GridTypeMinSec) {
2230 ruler_minsec_action->set_active(true);
2232 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2233 ruler_tempo_action->set_active(false);
2234 ruler_meter_action->set_active(false);
2235 ruler_bbt_action->set_active(false);
2236 ruler_timecode_action->set_active(false);
2237 ruler_samples_action->set_active(false);
2239 } else if (_grid_type == GridTypeCDFrame) {
2240 ruler_cd_marker_action->set_active(true);
2241 ruler_minsec_action->set_active(true);
2243 if (UIConfiguration::instance().get_rulers_follow_grid()) {
2244 ruler_tempo_action->set_active(false);
2245 ruler_meter_action->set_active(false);
2246 ruler_bbt_action->set_active(false);
2247 ruler_timecode_action->set_active(false);
2248 ruler_samples_action->set_active(false);
2254 Editor::set_grid_to (GridType gt)
2256 if (_grid_type == gt) { // already set
2260 unsigned int grid_ind = (unsigned int)gt;
2262 if (internal_editing() && UIConfiguration::instance().get_grid_follows_internal()) {
2263 internal_grid_type = gt;
2265 pre_internal_grid_type = gt;
2270 if (grid_ind > grid_type_strings.size() - 1) {
2272 _grid_type = (GridType)grid_ind;
2275 string str = grid_type_strings[grid_ind];
2277 if (str != grid_type_selector.get_text()) {
2278 grid_type_selector.set_text (str);
2281 if (UIConfiguration::instance().get_show_grids_ruler()) {
2282 show_rulers_for_grid ();
2287 if (grid_musical()) {
2288 compute_bbt_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
2289 update_tempo_based_rulers ();
2292 mark_region_boundary_cache_dirty ();
2294 redisplay_grid (false);
2296 SnapChanged (); /* EMIT SIGNAL */
2300 Editor::set_snap_mode (SnapMode mode)
2302 if (internal_editing()) {
2303 internal_snap_mode = mode;
2305 pre_internal_snap_mode = mode;
2310 if (_snap_mode == SnapOff) {
2311 snap_mode_button.set_active_state (Gtkmm2ext::Off);
2313 snap_mode_button.set_active_state (Gtkmm2ext::ExplicitActive);
2320 Editor::set_edit_point_preference (EditPoint ep, bool force)
2322 bool changed = (_edit_point != ep);
2325 if (Profile->get_mixbus())
2326 if (ep == EditAtSelectedMarker)
2327 ep = EditAtPlayhead;
2329 string str = edit_point_strings[(int)ep];
2330 if (str != edit_point_selector.get_text ()) {
2331 edit_point_selector.set_text (str);
2334 update_all_enter_cursors();
2336 if (!force && !changed) {
2340 const char* action=NULL;
2342 switch (_edit_point) {
2343 case EditAtPlayhead:
2344 action = "edit-at-playhead";
2346 case EditAtSelectedMarker:
2347 action = "edit-at-marker";
2350 action = "edit-at-mouse";
2354 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2356 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2360 bool in_track_canvas;
2362 if (!mouse_sample (foo, in_track_canvas)) {
2363 in_track_canvas = false;
2366 reset_canvas_action_sensitivity (in_track_canvas);
2367 sensitize_the_right_region_actions (false);
2373 Editor::set_state (const XMLNode& node, int version)
2376 PBD::Unwinder<bool> nsi (no_save_instant, true);
2379 Tabbable::set_state (node, version);
2382 if (_session && node.get_property ("playhead", ph_pos)) {
2384 playhead_cursor->set_position (ph_pos);
2386 warning << _("Playhead position stored with a negative value - ignored (use zero instead)") << endmsg;
2387 playhead_cursor->set_position (0);
2390 playhead_cursor->set_position (0);
2393 node.get_property ("mixer-width", editor_mixer_strip_width);
2395 node.get_property ("zoom-focus", zoom_focus);
2396 zoom_focus_selection_done (zoom_focus);
2399 if (node.get_property ("zoom", z)) {
2400 /* older versions of ardour used floating point samples_per_pixel */
2401 reset_zoom (llrintf (z));
2403 reset_zoom (samples_per_pixel);
2407 if (node.get_property ("visible-track-count", cnt)) {
2408 set_visible_track_count (cnt);
2412 if (!node.get_property ("grid-type", grid_type)) {
2413 grid_type = _grid_type;
2415 set_grid_to (grid_type);
2418 if (node.get_property ("snap-mode", sm)) {
2419 snap_mode_selection_done(sm);
2420 /* set text of Dropdown. in case _snap_mode == SnapOff (default)
2421 * snap_mode_selection_done() will only mark an already active item as active
2422 * which does not trigger set_text().
2426 set_snap_mode (_snap_mode);
2429 node.get_property ("internal-grid-type", internal_grid_type);
2430 node.get_property ("internal-snap-mode", internal_snap_mode);
2431 node.get_property ("pre-internal-grid-type", pre_internal_grid_type);
2432 node.get_property ("pre-internal-snap-mode", pre_internal_snap_mode);
2435 if (node.get_property ("mouse-mode", mm_str)) {
2436 MouseMode m = str2mousemode(mm_str);
2437 set_mouse_mode (m, true);
2439 set_mouse_mode (MouseObject, true);
2443 if (node.get_property ("left-frame", lf_pos)) {
2447 reset_x_origin (lf_pos);
2451 if (node.get_property ("y-origin", y_origin)) {
2452 reset_y_origin (y_origin);
2455 if (node.get_property ("join-object-range", yn)) {
2456 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object-range"));
2458 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2459 tact->set_active (!yn);
2460 tact->set_active (yn);
2462 set_mouse_mode(mouse_mode, true);
2466 if (node.get_property ("edit-point", ep)) {
2467 set_edit_point_preference (ep, true);
2469 set_edit_point_preference (_edit_point);
2472 if (node.get_property ("follow-playhead", yn)) {
2473 set_follow_playhead (yn);
2476 if (node.get_property ("stationary-playhead", yn)) {
2477 set_stationary_playhead (yn);
2480 RegionListSortType sort_type;
2481 if (node.get_property ("region-list-sort-type", sort_type)) {
2482 _regions->reset_sort_type (sort_type, true);
2485 if (node.get_property ("show-editor-mixer", yn)) {
2487 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2490 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2492 /* do it twice to force the change */
2494 tact->set_active (!yn);
2495 tact->set_active (yn);
2498 if (node.get_property ("show-editor-list", yn)) {
2500 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2503 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2505 /* do it twice to force the change */
2507 tact->set_active (!yn);
2508 tact->set_active (yn);
2512 if (node.get_property (X_("editor-list-page"), el_page)) {
2513 _the_notebook.set_current_page (el_page);
2516 if (node.get_property (X_("show-marker-lines"), yn)) {
2517 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2519 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2521 tact->set_active (!yn);
2522 tact->set_active (yn);
2525 XMLNodeList children = node.children ();
2526 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2527 selection->set_state (**i, Stateful::current_state_version);
2528 _regions->set_state (**i);
2529 _locations->set_state (**i);
2532 if (node.get_property ("maximised", yn)) {
2533 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMaximalEditor"));
2535 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2536 bool fs = tact && tact->get_active();
2538 ActionManager::do_action ("Common", "ToggleMaximalEditor");
2542 samplepos_t nudge_clock_value;
2543 if (node.get_property ("nudge-clock-value", nudge_clock_value)) {
2544 nudge_clock->set (nudge_clock_value);
2546 nudge_clock->set_mode (AudioClock::Timecode);
2547 nudge_clock->set (_session->sample_rate() * 5, true);
2552 * Not all properties may have been in XML, but
2553 * those that are linked to a private variable may need changing
2557 act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2558 yn = _follow_playhead;
2560 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2561 if (tact->get_active() != yn) {
2562 tact->set_active (yn);
2566 act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2567 yn = _stationary_playhead;
2569 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2570 if (tact->get_active() != yn) {
2571 tact->set_active (yn);
2580 Editor::get_state ()
2582 XMLNode* node = new XMLNode (X_("Editor"));
2584 node->set_property ("id", id().to_s ());
2586 node->add_child_nocopy (Tabbable::get_state());
2588 node->set_property("edit-horizontal-pane-pos", edit_pane.get_divider ());
2589 node->set_property("notebook-shrunk", _notebook_shrunk);
2590 node->set_property("edit-vertical-pane-pos", editor_summary_pane.get_divider());
2592 maybe_add_mixer_strip_width (*node);
2594 node->set_property ("zoom-focus", zoom_focus);
2596 node->set_property ("zoom", samples_per_pixel);
2597 node->set_property ("grid-type", _grid_type);
2598 node->set_property ("snap-mode", _snap_mode);
2599 node->set_property ("internal-grid-type", internal_grid_type);
2600 node->set_property ("internal-snap-mode", internal_snap_mode);
2601 node->set_property ("pre-internal-grid-type", pre_internal_grid_type);
2602 node->set_property ("pre-internal-snap-mode", pre_internal_snap_mode);
2603 node->set_property ("edit-point", _edit_point);
2604 node->set_property ("visible-track-count", _visible_track_count);
2606 node->set_property ("playhead", playhead_cursor->current_sample ());
2607 node->set_property ("left-frame", _leftmost_sample);
2608 node->set_property ("y-origin", vertical_adjustment.get_value ());
2610 node->set_property ("maximised", _maximised);
2611 node->set_property ("follow-playhead", _follow_playhead);
2612 node->set_property ("stationary-playhead", _stationary_playhead);
2613 node->set_property ("region-list-sort-type", _regions->sort_type ());
2614 node->set_property ("mouse-mode", mouse_mode);
2615 node->set_property ("join-object-range", smart_mode_action->get_active ());
2617 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2619 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2620 node->set_property (X_("show-editor-mixer"), tact->get_active());
2623 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2625 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2626 node->set_property (X_("show-editor-list"), tact->get_active());
2629 node->set_property (X_("editor-list-page"), _the_notebook.get_current_page ());
2631 if (button_bindings) {
2632 XMLNode* bb = new XMLNode (X_("Buttons"));
2633 button_bindings->save (*bb);
2634 node->add_child_nocopy (*bb);
2637 node->set_property (X_("show-marker-lines"), _show_marker_lines);
2639 node->add_child_nocopy (selection->get_state ());
2640 node->add_child_nocopy (_regions->get_state ());
2642 node->set_property ("nudge-clock-value", nudge_clock->current_duration());
2644 node->add_child_nocopy (_locations->get_state ());
2649 /** if @param trackview_relative_offset is true, @param y y is an offset into the trackview area, in pixel units
2650 * if @param trackview_relative_offset is false, @param y y is a global canvas * coordinate, in pixel units
2652 * @return pair: TimeAxisView that y is over, layer index.
2654 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2655 * in stacked or expanded region display mode, otherwise 0.
2657 std::pair<TimeAxisView *, double>
2658 Editor::trackview_by_y_position (double y, bool trackview_relative_offset) const
2660 if (!trackview_relative_offset) {
2661 y -= _trackview_group->canvas_origin().y;
2665 return std::make_pair ((TimeAxisView *) 0, 0);
2668 for (TrackViewList::const_iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2670 std::pair<TimeAxisView*, double> const r = (*iter)->covers_y_position (y);
2677 return std::make_pair ((TimeAxisView *) 0, 0);
2681 Editor::set_snapped_cursor_position (samplepos_t pos)
2683 if (_edit_point == EditAtMouse) {
2684 snapped_cursor->set_position(pos);
2689 /** Snap a position to the grid, if appropriate, taking into account current
2690 * grid settings and also the state of any snap modifier keys that may be pressed.
2691 * @param start Position to snap.
2692 * @param event Event to get current key modifier information from, or 0.
2695 Editor::snap_to_with_modifier (MusicSample& start, GdkEvent const * event, RoundMode direction, SnapPref pref)
2697 if (!_session || !event) {
2701 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2702 if (_snap_mode == SnapOff) {
2703 snap_to_internal (start, direction, pref);
2705 start.set (start.sample, 0);
2708 if (_snap_mode != SnapOff) {
2709 snap_to_internal (start, direction, pref);
2710 } else if (ArdourKeyboard::indicates_snap_delta (event->button.state)) {
2711 /* SnapOff, but we pressed the snap_delta modifier */
2712 snap_to_internal (start, direction, pref);
2714 start.set (start.sample, 0);
2720 Editor::snap_to (MusicSample& start, RoundMode direction, SnapPref pref, bool ensure_snap)
2722 if (!_session || (_snap_mode == SnapOff && !ensure_snap)) {
2723 start.set (start.sample, 0);
2727 snap_to_internal (start, direction, pref, ensure_snap);
2731 check_best_snap (samplepos_t presnap, samplepos_t &test, samplepos_t &dist, samplepos_t &best)
2733 samplepos_t diff = abs (test - presnap);
2739 test = max_samplepos; // reset this so it doesn't get accidentally reused
2743 Editor::snap_to_timecode (MusicSample presnap, RoundMode direction, SnapPref gpref)
2745 samplepos_t start = presnap.sample;
2746 const samplepos_t one_timecode_second = (samplepos_t)(rint(_session->timecode_frames_per_second()) * _session->samples_per_timecode_frame());
2747 samplepos_t one_timecode_minute = (samplepos_t)(rint(_session->timecode_frames_per_second()) * _session->samples_per_timecode_frame() * 60);
2749 TimecodeRulerScale scale = (gpref != SnapToGrid_Unscaled) ? timecode_ruler_scale : timecode_show_samples;
2752 case timecode_show_bits:
2753 case timecode_show_samples:
2754 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2755 fmod((double)start, (double)_session->samples_per_timecode_frame()) == 0) {
2756 /* start is already on a whole timecode frame, do nothing */
2757 } else if (((direction == 0) && (fmod((double)start, (double)_session->samples_per_timecode_frame()) > (_session->samples_per_timecode_frame() / 2))) || (direction > 0)) {
2758 start = (samplepos_t) (ceil ((double) start / _session->samples_per_timecode_frame()) * _session->samples_per_timecode_frame());
2760 start = (samplepos_t) (floor ((double) start / _session->samples_per_timecode_frame()) * _session->samples_per_timecode_frame());
2764 case timecode_show_seconds:
2765 if (_session->config.get_timecode_offset_negative()) {
2766 start += _session->config.get_timecode_offset ();
2768 start -= _session->config.get_timecode_offset ();
2770 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2771 (start % one_timecode_second == 0)) {
2772 /* start is already on a whole second, do nothing */
2773 } else if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2774 start = (samplepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2776 start = (samplepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2779 if (_session->config.get_timecode_offset_negative()) {
2780 start -= _session->config.get_timecode_offset ();
2782 start += _session->config.get_timecode_offset ();
2786 case timecode_show_minutes:
2787 case timecode_show_hours:
2788 case timecode_show_many_hours:
2789 if (_session->config.get_timecode_offset_negative()) {
2790 start += _session->config.get_timecode_offset ();
2792 start -= _session->config.get_timecode_offset ();
2794 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2795 (start % one_timecode_minute == 0)) {
2796 /* start is already on a whole minute, do nothing */
2797 } else if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2798 start = (samplepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2800 start = (samplepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2802 if (_session->config.get_timecode_offset_negative()) {
2803 start -= _session->config.get_timecode_offset ();
2805 start += _session->config.get_timecode_offset ();
2809 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2812 MusicSample ret(start,0);
2817 Editor::snap_to_minsec (MusicSample presnap, RoundMode direction, SnapPref gpref)
2819 MusicSample ret(presnap);
2821 const samplepos_t one_second = _session->sample_rate();
2822 const samplepos_t one_minute = one_second * 60;
2823 const samplepos_t one_hour = one_minute * 60;
2825 MinsecRulerScale scale = (gpref != SnapToGrid_Unscaled) ? minsec_ruler_scale : minsec_show_seconds;
2828 case minsec_show_msecs:
2829 case minsec_show_seconds: {
2830 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2831 presnap.sample % one_second == 0) {
2832 /* start is already on a whole second, do nothing */
2833 } else if (((direction == 0) && (presnap.sample % one_second > one_second / 2)) || (direction > 0)) {
2834 ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_second) * one_second;
2836 ret.sample = (samplepos_t) floor ((double) presnap.sample / one_second) * one_second;
2840 case minsec_show_minutes: {
2841 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2842 presnap.sample % one_minute == 0) {
2843 /* start is already on a whole minute, do nothing */
2844 } else if (((direction == 0) && (presnap.sample % one_minute > one_minute / 2)) || (direction > 0)) {
2845 ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_minute) * one_minute;
2847 ret.sample = (samplepos_t) floor ((double) presnap.sample / one_minute) * one_minute;
2852 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2853 presnap.sample % one_hour == 0) {
2854 /* start is already on a whole hour, do nothing */
2855 } else if (((direction == 0) && (presnap.sample % one_hour > one_hour / 2)) || (direction > 0)) {
2856 ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_hour) * one_hour;
2858 ret.sample = (samplepos_t) floor ((double) presnap.sample / one_hour) * one_hour;
2867 Editor::snap_to_cd_frames (MusicSample presnap, RoundMode direction, SnapPref gpref)
2869 if ((gpref != SnapToGrid_Unscaled) && (minsec_ruler_scale != minsec_show_msecs)) {
2870 return snap_to_minsec (presnap, direction, gpref);
2873 const samplepos_t one_second = _session->sample_rate();
2875 MusicSample ret(presnap);
2877 if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
2878 presnap.sample % (one_second/75) == 0) {
2879 /* start is already on a whole CD sample, do nothing */
2880 } else if (((direction == 0) && (presnap.sample % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2881 ret.sample = (samplepos_t) ceil ((double) presnap.sample / (one_second / 75)) * (one_second / 75);
2883 ret.sample = (samplepos_t) floor ((double) presnap.sample / (one_second / 75)) * (one_second / 75);
2890 Editor::snap_to_bbt (MusicSample presnap, RoundMode direction, SnapPref gpref)
2892 MusicSample ret(presnap);
2894 if (gpref != SnapToGrid_Unscaled) { // use the visual grid lines which are limited by the zoom scale that the user selected
2897 switch (_grid_type) {
2898 case GridTypeBeatDiv3:
2899 case GridTypeBeatDiv6:
2900 case GridTypeBeatDiv12:
2901 case GridTypeBeatDiv24:
2904 case GridTypeBeatDiv5:
2905 case GridTypeBeatDiv10:
2906 case GridTypeBeatDiv20:
2909 case GridTypeBeatDiv7:
2910 case GridTypeBeatDiv14:
2911 case GridTypeBeatDiv28:
2918 BBTRulerScale scale = bbt_ruler_scale;
2925 ret = _session->tempo_map().round_to_bar (presnap.sample, direction);
2927 case bbt_show_quarters:
2928 ret = _session->tempo_map().round_to_beat (presnap.sample, direction);
2930 case bbt_show_eighths:
2931 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 1 * divisor, direction);
2933 case bbt_show_sixteenths:
2934 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 2 * divisor, direction);
2936 case bbt_show_thirtyseconds:
2937 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 4 * divisor, direction);
2941 ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, get_grid_beat_divisions(_grid_type), direction);
2948 Editor::snap_to_grid (MusicSample presnap, RoundMode direction, SnapPref gpref)
2950 MusicSample ret(presnap);
2952 if (grid_musical()) {
2953 ret = snap_to_bbt (presnap, direction, gpref);
2956 switch (_grid_type) {
2957 case GridTypeTimecode:
2958 ret = snap_to_timecode(presnap, direction, gpref);
2960 case GridTypeMinSec:
2961 ret = snap_to_minsec(presnap, direction, gpref);
2963 case GridTypeCDFrame:
2964 ret = snap_to_cd_frames(presnap, direction, gpref);
2974 Editor::snap_to_marker (samplepos_t presnap, RoundMode direction)
2980 _session->locations()->marks_either_side (presnap, before, after);
2982 if (before == max_samplepos && after == max_samplepos) {
2983 /* No marks to snap to, so just don't snap */
2985 } else if (before == max_samplepos) {
2987 } else if (after == max_samplepos) {
2990 if ((direction == RoundUpMaybe || direction == RoundUpAlways)) {
2992 } else if ((direction == RoundDownMaybe || direction == RoundDownAlways)) {
2994 } else if (direction == 0) {
2995 if ((presnap - before) < (after - presnap)) {
3007 Editor::snap_to_internal (MusicSample& start, RoundMode direction, SnapPref pref, bool ensure_snap)
3009 const samplepos_t presnap = start.sample;
3011 samplepos_t test = max_samplepos; // for each snap, we'll use this value
3012 samplepos_t dist = max_samplepos; // this records the distance of the best snap result we've found so far
3013 samplepos_t best = max_samplepos; // this records the best snap-result we've found so far
3015 /* check snap-to-marker */
3016 if ((pref == SnapToAny_Visual) && UIConfiguration::instance().get_snap_to_marks()) {
3017 test = snap_to_marker (presnap, direction);
3018 check_best_snap(presnap, test, dist, best);
3021 /* check snap-to-region-{start/end/sync} */
3023 (pref == SnapToAny_Visual) &&
3024 (UIConfiguration::instance().get_snap_to_region_start() || UIConfiguration::instance().get_snap_to_region_end() || UIConfiguration::instance().get_snap_to_region_sync())
3026 if (!region_boundary_cache.empty()) {
3028 vector<samplepos_t>::iterator prev = region_boundary_cache.begin();
3029 vector<samplepos_t>::iterator next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), presnap);
3030 if (next != region_boundary_cache.begin ()) {
3035 if ((direction == RoundUpMaybe || direction == RoundUpAlways))
3037 else if ((direction == RoundDownMaybe || direction == RoundDownAlways))
3039 else if (direction == 0) {
3040 if ((presnap - *prev) < (*next - presnap)) {
3049 check_best_snap(presnap, test, dist, best);
3053 if (UIConfiguration::instance().get_snap_to_grid() && (_grid_type != GridTypeNone)) {
3054 MusicSample pre(presnap, 0);
3055 MusicSample post = snap_to_grid (pre, direction, pref);
3056 check_best_snap(presnap, post.sample, dist, best);
3059 /* now check "magnetic" state: is the grid within reasonable on-screen distance to trigger a snap?
3060 * this also helps to avoid snapping to somewhere the user can't see. (i.e.: I clicked on a region and it disappeared!!)
3061 * ToDo: Perhaps this should only occur if EditPointMouse?
3063 int snap_threshold_s = pixel_to_sample(UIConfiguration::instance().get_snap_threshold());
3065 start.set (best, 0);
3067 } else if (presnap > best) {
3068 if (presnap > (best+ snap_threshold_s)) {
3071 } else if (presnap < best) {
3072 if (presnap < (best - snap_threshold_s)) {
3077 start.set (best, 0);
3082 Editor::setup_toolbar ()
3084 HBox* mode_box = manage(new HBox);
3085 mode_box->set_border_width (2);
3086 mode_box->set_spacing(2);
3088 HBox* mouse_mode_box = manage (new HBox);
3089 HBox* mouse_mode_hbox = manage (new HBox);
3090 VBox* mouse_mode_vbox = manage (new VBox);
3091 Alignment* mouse_mode_align = manage (new Alignment);
3093 Glib::RefPtr<SizeGroup> mouse_mode_size_group = SizeGroup::create (SIZE_GROUP_VERTICAL);
3094 mouse_mode_size_group->add_widget (smart_mode_button);
3095 mouse_mode_size_group->add_widget (mouse_move_button);
3096 mouse_mode_size_group->add_widget (mouse_cut_button);
3097 mouse_mode_size_group->add_widget (mouse_select_button);
3098 mouse_mode_size_group->add_widget (mouse_timefx_button);
3099 mouse_mode_size_group->add_widget (mouse_audition_button);
3100 mouse_mode_size_group->add_widget (mouse_draw_button);
3101 mouse_mode_size_group->add_widget (mouse_content_button);
3103 if (!Profile->get_mixbus()) {
3104 mouse_mode_size_group->add_widget (zoom_in_button);
3105 mouse_mode_size_group->add_widget (zoom_out_button);
3106 mouse_mode_size_group->add_widget (zoom_out_full_button);
3107 mouse_mode_size_group->add_widget (zoom_focus_selector);
3108 mouse_mode_size_group->add_widget (tav_shrink_button);
3109 mouse_mode_size_group->add_widget (tav_expand_button);
3111 mouse_mode_size_group->add_widget (zoom_preset_selector);
3112 mouse_mode_size_group->add_widget (visible_tracks_selector);
3115 mouse_mode_size_group->add_widget (grid_type_selector);
3116 mouse_mode_size_group->add_widget (snap_mode_button);
3118 mouse_mode_size_group->add_widget (edit_point_selector);
3119 mouse_mode_size_group->add_widget (edit_mode_selector);
3121 mouse_mode_size_group->add_widget (*nudge_clock);
3122 mouse_mode_size_group->add_widget (nudge_forward_button);
3123 mouse_mode_size_group->add_widget (nudge_backward_button);
3125 mouse_mode_hbox->set_spacing (2);
3127 if (!ARDOUR::Profile->get_trx()) {
3128 mouse_mode_hbox->pack_start (smart_mode_button, false, false);
3131 mouse_mode_hbox->pack_start (mouse_move_button, false, false);
3132 mouse_mode_hbox->pack_start (mouse_select_button, false, false);
3134 if (!ARDOUR::Profile->get_mixbus()) {
3135 mouse_mode_hbox->pack_start (mouse_cut_button, false, false);
3136 mouse_mode_hbox->pack_start (mouse_audition_button, false, false);
3139 if (!ARDOUR::Profile->get_trx()) {
3140 mouse_mode_hbox->pack_start (mouse_timefx_button, false, false);
3141 mouse_mode_hbox->pack_start (mouse_draw_button, false, false);
3142 mouse_mode_hbox->pack_start (mouse_content_button, false, false);
3145 mouse_mode_vbox->pack_start (*mouse_mode_hbox);
3147 mouse_mode_align->add (*mouse_mode_vbox);
3148 mouse_mode_align->set (0.5, 1.0, 0.0, 0.0);
3150 mouse_mode_box->pack_start (*mouse_mode_align, false, false);
3152 edit_mode_selector.set_name ("mouse mode button");
3154 if (!ARDOUR::Profile->get_trx()) {
3155 mode_box->pack_start (edit_mode_selector, false, false);
3156 mode_box->pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3157 mode_box->pack_start (edit_point_selector, false, false);
3158 mode_box->pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3161 mode_box->pack_start (*mouse_mode_box, false, false);
3165 _zoom_box.set_spacing (2);
3166 _zoom_box.set_border_width (2);
3170 zoom_preset_selector.set_name ("zoom button");
3171 zoom_preset_selector.set_icon (ArdourIcon::ZoomExpand);
3173 zoom_in_button.set_name ("zoom button");
3174 zoom_in_button.set_icon (ArdourIcon::ZoomIn);
3175 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-in"));
3176 zoom_in_button.set_related_action (act);
3178 zoom_out_button.set_name ("zoom button");
3179 zoom_out_button.set_icon (ArdourIcon::ZoomOut);
3180 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-out"));
3181 zoom_out_button.set_related_action (act);
3183 zoom_out_full_button.set_name ("zoom button");
3184 zoom_out_full_button.set_icon (ArdourIcon::ZoomFull);
3185 act = ActionManager::get_action (X_("Editor"), X_("zoom-to-session"));
3186 zoom_out_full_button.set_related_action (act);
3188 zoom_focus_selector.set_name ("zoom button");
3190 if (ARDOUR::Profile->get_mixbus()) {
3191 _zoom_box.pack_start (zoom_preset_selector, false, false);
3192 } else if (ARDOUR::Profile->get_trx()) {
3193 mode_box->pack_start (zoom_out_button, false, false);
3194 mode_box->pack_start (zoom_in_button, false, false);
3196 _zoom_box.pack_start (zoom_out_button, false, false);
3197 _zoom_box.pack_start (zoom_in_button, false, false);
3198 _zoom_box.pack_start (zoom_out_full_button, false, false);
3199 _zoom_box.pack_start (zoom_focus_selector, false, false);
3202 /* Track zoom buttons */
3203 _track_box.set_spacing (2);
3204 _track_box.set_border_width (2);
3206 visible_tracks_selector.set_name ("zoom button");
3207 if (Profile->get_mixbus()) {
3208 visible_tracks_selector.set_icon (ArdourIcon::TimeAxisExpand);
3210 set_size_request_to_display_given_text (visible_tracks_selector, _("All"), 30, 2);
3213 tav_expand_button.set_name ("zoom button");
3214 tav_expand_button.set_icon (ArdourIcon::TimeAxisExpand);
3215 act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
3216 tav_expand_button.set_related_action (act);
3218 tav_shrink_button.set_name ("zoom button");
3219 tav_shrink_button.set_icon (ArdourIcon::TimeAxisShrink);
3220 act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
3221 tav_shrink_button.set_related_action (act);
3223 if (ARDOUR::Profile->get_mixbus()) {
3224 _track_box.pack_start (visible_tracks_selector);
3225 } else if (ARDOUR::Profile->get_trx()) {
3226 _track_box.pack_start (tav_shrink_button);
3227 _track_box.pack_start (tav_expand_button);
3229 _track_box.pack_start (visible_tracks_selector);
3230 _track_box.pack_start (tav_shrink_button);
3231 _track_box.pack_start (tav_expand_button);
3234 snap_box.set_spacing (2);
3235 snap_box.set_border_width (2);
3237 grid_type_selector.set_name ("mouse mode button");
3239 snap_mode_button.set_name ("mouse mode button");
3241 edit_point_selector.set_name ("mouse mode button");
3243 snap_box.pack_start (snap_mode_button, false, false);
3244 snap_box.pack_start (grid_type_selector, false, false);
3248 HBox *nudge_box = manage (new HBox);
3249 nudge_box->set_spacing (2);
3250 nudge_box->set_border_width (2);
3252 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
3253 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
3255 nudge_box->pack_start (nudge_backward_button, false, false);
3256 nudge_box->pack_start (nudge_forward_button, false, false);
3257 nudge_box->pack_start (*nudge_clock, false, false);
3260 /* Pack everything in... */
3262 toolbar_hbox.set_spacing (2);
3263 toolbar_hbox.set_border_width (2);
3265 ArdourWidgets::ArdourDropShadow *tool_shadow = manage (new (ArdourWidgets::ArdourDropShadow));
3266 tool_shadow->set_size_request (4, -1);
3267 tool_shadow->show();
3269 ebox_hpacker.pack_start (*tool_shadow, false, false);
3270 ebox_hpacker.pack_start(ebox_vpacker, true, true);
3272 Gtk::EventBox* spacer = manage (new Gtk::EventBox); // extra space under the mouse toolbar, for aesthetics
3273 spacer->set_name("EditorWindow");
3274 spacer->set_size_request(-1,4);
3277 ebox_vpacker.pack_start(toolbar_hbox, false, false);
3278 ebox_vpacker.pack_start(*spacer, false, false);
3279 ebox_vpacker.show();
3281 toolbar_hbox.pack_start (*mode_box, false, false);
3283 if (!ARDOUR::Profile->get_trx()) {
3285 toolbar_hbox.pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3287 toolbar_hbox.pack_start (snap_box, false, false);
3289 toolbar_hbox.pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
3291 toolbar_hbox.pack_start (*nudge_box, false, false);
3293 toolbar_hbox.pack_end (_zoom_box, false, false, 2);
3295 toolbar_hbox.pack_end (*(manage (new ArdourVSpacer ())), false, false, 3);
3297 toolbar_hbox.pack_end (_track_box, false, false);
3301 toolbar_hbox.show_all ();
3305 Editor::build_edit_point_menu ()
3307 using namespace Menu_Helpers;
3309 edit_point_selector.AddMenuElem (MenuElem (edit_point_strings[(int)EditAtPlayhead], sigc::bind (sigc::mem_fun(*this, &Editor::edit_point_selection_done), (EditPoint) EditAtPlayhead)));
3310 if(!Profile->get_mixbus())
3311 edit_point_selector.AddMenuElem (MenuElem (edit_point_strings[(int)EditAtSelectedMarker], sigc::bind (sigc::mem_fun(*this, &Editor::edit_point_selection_done), (EditPoint) EditAtSelectedMarker)));
3312 edit_point_selector.AddMenuElem (MenuElem (edit_point_strings[(int)EditAtMouse], sigc::bind (sigc::mem_fun(*this, &Editor::edit_point_selection_done), (EditPoint) EditAtMouse)));
3314 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_TRIANGLE_WIDTH, 2);
3318 Editor::build_edit_mode_menu ()
3320 using namespace Menu_Helpers;
3322 edit_mode_selector.AddMenuElem (MenuElem (edit_mode_strings[(int)Slide], sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Slide)));
3323 edit_mode_selector.AddMenuElem (MenuElem (edit_mode_strings[(int)Ripple], sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Ripple)));
3324 edit_mode_selector.AddMenuElem (MenuElem (edit_mode_strings[(int)Lock], sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Lock)));
3325 /* Note: Splice was removed */
3327 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_TRIANGLE_WIDTH, 2);
3331 Editor::build_grid_type_menu ()
3333 using namespace Menu_Helpers;
3335 /* main grid: bars, quarter-notes, etc */
3336 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeNone], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeNone)));
3337 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBar], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBar)));
3338 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeat], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeat)));
3339 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv2], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv2)));
3340 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv4], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv4)));
3341 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv8], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv8)));
3342 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv16], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv16)));
3343 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv32], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv32)));
3346 grid_type_selector.AddMenuElem(SeparatorElem());
3347 Gtk::Menu *_triplet_menu = manage (new Menu);
3348 MenuList& triplet_items (_triplet_menu->items());
3350 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv3], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv3)));
3351 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv6], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv6)));
3352 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv12], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv12)));
3353 triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv24], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv24)));
3355 grid_type_selector.AddMenuElem (Menu_Helpers::MenuElem (_("Triplets"), *_triplet_menu));
3357 /* quintuplet grid */
3358 Gtk::Menu *_quintuplet_menu = manage (new Menu);
3359 MenuList& quintuplet_items (_quintuplet_menu->items());
3361 quintuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv5], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv5)));
3362 quintuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv10], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv10)));
3363 quintuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv20], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv20)));
3365 grid_type_selector.AddMenuElem (Menu_Helpers::MenuElem (_("Quintuplets"), *_quintuplet_menu));
3367 /* septuplet grid */
3368 Gtk::Menu *_septuplet_menu = manage (new Menu);
3369 MenuList& septuplet_items (_septuplet_menu->items());
3371 septuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv7], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv7)));
3372 septuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv14], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv14)));
3373 septuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv28], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv28)));
3375 grid_type_selector.AddMenuElem (Menu_Helpers::MenuElem (_("Septuplets"), *_septuplet_menu));
3377 grid_type_selector.AddMenuElem(SeparatorElem());
3378 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeTimecode], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeTimecode)));
3379 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeMinSec], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeMinSec)));
3380 grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeCDFrame], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeCDFrame)));
3384 Editor::setup_tooltips ()
3386 set_tooltip (smart_mode_button, _("Smart Mode (add range functions to Grab Mode)"));
3387 set_tooltip (mouse_move_button, _("Grab Mode (select/move objects)"));
3388 set_tooltip (mouse_cut_button, _("Cut Mode (split regions)"));
3389 set_tooltip (mouse_select_button, _("Range Mode (select time ranges)"));
3390 set_tooltip (mouse_draw_button, _("Draw Mode (draw and edit gain/notes/automation)"));
3391 set_tooltip (mouse_timefx_button, _("Stretch Mode (time-stretch audio and midi regions, preserving pitch)"));
3392 set_tooltip (mouse_audition_button, _("Audition Mode (listen to regions)"));
3393 set_tooltip (mouse_content_button, _("Internal Edit Mode (edit notes and automation points)"));
3394 set_tooltip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
3395 set_tooltip (nudge_forward_button, _("Nudge Region/Selection Later"));
3396 set_tooltip (nudge_backward_button, _("Nudge Region/Selection Earlier"));
3397 set_tooltip (zoom_in_button, _("Zoom In"));
3398 set_tooltip (zoom_out_button, _("Zoom Out"));
3399 set_tooltip (zoom_preset_selector, _("Zoom to Time Scale"));
3400 set_tooltip (zoom_out_full_button, _("Zoom to Session"));
3401 set_tooltip (zoom_focus_selector, _("Zoom Focus"));
3402 set_tooltip (tav_expand_button, _("Expand Tracks"));
3403 set_tooltip (tav_shrink_button, _("Shrink Tracks"));
3404 set_tooltip (visible_tracks_selector, _("Number of visible tracks"));
3405 set_tooltip (grid_type_selector, _("Grid Mode"));
3406 set_tooltip (snap_mode_button, _("Snap Mode\n\nRight-click to visit Snap preferences."));
3407 set_tooltip (edit_point_selector, _("Edit Point"));
3408 set_tooltip (edit_mode_selector, _("Edit Mode"));
3409 set_tooltip (nudge_clock, _("Nudge Clock\n(controls distance used to nudge regions and selections)"));
3413 Editor::convert_drop_to_paths (
3414 vector<string>& paths,
3415 const RefPtr<Gdk::DragContext>& /*context*/,
3418 const SelectionData& data,
3422 if (_session == 0) {
3426 vector<string> uris = data.get_uris();
3430 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3431 are actually URI lists. So do it by hand.
3434 if (data.get_target() != "text/plain") {
3438 /* Parse the "uri-list" format that Nautilus provides,
3439 where each pathname is delimited by \r\n.
3441 THERE MAY BE NO NULL TERMINATING CHAR!!!
3444 string txt = data.get_text();
3448 p = (char *) malloc (txt.length() + 1);
3449 txt.copy (p, txt.length(), 0);
3450 p[txt.length()] = '\0';
3456 while (g_ascii_isspace (*p))
3460 while (*q && (*q != '\n') && (*q != '\r')) {
3467 while (q > p && g_ascii_isspace (*q))
3472 uris.push_back (string (p, q - p + 1));
3476 p = strchr (p, '\n');
3488 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3489 if ((*i).substr (0,7) == "file://") {
3490 paths.push_back (Glib::filename_from_uri (*i));
3498 Editor::new_tempo_section ()
3503 Editor::map_transport_state ()
3505 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state);
3507 if (_session && _session->transport_stopped()) {
3508 have_pending_keyboard_selection = false;
3511 update_loop_range_view ();
3515 Editor::transport_looped ()
3517 /* reset Playhead position interpolation.
3518 * see Editor::super_rapid_screen_update
3520 _last_update_time = 0;
3526 Editor::begin_selection_op_history ()
3528 selection_op_cmd_depth = 0;
3529 selection_op_history_it = 0;
3531 while(!selection_op_history.empty()) {
3532 delete selection_op_history.front();
3533 selection_op_history.pop_front();
3536 selection_undo_action->set_sensitive (false);
3537 selection_redo_action->set_sensitive (false);
3538 selection_op_history.push_front (&_selection_memento->get_state ());
3542 Editor::begin_reversible_selection_op (string name)
3545 //cerr << name << endl;
3546 /* begin/commit pairs can be nested */
3547 selection_op_cmd_depth++;
3552 Editor::commit_reversible_selection_op ()
3555 if (selection_op_cmd_depth == 1) {
3557 if (selection_op_history_it > 0 && selection_op_history_it < selection_op_history.size()) {
3558 /* The user has undone some selection ops and then made a new one,
3559 * making anything earlier in the list invalid.
3562 list<XMLNode *>::iterator it = selection_op_history.begin();
3563 list<XMLNode *>::iterator e_it = it;
3564 advance (e_it, selection_op_history_it);
3566 for (; it != e_it; ++it) {
3569 selection_op_history.erase (selection_op_history.begin(), e_it);
3572 selection_op_history.push_front (&_selection_memento->get_state ());
3573 selection_op_history_it = 0;
3575 selection_undo_action->set_sensitive (true);
3576 selection_redo_action->set_sensitive (false);
3579 if (selection_op_cmd_depth > 0) {
3580 selection_op_cmd_depth--;
3586 Editor::undo_selection_op ()
3589 selection_op_history_it++;
3591 for (std::list<XMLNode *>::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) {
3592 if (n == selection_op_history_it) {
3593 _selection_memento->set_state (*(*i), Stateful::current_state_version);
3594 selection_redo_action->set_sensitive (true);
3598 /* is there an earlier entry? */
3599 if ((selection_op_history_it + 1) >= selection_op_history.size()) {
3600 selection_undo_action->set_sensitive (false);
3606 Editor::redo_selection_op ()
3609 if (selection_op_history_it > 0) {
3610 selection_op_history_it--;
3613 for (std::list<XMLNode *>::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) {
3614 if (n == selection_op_history_it) {
3615 _selection_memento->set_state (*(*i), Stateful::current_state_version);
3616 selection_undo_action->set_sensitive (true);
3621 if (selection_op_history_it == 0) {
3622 selection_redo_action->set_sensitive (false);
3628 Editor::begin_reversible_command (string name)
3631 before.push_back (&_selection_memento->get_state ());
3632 _session->begin_reversible_command (name);
3637 Editor::begin_reversible_command (GQuark q)
3640 before.push_back (&_selection_memento->get_state ());
3641 _session->begin_reversible_command (q);
3646 Editor::abort_reversible_command ()
3649 while(!before.empty()) {
3650 delete before.front();
3653 _session->abort_reversible_command ();
3658 Editor::commit_reversible_command ()
3661 if (before.size() == 1) {
3662 _session->add_command (new MementoCommand<SelectionMemento>(*(_selection_memento), before.front(), &_selection_memento->get_state ()));
3663 redo_action->set_sensitive(false);
3664 undo_action->set_sensitive(true);
3665 begin_selection_op_history ();
3668 if (before.empty()) {
3669 cerr << "Please call begin_reversible_command() before commit_reversible_command()." << endl;
3674 _session->commit_reversible_command ();
3679 Editor::history_changed ()
3683 if (undo_action && _session) {
3684 if (_session->undo_depth() == 0) {
3685 label = S_("Command|Undo");
3687 label = string_compose(S_("Command|Undo (%1)"), _session->next_undo());
3689 undo_action->property_label() = label;
3692 if (redo_action && _session) {
3693 if (_session->redo_depth() == 0) {
3695 redo_action->set_sensitive (false);
3697 label = string_compose(_("Redo (%1)"), _session->next_redo());
3698 redo_action->set_sensitive (true);
3700 redo_action->property_label() = label;
3705 Editor::duplicate_range (bool with_dialog)
3709 RegionSelection rs = get_regions_from_selection_and_entered ();
3711 if (selection->time.length() == 0 && rs.empty()) {
3717 ArdourDialog win (_("Duplicate"));
3718 Label label (_("Number of duplications:"));
3719 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3720 SpinButton spinner (adjustment, 0.0, 1);
3723 win.get_vbox()->set_spacing (12);
3724 win.get_vbox()->pack_start (hbox);
3725 hbox.set_border_width (6);
3726 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3728 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3729 place, visually. so do this by hand.
3732 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3733 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3734 spinner.grab_focus();
3740 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3741 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3742 win.set_default_response (RESPONSE_ACCEPT);
3744 spinner.grab_focus ();
3746 switch (win.run ()) {
3747 case RESPONSE_ACCEPT:
3753 times = adjustment.get_value();
3756 if ((current_mouse_mode() == MouseRange)) {
3757 if (selection->time.length()) {
3758 duplicate_selection (times);
3760 } else if (get_smart_mode()) {
3761 if (selection->time.length()) {
3762 duplicate_selection (times);
3764 duplicate_some_regions (rs, times);
3766 duplicate_some_regions (rs, times);
3771 Editor::set_edit_mode (EditMode m)
3773 Config->set_edit_mode (m);
3777 Editor::cycle_edit_mode ()
3779 switch (Config->get_edit_mode()) {
3781 Config->set_edit_mode (Ripple);
3785 Config->set_edit_mode (Lock);
3788 Config->set_edit_mode (Slide);
3794 Editor::edit_mode_selection_done (EditMode m)
3796 Config->set_edit_mode (m);
3800 Editor::grid_type_selection_done (GridType gridtype)
3802 RefPtr<RadioAction> ract = grid_type_action (gridtype);
3804 ract->set_active ();
3809 Editor::snap_mode_selection_done (SnapMode mode)
3811 RefPtr<RadioAction> ract = snap_mode_action (mode);
3814 ract->set_active (true);
3819 Editor::cycle_edit_point (bool with_marker)
3821 if(Profile->get_mixbus())
3822 with_marker = false;
3824 switch (_edit_point) {
3826 set_edit_point_preference (EditAtPlayhead);
3828 case EditAtPlayhead:
3830 set_edit_point_preference (EditAtSelectedMarker);
3832 set_edit_point_preference (EditAtMouse);
3835 case EditAtSelectedMarker:
3836 set_edit_point_preference (EditAtMouse);
3842 Editor::edit_point_selection_done (EditPoint ep)
3844 set_edit_point_preference (ep);
3848 Editor::build_zoom_focus_menu ()
3850 using namespace Menu_Helpers;
3852 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusLeft], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusLeft)));
3853 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusRight], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusRight)));
3854 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusCenter], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusCenter)));
3855 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusPlayhead], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusPlayhead)));
3856 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusMouse], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusMouse)));
3857 zoom_focus_selector.AddMenuElem (MenuElem (zoom_focus_strings[(int)ZoomFocusEdit], sigc::bind (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done), (ZoomFocus) ZoomFocusEdit)));
3859 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_TRIANGLE_WIDTH, 2);
3863 Editor::zoom_focus_selection_done (ZoomFocus f)
3865 RefPtr<RadioAction> ract = zoom_focus_action (f);
3867 ract->set_active ();
3872 Editor::build_track_count_menu ()
3874 using namespace Menu_Helpers;
3876 if (!Profile->get_mixbus()) {
3877 visible_tracks_selector.AddMenuElem (MenuElem (X_("1"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 1)));
3878 visible_tracks_selector.AddMenuElem (MenuElem (X_("2"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 2)));
3879 visible_tracks_selector.AddMenuElem (MenuElem (X_("3"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 3)));
3880 visible_tracks_selector.AddMenuElem (MenuElem (X_("4"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 4)));
3881 visible_tracks_selector.AddMenuElem (MenuElem (X_("8"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 8)));
3882 visible_tracks_selector.AddMenuElem (MenuElem (X_("12"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 12)));
3883 visible_tracks_selector.AddMenuElem (MenuElem (X_("16"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 16)));
3884 visible_tracks_selector.AddMenuElem (MenuElem (X_("20"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 20)));
3885 visible_tracks_selector.AddMenuElem (MenuElem (X_("24"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 24)));
3886 visible_tracks_selector.AddMenuElem (MenuElem (X_("32"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 32)));
3887 visible_tracks_selector.AddMenuElem (MenuElem (X_("64"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 64)));
3888 visible_tracks_selector.AddMenuElem (MenuElem (_("Selection"), sigc::mem_fun(*this, &Editor::fit_selection)));
3889 visible_tracks_selector.AddMenuElem (MenuElem (_("All"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 0)));
3891 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 1 track"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 1)));
3892 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 2 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 2)));
3893 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 4 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 4)));
3894 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 8 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 8)));
3895 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 16 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 16)));
3896 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 24 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 24)));
3897 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 32 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 32)));
3898 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 48 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 48)));
3899 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit All tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 0)));
3900 visible_tracks_selector.AddMenuElem (MenuElem (_("Fit Selection"), sigc::mem_fun(*this, &Editor::fit_selection)));
3902 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 10 ms"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 10)));
3903 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 100 ms"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 100)));
3904 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 1 sec"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 1 * 1000)));
3905 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 10 sec"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 10 * 1000)));
3906 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 1 min"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 60 * 1000)));
3907 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 10 min"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 10 * 60 * 1000)));
3908 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 1 hour"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 60 * 60 * 1000)));
3909 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 8 hours"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 8 * 60 * 60 * 1000)));
3910 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 24 hours"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 24 * 60 * 60 * 1000)));
3911 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Session"), sigc::mem_fun(*this, &Editor::temporal_zoom_session)));
3912 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Extents"), sigc::mem_fun(*this, &Editor::temporal_zoom_extents)));
3913 zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Range/Region Selection"), sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_selection), Horizontal)));
3918 Editor::set_zoom_preset (int64_t ms)
3921 temporal_zoom_session();
3925 ARDOUR::samplecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
3926 temporal_zoom ((sample_rate * ms / 1000) / _visible_canvas_width);
3930 Editor::set_visible_track_count (int32_t n)
3932 _visible_track_count = n;
3934 /* if the canvas hasn't really been allocated any size yet, just
3935 record the desired number of visible tracks and return. when canvas
3936 allocation happens, we will get called again and then we can do the
3940 if (_visible_canvas_height <= 1) {
3946 DisplaySuspender ds;
3948 if (_visible_track_count > 0) {
3949 h = trackviews_height() / _visible_track_count;
3950 std::ostringstream s;
3951 s << _visible_track_count;
3953 } else if (_visible_track_count == 0) {
3955 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
3956 if ((*i)->marked_for_display()) {
3958 TimeAxisView::Children cl ((*i)->get_child_list ());
3959 for (TimeAxisView::Children::const_iterator j = cl.begin(); j != cl.end(); ++j) {
3960 if ((*j)->marked_for_display()) {
3967 visible_tracks_selector.set_text (X_("*"));
3970 h = trackviews_height() / n;
3973 /* negative value means that the visible track count has
3974 been overridden by explicit track height changes.
3976 visible_tracks_selector.set_text (X_("*"));
3980 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3981 (*i)->set_height (h, TimeAxisView::HeightPerLane);
3984 if (str != visible_tracks_selector.get_text()) {
3985 visible_tracks_selector.set_text (str);
3990 Editor::override_visible_track_count ()
3992 _visible_track_count = -1;
3993 visible_tracks_selector.set_text (_("*"));
3997 Editor::edit_controls_button_release (GdkEventButton* ev)
3999 if (Keyboard::is_context_menu_event (ev)) {
4000 ARDOUR_UI::instance()->add_route ();
4001 } else if (ev->button == 1) {
4002 selection->clear_tracks ();
4009 Editor::mouse_select_button_release (GdkEventButton* ev)
4011 /* this handles just right-clicks */
4013 if (ev->button != 3) {
4021 Editor::set_zoom_focus (ZoomFocus f)
4023 string str = zoom_focus_strings[(int)f];
4025 if (str != zoom_focus_selector.get_text()) {
4026 zoom_focus_selector.set_text (str);
4029 if (zoom_focus != f) {
4036 Editor::cycle_zoom_focus ()
4038 switch (zoom_focus) {
4040 set_zoom_focus (ZoomFocusRight);
4042 case ZoomFocusRight:
4043 set_zoom_focus (ZoomFocusCenter);
4045 case ZoomFocusCenter:
4046 set_zoom_focus (ZoomFocusPlayhead);
4048 case ZoomFocusPlayhead:
4049 set_zoom_focus (ZoomFocusMouse);
4051 case ZoomFocusMouse:
4052 set_zoom_focus (ZoomFocusEdit);
4055 set_zoom_focus (ZoomFocusLeft);
4061 Editor::update_grid ()
4063 if (grid_musical()) {
4064 std::vector<TempoMap::BBTPoint> grid;
4065 if (bbt_ruler_scale != bbt_show_many) {
4066 compute_current_bbt_points (grid, _leftmost_sample, _leftmost_sample + current_page_samples());
4068 maybe_draw_grid_lines ();
4069 } else if (grid_nonmusical()) {
4070 maybe_draw_grid_lines ();
4077 Editor::toggle_follow_playhead ()
4079 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
4081 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
4082 set_follow_playhead (tact->get_active());
4086 /** @param yn true to follow playhead, otherwise false.
4087 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
4090 Editor::set_follow_playhead (bool yn, bool catch_up)
4092 if (_follow_playhead != yn) {
4093 if ((_follow_playhead = yn) == true && catch_up) {
4095 reset_x_origin_to_follow_playhead ();
4102 Editor::toggle_stationary_playhead ()
4104 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
4106 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
4107 set_stationary_playhead (tact->get_active());
4112 Editor::set_stationary_playhead (bool yn)
4114 if (_stationary_playhead != yn) {
4115 if ((_stationary_playhead = yn) == true) {
4116 /* catch up -- FIXME need a 3.0 equivalent of this 2.X call */
4117 // update_current_screen ();
4124 Editor::playlist_selector () const
4126 return *_playlist_selector;
4130 Editor::get_paste_offset (samplepos_t pos, unsigned paste_count, samplecnt_t duration)
4132 if (paste_count == 0) {
4133 /* don't bother calculating an offset that will be zero anyway */
4137 /* calculate basic unsnapped multi-paste offset */
4138 samplecnt_t offset = paste_count * duration;
4140 /* snap offset so pos + offset is aligned to the grid */
4141 MusicSample offset_pos (pos + offset, 0);
4142 snap_to(offset_pos, RoundUpMaybe);
4143 offset = offset_pos.sample - pos;
4149 Editor::get_grid_beat_divisions(samplepos_t position)
4151 switch (_grid_type) {
4152 case GridTypeBeatDiv32: return 32;
4153 case GridTypeBeatDiv28: return 28;
4154 case GridTypeBeatDiv24: return 24;
4155 case GridTypeBeatDiv20: return 20;
4156 case GridTypeBeatDiv16: return 16;
4157 case GridTypeBeatDiv14: return 14;
4158 case GridTypeBeatDiv12: return 12;
4159 case GridTypeBeatDiv10: return 10;
4160 case GridTypeBeatDiv8: return 8;
4161 case GridTypeBeatDiv7: return 7;
4162 case GridTypeBeatDiv6: return 6;
4163 case GridTypeBeatDiv5: return 5;
4164 case GridTypeBeatDiv4: return 4;
4165 case GridTypeBeatDiv3: return 3;
4166 case GridTypeBeatDiv2: return 2;
4167 case GridTypeBeat: return 1;
4168 case GridTypeBar: return 1;
4170 case GridTypeNone: return 0;
4171 case GridTypeTimecode: return 0;
4172 case GridTypeMinSec: return 0;
4173 case GridTypeCDFrame: return 0;
4179 /** returns the current musical grid divisiions using the supplied modifier mask from a GtkEvent.
4180 if the grid is non-musical, returns 0.
4181 if the grid is snapped to bars, returns -1.
4182 @param event_state the current keyboard modifier mask.
4185 Editor::get_grid_music_divisions (uint32_t event_state)
4187 if (snap_mode() == SnapOff && !ArdourKeyboard::indicates_snap (event_state)) {
4191 if (snap_mode() != SnapOff && ArdourKeyboard::indicates_snap (event_state)) {
4195 switch (_grid_type) {
4196 case GridTypeBeatDiv32: return 32;
4197 case GridTypeBeatDiv28: return 28;
4198 case GridTypeBeatDiv24: return 24;
4199 case GridTypeBeatDiv20: return 20;
4200 case GridTypeBeatDiv16: return 16;
4201 case GridTypeBeatDiv14: return 14;
4202 case GridTypeBeatDiv12: return 12;
4203 case GridTypeBeatDiv10: return 10;
4204 case GridTypeBeatDiv8: return 8;
4205 case GridTypeBeatDiv7: return 7;
4206 case GridTypeBeatDiv6: return 6;
4207 case GridTypeBeatDiv5: return 5;
4208 case GridTypeBeatDiv4: return 4;
4209 case GridTypeBeatDiv3: return 3;
4210 case GridTypeBeatDiv2: return 2;
4211 case GridTypeBeat: return 1;
4212 case GridTypeBar : return -1;
4214 case GridTypeNone: return 0;
4215 case GridTypeTimecode: return 0;
4216 case GridTypeMinSec: return 0;
4217 case GridTypeCDFrame: return 0;
4223 Editor::get_grid_type_as_beats (bool& success, samplepos_t position)
4227 const unsigned divisions = get_grid_beat_divisions(position);
4229 return Temporal::Beats(1.0 / (double)get_grid_beat_divisions(position));
4232 switch (_grid_type) {
4234 return Temporal::Beats(4.0 / _session->tempo_map().meter_at_sample (position).note_divisor());
4237 const Meter& m = _session->tempo_map().meter_at_sample (position);
4238 return Temporal::Beats((4.0 * m.divisions_per_bar()) / m.note_divisor());
4246 return Temporal::Beats();
4250 Editor::get_nudge_distance (samplepos_t pos, samplecnt_t& next)
4254 ret = nudge_clock->current_duration (pos);
4255 next = ret + 1; /* XXXX fix me */
4261 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
4263 ArdourDialog dialog (_("Playlist Deletion"));
4264 Label label (string_compose (_("Playlist %1 is currently unused.\n"
4265 "If it is kept, its audio files will not be cleaned.\n"
4266 "If it is deleted, audio files used by it alone will be cleaned."),
4269 dialog.set_position (WIN_POS_CENTER);
4270 dialog.get_vbox()->pack_start (label);
4274 dialog.add_button (_("Delete All Unused"), RESPONSE_YES); // needs clarification. this and all remaining ones
4275 dialog.add_button (_("Delete Playlist"), RESPONSE_ACCEPT);
4276 Button* keep = dialog.add_button (_("Keep Playlist"), RESPONSE_REJECT);
4277 dialog.add_button (_("Keep Remaining"), RESPONSE_NO); // ditto
4278 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
4280 /* by default gtk uses the left most button */
4281 keep->grab_focus ();
4283 switch (dialog.run ()) {
4285 /* keep this and all remaining ones */
4290 /* delete this and all others */
4294 case RESPONSE_ACCEPT:
4295 /* delete the playlist */
4299 case RESPONSE_REJECT:
4300 /* keep the playlist */
4312 Editor::audio_region_selection_covers (samplepos_t where)
4314 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
4315 if ((*a)->region()->covers (where)) {
4324 Editor::prepare_for_cleanup ()
4326 cut_buffer->clear_regions ();
4327 cut_buffer->clear_playlists ();
4329 selection->clear_regions ();
4330 selection->clear_playlists ();
4332 _regions->suspend_redisplay ();
4336 Editor::finish_cleanup ()
4338 _regions->resume_redisplay ();
4342 Editor::transport_loop_location()
4345 return _session->locations()->auto_loop_location();
4352 Editor::transport_punch_location()
4355 return _session->locations()->auto_punch_location();
4362 Editor::control_layout_scroll (GdkEventScroll* ev)
4364 /* Just forward to the normal canvas scroll method. The coordinate
4365 systems are different but since the canvas is always larger than the
4366 track headers, and aligned with the trackview area, this will work.
4368 In the not too distant future this layout is going away anyway and
4369 headers will be on the canvas.
4371 return canvas_scroll_event (ev, false);
4375 Editor::session_state_saved (string)
4378 _snapshots->redisplay ();
4382 Editor::maximise_editing_space ()
4388 Gtk::Window* toplevel = current_toplevel();
4391 toplevel->fullscreen ();
4397 Editor::restore_editing_space ()
4403 Gtk::Window* toplevel = current_toplevel();
4406 toplevel->unfullscreen();
4412 * Make new playlists for a given track and also any others that belong
4413 * to the same active route group with the `select' property.
4418 Editor::new_playlists (TimeAxisView* v)
4420 begin_reversible_command (_("new playlists"));
4421 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4422 _session->playlists->get (playlists);
4423 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::group_select.property_id);
4424 commit_reversible_command ();
4428 * Use a copy of the current playlist for a given track and also any others that belong
4429 * to the same active route group with the `select' property.
4434 Editor::copy_playlists (TimeAxisView* v)
4436 begin_reversible_command (_("copy playlists"));
4437 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4438 _session->playlists->get (playlists);
4439 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::group_select.property_id);
4440 commit_reversible_command ();
4443 /** Clear the current playlist for a given track and also any others that belong
4444 * to the same active route group with the `select' property.
4449 Editor::clear_playlists (TimeAxisView* v)
4451 begin_reversible_command (_("clear playlists"));
4452 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4453 _session->playlists->get (playlists);
4454 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::group_select.property_id);
4455 commit_reversible_command ();
4459 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4461 atv.use_new_playlist (sz > 1 ? false : true, playlists, false);
4465 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4467 atv.use_new_playlist (sz > 1 ? false : true, playlists, true);
4471 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4473 atv.clear_playlist ();
4477 Editor::get_y_origin () const
4479 return vertical_adjustment.get_value ();
4482 /** Queue up a change to the viewport x origin.
4483 * @param sample New x origin.
4486 Editor::reset_x_origin (samplepos_t sample)
4488 pending_visual_change.add (VisualChange::TimeOrigin);
4489 pending_visual_change.time_origin = sample;
4490 ensure_visual_change_idle_handler ();
4494 Editor::reset_y_origin (double y)
4496 pending_visual_change.add (VisualChange::YOrigin);
4497 pending_visual_change.y_origin = y;
4498 ensure_visual_change_idle_handler ();
4502 Editor::reset_zoom (samplecnt_t spp)
4504 if (spp == samples_per_pixel) {
4508 pending_visual_change.add (VisualChange::ZoomLevel);
4509 pending_visual_change.samples_per_pixel = spp;
4510 ensure_visual_change_idle_handler ();
4514 Editor::reposition_and_zoom (samplepos_t sample, double fpu)
4516 reset_x_origin (sample);
4519 if (!no_save_visual) {
4520 undo_visual_stack.push_back (current_visual_state(false));
4524 Editor::VisualState::VisualState (bool with_tracks)
4525 : gui_state (with_tracks ? new GUIObjectState : 0)
4529 Editor::VisualState::~VisualState ()
4534 Editor::VisualState*
4535 Editor::current_visual_state (bool with_tracks)
4537 VisualState* vs = new VisualState (with_tracks);
4538 vs->y_position = vertical_adjustment.get_value();
4539 vs->samples_per_pixel = samples_per_pixel;
4540 vs->_leftmost_sample = _leftmost_sample;
4541 vs->zoom_focus = zoom_focus;
4544 vs->gui_state->set_state (ARDOUR_UI::instance()->gui_object_state->get_state());
4551 Editor::undo_visual_state ()
4553 if (undo_visual_stack.empty()) {
4557 VisualState* vs = undo_visual_stack.back();
4558 undo_visual_stack.pop_back();
4561 redo_visual_stack.push_back (current_visual_state (vs ? vs->gui_state != 0 : false));
4564 use_visual_state (*vs);
4569 Editor::redo_visual_state ()
4571 if (redo_visual_stack.empty()) {
4575 VisualState* vs = redo_visual_stack.back();
4576 redo_visual_stack.pop_back();
4578 /* XXX: can 'vs' really be 0? Is there a place that puts NULL pointers onto the stack? */
4579 undo_visual_stack.push_back (current_visual_state (vs ? (vs->gui_state != 0) : false));
4582 use_visual_state (*vs);
4587 Editor::swap_visual_state ()
4589 if (undo_visual_stack.empty()) {
4590 redo_visual_state ();
4592 undo_visual_state ();
4597 Editor::use_visual_state (VisualState& vs)
4599 PBD::Unwinder<bool> nsv (no_save_visual, true);
4600 DisplaySuspender ds;
4602 vertical_adjustment.set_value (vs.y_position);
4604 set_zoom_focus (vs.zoom_focus);
4605 reposition_and_zoom (vs._leftmost_sample, vs.samples_per_pixel);
4608 ARDOUR_UI::instance()->gui_object_state->set_state (vs.gui_state->get_state());
4610 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4611 (*i)->clear_property_cache();
4612 (*i)->reset_visual_state ();
4616 _routes->update_visibility ();
4619 /** This is the core function that controls the zoom level of the canvas. It is called
4620 * whenever one or more calls are made to reset_zoom(). It executes in an idle handler.
4621 * @param spp new number of samples per pixel
4624 Editor::set_samples_per_pixel (samplecnt_t spp)
4630 const samplecnt_t three_days = 3 * 24 * 60 * 60 * (_session ? _session->sample_rate() : 48000);
4631 const samplecnt_t lots_of_pixels = 4000;
4633 /* if the zoom level is greater than what you'd get trying to display 3
4634 * days of audio on a really big screen, then it's too big.
4637 if (spp * lots_of_pixels > three_days) {
4641 samples_per_pixel = spp;
4645 Editor::on_samples_per_pixel_changed ()
4647 bool const showing_time_selection = selection->time.length() > 0;
4649 if (showing_time_selection && selection->time.start () != selection->time.end_sample ()) {
4650 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4651 (*i)->reshow_selection (selection->time);
4655 ZoomChanged (); /* EMIT_SIGNAL */
4657 ArdourCanvas::GtkCanvasViewport* c;
4659 c = get_track_canvas();
4661 c->canvas()->zoomed ();
4664 if (playhead_cursor) {
4665 playhead_cursor->set_position (playhead_cursor->current_sample ());
4668 refresh_location_display();
4669 _summary->set_overlays_dirty ();
4671 update_marker_labels ();
4677 Editor::playhead_cursor_sample () const
4679 return playhead_cursor->current_sample();
4683 Editor::queue_visual_videotimeline_update ()
4685 pending_visual_change.add (VisualChange::VideoTimeline);
4686 ensure_visual_change_idle_handler ();
4690 Editor::ensure_visual_change_idle_handler ()
4692 if (pending_visual_change.idle_handler_id < 0) {
4693 /* see comment in add_to_idle_resize above. */
4694 pending_visual_change.idle_handler_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, _idle_visual_changer, this, NULL);
4695 pending_visual_change.being_handled = false;
4700 Editor::_idle_visual_changer (void* arg)
4702 return static_cast<Editor*>(arg)->idle_visual_changer ();
4706 Editor::pre_render ()
4708 visual_change_queued = false;
4710 if (pending_visual_change.pending != 0) {
4711 ensure_visual_change_idle_handler();
4716 Editor::idle_visual_changer ()
4718 pending_visual_change.idle_handler_id = -1;
4720 if (pending_visual_change.pending == 0) {
4724 /* set_horizontal_position() below (and maybe other calls) call
4725 gtk_main_iteration(), so it's possible that a signal will be handled
4726 half-way through this method. If this signal wants an
4727 idle_visual_changer we must schedule another one after this one, so
4728 mark the idle_handler_id as -1 here to allow that. Also make a note
4729 that we are doing the visual change, so that changes in response to
4730 super-rapid-screen-update can be dropped if we are still processing
4734 if (visual_change_queued) {
4738 pending_visual_change.being_handled = true;
4740 VisualChange vc = pending_visual_change;
4742 pending_visual_change.pending = (VisualChange::Type) 0;
4744 visual_changer (vc);
4746 pending_visual_change.being_handled = false;
4748 visual_change_queued = true;
4750 return 0; /* this is always a one-shot call */
4754 Editor::visual_changer (const VisualChange& vc)
4757 * Changed first so the correct horizontal canvas position is calculated in
4758 * Editor::set_horizontal_position
4760 if (vc.pending & VisualChange::ZoomLevel) {
4761 set_samples_per_pixel (vc.samples_per_pixel);
4764 if (vc.pending & VisualChange::TimeOrigin) {
4765 double new_time_origin = sample_to_pixel_unrounded (vc.time_origin);
4766 set_horizontal_position (new_time_origin);
4769 if (vc.pending & VisualChange::YOrigin) {
4770 vertical_adjustment.set_value (vc.y_origin);
4774 * Now the canvas is in the final state before render the canvas items that
4775 * support the Item::prepare_for_render interface can calculate the correct
4776 * item to visible canvas intersection.
4778 if (vc.pending & VisualChange::ZoomLevel) {
4779 on_samples_per_pixel_changed ();
4781 compute_fixed_ruler_scale ();
4783 compute_bbt_ruler_scale (vc.time_origin, pending_visual_change.time_origin + current_page_samples());
4784 update_tempo_based_rulers ();
4787 if (!(vc.pending & VisualChange::ZoomLevel)) {
4788 /* If the canvas is not being zoomed then the canvas items will not change
4789 * and cause Item::prepare_for_render to be called so do it here manually.
4790 * Not ideal, but I can't think of a better solution atm.
4792 _track_canvas->prepare_for_render();
4795 /* If we are only scrolling vertically there is no need to update these */
4796 if (vc.pending != VisualChange::YOrigin) {
4797 update_fixed_rulers ();
4798 redisplay_grid (true);
4800 /* video frames & position need to be updated for zoom, horiz-scroll
4801 * and (explicitly) VisualChange::VideoTimeline.
4803 update_video_timeline();
4806 _summary->set_overlays_dirty ();
4809 struct EditorOrderTimeAxisSorter {
4810 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4811 return a->order () < b->order ();
4816 Editor::sort_track_selection (TrackViewList& sel)
4818 EditorOrderTimeAxisSorter cmp;
4823 Editor::get_preferred_edit_position (EditIgnoreOption ignore, bool from_context_menu, bool from_outside_canvas)
4826 samplepos_t where = 0;
4827 EditPoint ep = _edit_point;
4829 if (Profile->get_mixbus()) {
4830 if (ep == EditAtSelectedMarker) {
4831 ep = EditAtPlayhead;
4835 if (from_outside_canvas && (ep == EditAtMouse)) {
4836 ep = EditAtPlayhead;
4837 } else if (from_context_menu && (ep == EditAtMouse)) {
4838 return canvas_event_sample (&context_click_event, 0, 0);
4841 if (entered_marker) {
4842 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4843 return entered_marker->position();
4846 if ((ignore == EDIT_IGNORE_PHEAD) && ep == EditAtPlayhead) {
4847 ep = EditAtSelectedMarker;
4850 if ((ignore == EDIT_IGNORE_MOUSE) && ep == EditAtMouse) {
4851 ep = EditAtPlayhead;
4854 MusicSample snap_mf (0, 0);
4857 case EditAtPlayhead:
4858 if (_dragging_playhead) {
4859 /* NOTE: since the user is dragging with the mouse, this operation will implicitly be Snapped */
4860 where = playhead_cursor->current_sample();
4862 where = _session->audible_sample();
4864 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4867 case EditAtSelectedMarker:
4868 if (!selection->markers.empty()) {
4870 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4873 where = loc->start();
4877 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4885 if (!mouse_sample (where, ignored)) {
4886 /* XXX not right but what can we do ? */
4889 snap_mf.sample = where;
4891 where = snap_mf.sample;
4892 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4900 Editor::set_loop_range (samplepos_t start, samplepos_t end, string cmd)
4902 if (!_session) return;
4904 begin_reversible_command (cmd);
4908 if ((tll = transport_loop_location()) == 0) {
4909 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop, get_grid_music_divisions(0));
4910 XMLNode &before = _session->locations()->get_state();
4911 _session->locations()->add (loc, true);
4912 _session->set_auto_loop_location (loc);
4913 XMLNode &after = _session->locations()->get_state();
4914 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4916 XMLNode &before = tll->get_state();
4917 tll->set_hidden (false, this);
4918 tll->set (start, end);
4919 XMLNode &after = tll->get_state();
4920 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4923 commit_reversible_command ();
4927 Editor::set_punch_range (samplepos_t start, samplepos_t end, string cmd)
4929 if (!_session) return;
4931 begin_reversible_command (cmd);
4935 if ((tpl = transport_punch_location()) == 0) {
4936 Location* loc = new Location (*_session, start, end, _("Punch"), Location::IsAutoPunch, get_grid_music_divisions(0));
4937 XMLNode &before = _session->locations()->get_state();
4938 _session->locations()->add (loc, true);
4939 _session->set_auto_punch_location (loc);
4940 XMLNode &after = _session->locations()->get_state();
4941 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4943 XMLNode &before = tpl->get_state();
4944 tpl->set_hidden (false, this);
4945 tpl->set (start, end);
4946 XMLNode &after = tpl->get_state();
4947 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4950 commit_reversible_command ();
4953 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4954 * @param rs List to which found regions are added.
4955 * @param where Time to look at.
4956 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4959 Editor::get_regions_at (RegionSelection& rs, samplepos_t where, const TrackViewList& ts) const
4961 const TrackViewList* tracks;
4964 tracks = &track_views;
4969 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4971 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4974 boost::shared_ptr<Track> tr;
4975 boost::shared_ptr<Playlist> pl;
4977 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4979 boost::shared_ptr<RegionList> regions = pl->regions_at (where);
4981 for (RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4982 RegionView* rv = rtv->view()->find_view (*i);
4993 Editor::get_regions_after (RegionSelection& rs, samplepos_t where, const TrackViewList& ts) const
4995 const TrackViewList* tracks;
4998 tracks = &track_views;
5003 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
5004 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
5006 boost::shared_ptr<Track> tr;
5007 boost::shared_ptr<Playlist> pl;
5009 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
5011 boost::shared_ptr<RegionList> regions = pl->regions_touched (where, max_samplepos);
5013 for (RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
5015 RegionView* rv = rtv->view()->find_view (*i);
5026 /** Get regions using the following method:
5028 * Make a region list using:
5029 * (a) any selected regions
5030 * (b) the intersection of any selected tracks and the edit point(*)
5031 * (c) if neither exists, and edit_point == mouse, then whatever region is under the mouse
5033 * (*) NOTE: in this case, if 'No Selection = All Tracks' is active, search all tracks
5035 * Note that we have forced the rule that selected regions and selected tracks are mutually exclusive
5039 Editor::get_regions_from_selection_and_edit_point (EditIgnoreOption ignore, bool from_context_menu, bool from_outside_canvas)
5041 RegionSelection regions;
5043 if (_edit_point == EditAtMouse && entered_regionview && selection->tracks.empty() && selection->regions.empty()) {
5044 regions.add (entered_regionview);
5046 regions = selection->regions;
5049 if (regions.empty()) {
5050 TrackViewList tracks = selection->tracks;
5052 if (!tracks.empty()) {
5053 /* no region selected or entered, but some selected tracks:
5054 * act on all regions on the selected tracks at the edit point
5056 samplepos_t const where = get_preferred_edit_position (ignore, from_context_menu, from_outside_canvas);
5057 get_regions_at(regions, where, tracks);
5064 /** Get regions using the following method:
5066 * Make a region list using:
5067 * (a) any selected regions
5068 * (b) the intersection of any selected tracks and the edit point(*)
5069 * (c) if neither exists, then whatever region is under the mouse
5071 * (*) NOTE: in this case, if 'No Selection = All Tracks' is active, search all tracks
5073 * Note that we have forced the rule that selected regions and selected tracks are mutually exclusive
5076 Editor::get_regions_from_selection_and_mouse (samplepos_t pos)
5078 RegionSelection regions;
5080 if (entered_regionview && selection->tracks.empty() && selection->regions.empty()) {
5081 regions.add (entered_regionview);
5083 regions = selection->regions;
5086 if (regions.empty()) {
5087 TrackViewList tracks = selection->tracks;
5089 if (!tracks.empty()) {
5090 /* no region selected or entered, but some selected tracks:
5091 * act on all regions on the selected tracks at the edit point
5093 get_regions_at(regions, pos, tracks);
5100 /** Start with regions that are selected, or the entered regionview if none are selected.
5101 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
5102 * of the regions that we started with.
5106 Editor::get_regions_from_selection_and_entered () const
5108 RegionSelection regions = selection->regions;
5110 if (regions.empty() && entered_regionview) {
5111 regions.add (entered_regionview);
5118 Editor::get_regionviews_by_id (PBD::ID const id, RegionSelection & regions) const
5120 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5121 RouteTimeAxisView* rtav;
5123 if ((rtav = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5124 boost::shared_ptr<Playlist> pl;
5125 std::vector<boost::shared_ptr<Region> > results;
5126 boost::shared_ptr<Track> tr;
5128 if ((tr = rtav->track()) == 0) {
5133 if ((pl = (tr->playlist())) != 0) {
5134 boost::shared_ptr<Region> r = pl->region_by_id (id);
5136 RegionView* rv = rtav->view()->find_view (r);
5138 regions.push_back (rv);
5147 Editor::get_per_region_note_selection (list<pair<PBD::ID, set<boost::shared_ptr<Evoral::Note<Temporal::Beats> > > > > &selection) const
5150 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5151 MidiTimeAxisView* mtav;
5153 if ((mtav = dynamic_cast<MidiTimeAxisView*> (*i)) != 0) {
5155 mtav->get_per_region_note_selection (selection);
5162 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions, bool src_comparison)
5164 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5166 RouteTimeAxisView* tatv;
5168 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5170 boost::shared_ptr<Playlist> pl;
5171 vector<boost::shared_ptr<Region> > results;
5173 boost::shared_ptr<Track> tr;
5175 if ((tr = tatv->track()) == 0) {
5180 if ((pl = (tr->playlist())) != 0) {
5181 if (src_comparison) {
5182 pl->get_source_equivalent_regions (region, results);
5184 pl->get_region_list_equivalent_regions (region, results);
5188 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
5189 if ((marv = tatv->view()->find_view (*ir)) != 0) {
5190 regions.push_back (marv);
5199 Editor::regionview_from_region (boost::shared_ptr<Region> region) const
5201 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5202 RouteTimeAxisView* tatv;
5203 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5204 if (!tatv->track()) {
5207 RegionView* marv = tatv->view()->find_view (region);
5217 Editor::rtav_from_route (boost::shared_ptr<Route> route) const
5219 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5220 RouteTimeAxisView* rtav;
5221 if ((rtav = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
5222 if (rtav->route() == route) {
5231 Editor::show_rhythm_ferret ()
5233 if (rhythm_ferret == 0) {
5234 rhythm_ferret = new RhythmFerret(*this);
5237 rhythm_ferret->set_session (_session);
5238 rhythm_ferret->show ();
5239 rhythm_ferret->present ();
5243 Editor::first_idle ()
5245 MessageDialog* dialog = 0;
5247 if (track_views.size() > 1) {
5248 Timers::TimerSuspender t;
5249 dialog = new MessageDialog (
5250 string_compose (_("Please wait while %1 loads visual data."), PROGRAM_NAME),
5254 ARDOUR_UI::instance()->flush_pending (60);
5257 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
5261 /* now that all regionviews should exist, setup region selection */
5265 for (list<PBD::ID>::iterator pr = selection->regions.pending.begin (); pr != selection->regions.pending.end (); ++pr) {
5266 /* this is cumulative: rs is NOT cleared each time */
5267 get_regionviews_by_id (*pr, rs);
5270 selection->set (rs);
5272 /* first idle adds route children (automation tracks), so we need to redisplay here */
5273 _routes->redisplay ();
5277 if (_session->undo_depth() == 0) {
5278 undo_action->set_sensitive(false);
5280 redo_action->set_sensitive(false);
5281 begin_selection_op_history ();
5287 Editor::_idle_resize (gpointer arg)
5289 return ((Editor*)arg)->idle_resize ();
5293 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
5295 if (resize_idle_id < 0) {
5296 /* https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#G-PRIORITY-HIGH-IDLE:CAPS
5297 * GTK+ uses G_PRIORITY_HIGH_IDLE + 10 for resizing operations, and G_PRIORITY_HIGH_IDLE + 20 for redrawing operations.
5298 * (This is done to ensure that any pending resizes are processed before any pending redraws, so that widgets are not redrawn twice unnecessarily.)
5300 resize_idle_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, _idle_resize, this, NULL);
5301 _pending_resize_amount = 0;
5304 /* make a note of the smallest resulting height, so that we can clamp the
5305 lower limit at TimeAxisView::hSmall */
5307 int32_t min_resulting = INT32_MAX;
5309 _pending_resize_amount += h;
5310 _pending_resize_view = view;
5312 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
5314 if (selection->tracks.contains (_pending_resize_view)) {
5315 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5316 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
5320 if (min_resulting < 0) {
5325 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
5326 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
5330 /** Handle pending resizing of tracks */
5332 Editor::idle_resize ()
5334 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
5336 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
5337 selection->tracks.contains (_pending_resize_view)) {
5339 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5340 if (*i != _pending_resize_view) {
5341 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
5346 _pending_resize_amount = 0;
5347 _group_tabs->set_dirty ();
5348 resize_idle_id = -1;
5356 ENSURE_GUI_THREAD (*this, &Editor::located);
5359 playhead_cursor->set_position (_session->audible_sample ());
5360 if (_follow_playhead && !_pending_initial_locate) {
5361 reset_x_origin_to_follow_playhead ();
5365 _pending_locate_request = false;
5366 _pending_initial_locate = false;
5367 _last_update_time = 0;
5371 Editor::region_view_added (RegionView * rv)
5373 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (rv);
5375 list<pair<PBD::ID const, list<Evoral::event_id_t> > >::iterator rnote;
5376 for (rnote = selection->pending_midi_note_selection.begin(); rnote != selection->pending_midi_note_selection.end(); ++rnote) {
5377 if (rv->region()->id () == (*rnote).first) {
5378 mrv->select_notes ((*rnote).second);
5379 selection->pending_midi_note_selection.erase(rnote);
5385 _summary->set_background_dirty ();
5387 mark_region_boundary_cache_dirty ();
5391 Editor::region_view_removed ()
5393 _summary->set_background_dirty ();
5395 mark_region_boundary_cache_dirty ();
5399 Editor::axis_view_by_stripable (boost::shared_ptr<Stripable> s) const
5401 for (TrackViewList::const_iterator j = track_views.begin (); j != track_views.end(); ++j) {
5402 if ((*j)->stripable() == s) {
5411 Editor::axis_view_by_control (boost::shared_ptr<AutomationControl> c) const
5413 for (TrackViewList::const_iterator j = track_views.begin (); j != track_views.end(); ++j) {
5414 if ((*j)->control() == c) {
5418 TimeAxisView::Children kids = (*j)->get_child_list ();
5420 for (TimeAxisView::Children::iterator k = kids.begin(); k != kids.end(); ++k) {
5421 if ((*k)->control() == c) {
5431 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
5435 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
5436 TimeAxisView* tv = time_axis_view_from_stripable (*i);
5446 Editor::suspend_route_redisplay ()
5449 _routes->suspend_redisplay();
5454 Editor::resume_route_redisplay ()
5457 _routes->redisplay(); // queue redisplay
5458 _routes->resume_redisplay();
5463 Editor::add_vcas (VCAList& vlist)
5467 for (VCAList::iterator v = vlist.begin(); v != vlist.end(); ++v) {
5468 sl.push_back (boost::dynamic_pointer_cast<Stripable> (*v));
5471 add_stripables (sl);
5475 Editor::add_routes (RouteList& rlist)
5479 for (RouteList::iterator r = rlist.begin(); r != rlist.end(); ++r) {
5483 add_stripables (sl);
5487 Editor::add_stripables (StripableList& sl)
5489 list<TimeAxisView*> new_views;
5490 boost::shared_ptr<VCA> v;
5491 boost::shared_ptr<Route> r;
5492 TrackViewList new_selection;
5493 bool from_scratch = (track_views.size() == 0);
5495 sl.sort (Stripable::Sorter());
5497 for (StripableList::iterator s = sl.begin(); s != sl.end(); ++s) {
5499 if ((v = boost::dynamic_pointer_cast<VCA> (*s)) != 0) {
5501 VCATimeAxisView* vtv = new VCATimeAxisView (*this, _session, *_track_canvas);
5503 new_views.push_back (vtv);
5505 } else if ((r = boost::dynamic_pointer_cast<Route> (*s)) != 0) {
5507 if (r->is_auditioner() || r->is_monitor()) {
5511 RouteTimeAxisView* rtv;
5512 DataType dt = r->input()->default_type();
5514 if (dt == ARDOUR::DataType::AUDIO) {
5515 rtv = new AudioTimeAxisView (*this, _session, *_track_canvas);
5517 } else if (dt == ARDOUR::DataType::MIDI) {
5518 rtv = new MidiTimeAxisView (*this, _session, *_track_canvas);
5521 throw unknown_type();
5524 new_views.push_back (rtv);
5525 track_views.push_back (rtv);
5526 new_selection.push_back (rtv);
5528 rtv->effective_gain_display ();
5530 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
5531 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
5535 if (new_views.size() > 0) {
5536 _routes->time_axis_views_added (new_views);
5537 //_summary->routes_added (new_selection); /* XXX requires RouteTimeAxisViewList */
5540 /* note: !new_selection.empty() means that we got some routes rather
5544 if (!from_scratch && !new_selection.empty()) {
5545 selection->set (new_selection);
5546 begin_selection_op_history();
5549 if (show_editor_mixer_when_tracks_arrive && !new_selection.empty()) {
5550 show_editor_mixer (true);
5553 editor_list_button.set_sensitive (true);
5557 Editor::timeaxisview_deleted (TimeAxisView *tv)
5559 if (tv == entered_track) {
5563 if (_session && _session->deletion_in_progress()) {
5564 /* the situation is under control */
5568 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
5570 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
5572 _routes->route_removed (tv);
5574 TimeAxisView::Children c = tv->get_child_list ();
5575 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
5576 if (entered_track == i->get()) {
5581 /* remove it from the list of track views */
5583 TrackViewList::iterator i;
5585 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
5586 i = track_views.erase (i);
5589 /* update whatever the current mixer strip is displaying, if revelant */
5591 boost::shared_ptr<Route> route;
5594 route = rtav->route ();
5597 if (current_mixer_strip && current_mixer_strip->route() == route) {
5599 TimeAxisView* next_tv;
5601 if (track_views.empty()) {
5603 } else if (i == track_views.end()) {
5604 next_tv = track_views.front();
5609 // skip VCAs (cannot be selected, n/a in editor-mixer)
5610 if (dynamic_cast<VCATimeAxisView*> (next_tv)) {
5611 /* VCAs are sorted last in line -- route_sorter.h, jump to top */
5612 next_tv = track_views.front();
5614 if (dynamic_cast<VCATimeAxisView*> (next_tv)) {
5615 /* just in case: no master, only a VCA remains */
5621 set_selected_mixer_strip (*next_tv);
5623 /* make the editor mixer strip go away setting the
5624 * button to inactive (which also unticks the menu option)
5627 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
5633 Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection)
5639 DisplaySuspender ds;
5640 PresentationInfo::ChangeSuspender cs;
5642 if (apply_to_selection) {
5643 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end();) {
5645 TrackSelection::iterator j = i;
5648 hide_track_in_display (*i, false);
5653 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
5655 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
5656 /* this will hide the mixer strip */
5657 set_selected_mixer_strip (*tv);
5660 _routes->hide_track_in_display (*tv);
5665 Editor::show_track_in_display (TimeAxisView* tv, bool move_into_view)
5670 _routes->show_track_in_display (*tv);
5671 if (move_into_view) {
5672 ensure_time_axis_view_is_visible (*tv, false);
5677 Editor::sync_track_view_list_and_routes ()
5679 track_views = TrackViewList (_routes->views ());
5681 _summary->set_background_dirty();
5682 _group_tabs->set_dirty ();
5684 return false; // do not call again (until needed)
5688 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5690 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5695 /** Find a StripableTimeAxisView by the ID of its stripable */
5696 StripableTimeAxisView*
5697 Editor::get_stripable_time_axis_by_id (const PBD::ID& id) const
5699 StripableTimeAxisView* v;
5701 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5702 if((v = dynamic_cast<StripableTimeAxisView*>(*i)) != 0) {
5703 if(v->stripable()->id() == id) {
5713 Editor::fit_route_group (RouteGroup *g)
5715 TrackViewList ts = axis_views_from_routes (g->route_list ());
5720 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5722 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5725 _session->cancel_audition ();
5729 if (_session->is_auditioning()) {
5730 _session->cancel_audition ();
5731 if (r == last_audition_region) {
5736 _session->audition_region (r);
5737 last_audition_region = r;
5742 Editor::hide_a_region (boost::shared_ptr<Region> r)
5744 r->set_hidden (true);
5748 Editor::show_a_region (boost::shared_ptr<Region> r)
5750 r->set_hidden (false);
5754 Editor::audition_region_from_region_list ()
5756 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5760 Editor::hide_region_from_region_list ()
5762 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5766 Editor::show_region_in_region_list ()
5768 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5772 Editor::step_edit_status_change (bool yn)
5775 start_step_editing ();
5777 stop_step_editing ();
5782 Editor::start_step_editing ()
5784 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5788 Editor::stop_step_editing ()
5790 step_edit_connection.disconnect ();
5794 Editor::check_step_edit ()
5796 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5797 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5799 mtv->check_step_edit ();
5803 return true; // do it again, till we stop
5807 Editor::scroll_press (Direction dir)
5809 ++_scroll_callbacks;
5811 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5812 /* delay the first auto-repeat */
5818 scroll_backward (1);
5826 scroll_up_one_track ();
5830 scroll_down_one_track ();
5834 /* do hacky auto-repeat */
5835 if (!_scroll_connection.connected ()) {
5837 _scroll_connection = Glib::signal_timeout().connect (
5838 sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100
5841 _scroll_callbacks = 0;
5848 Editor::scroll_release ()
5850 _scroll_connection.disconnect ();
5853 /** Queue a change for the Editor viewport x origin to follow the playhead */
5855 Editor::reset_x_origin_to_follow_playhead ()
5857 samplepos_t const sample = playhead_cursor->current_sample ();
5859 if (sample < _leftmost_sample || sample > _leftmost_sample + current_page_samples()) {
5861 if (_session->transport_speed() < 0) {
5863 if (sample > (current_page_samples() / 2)) {
5864 center_screen (sample-(current_page_samples()/2));
5866 center_screen (current_page_samples()/2);
5873 if (sample < _leftmost_sample) {
5875 if (_session->transport_rolling()) {
5876 /* rolling; end up with the playhead at the right of the page */
5877 l = sample - current_page_samples ();
5879 /* not rolling: end up with the playhead 1/4 of the way along the page */
5880 l = sample - current_page_samples() / 4;
5884 if (_session->transport_rolling()) {
5885 /* rolling: end up with the playhead on the left of the page */
5888 /* not rolling: end up with the playhead 3/4 of the way along the page */
5889 l = sample - 3 * current_page_samples() / 4;
5897 center_screen_internal (l + (current_page_samples() / 2), current_page_samples ());
5903 Editor::super_rapid_screen_update ()
5905 if (!_session || !_session->engine().running()) {
5909 /* METERING / MIXER STRIPS */
5911 /* update track meters, if required */
5912 if (contents().is_mapped() && meters_running) {
5913 RouteTimeAxisView* rtv;
5914 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5915 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5916 rtv->fast_update ();
5921 /* and any current mixer strip */
5922 if (current_mixer_strip) {
5923 current_mixer_strip->fast_update ();
5926 bool latent_locate = false;
5927 samplepos_t sample = _session->audible_sample (&latent_locate);
5928 const int64_t now = g_get_monotonic_time ();
5931 if (_session->exporting ()) {
5932 /* freewheel/export may be faster or slower than transport_speed() / SR.
5933 * Also exporting multiple ranges locates/jumps without a _pending_locate_request.
5935 _last_update_time = 0;
5938 if (!_session->transport_rolling () || _session->is_auditioning ()) {
5939 /* Do not interpolate the playhead position; just set it */
5940 _last_update_time = 0;
5943 if (_last_update_time > 0) {
5944 /* interpolate and smoothen playhead position */
5945 const double ds = (now - _last_update_time) * _session->transport_speed() * _session->nominal_sample_rate () * 1e-6;
5946 samplepos_t guess = playhead_cursor->current_sample () + rint (ds);
5947 err = sample - guess;
5949 guess += err * .12 + _err_screen_engine; // time-constant based on 25fps (super_rapid_screen_update)
5950 _err_screen_engine += .0144 * (err - _err_screen_engine); // tc^2
5953 printf ("eng: %ld gui:%ld (%+6.1f) diff: %6.1f (err: %7.2f)\n",
5955 err, _err_screen_engine);
5960 _err_screen_engine = 0;
5963 if (err > 8192 || latent_locate) {
5964 // in case of x-runs or freewheeling
5965 _last_update_time = 0;
5966 sample = _session->audible_sample ();
5968 _last_update_time = now;
5971 /* snapped cursor stuff (the snapped_cursor shows where an operation is going to occur) */
5973 MusicSample where (sample, 0);
5974 if (!UIConfiguration::instance().get_show_snapped_cursor()) {
5975 snapped_cursor->hide ();
5976 } else if (_edit_point == EditAtPlayhead && !_dragging_playhead) {
5977 /* EditAtPlayhead does not snap */
5978 } else if (_edit_point == EditAtSelectedMarker) {
5979 /* NOTE: I don't think EditAtSelectedMarker should snap. They are what they are.
5980 * however, the current editing code -does- snap so I'll draw it that way for now.
5982 if (!selection->markers.empty()) {
5983 MusicSample ms (selection->markers.front()->position(), 0);
5984 snap_to (ms); // should use snap_to_with_modifier?
5985 snapped_cursor->set_position (ms.sample);
5986 snapped_cursor->show ();
5988 } else if (mouse_sample (where.sample, ignored)) { // cursor is in the editing canvas. show it.
5989 snapped_cursor->show ();
5990 } else { // mouse is out of the editing canvas. hide the snapped_cursor
5991 snapped_cursor->hide ();
5994 /* There are a few reasons why we might not update the playhead / viewport stuff:
5996 * 1. we don't update things when there's a pending locate request, otherwise
5997 * when the editor requests a locate there is a chance that this method
5998 * will move the playhead before the locate request is processed, causing
6000 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
6001 * 3. if we're still at the same frame that we were last time, there's nothing to do.
6003 if (_pending_locate_request) {
6004 _last_update_time = 0;
6008 if (_dragging_playhead) {
6009 _last_update_time = 0;
6013 if (playhead_cursor->current_sample () == sample) {
6017 playhead_cursor->set_position (sample);
6019 if (_session->requested_return_sample() >= 0) {
6020 _last_update_time = 0;
6024 if (!_follow_playhead || pending_visual_change.being_handled) {
6025 /* We only do this if we aren't already
6026 * handling a visual change (ie if
6027 * pending_visual_change.being_handled is
6028 * false) so that these requests don't stack
6029 * up there are too many of them to handle in
6035 if (!_stationary_playhead) {
6036 reset_x_origin_to_follow_playhead ();
6038 samplepos_t const sample = playhead_cursor->current_sample ();
6039 double target = ((double)sample - (double)current_page_samples() / 2.0);
6040 if (target <= 0.0) {
6043 /* compare to EditorCursor::set_position() */
6044 double const old_pos = sample_to_pixel_unrounded (_leftmost_sample);
6045 double const new_pos = sample_to_pixel_unrounded (target);
6046 if (rint (new_pos) != rint (old_pos)) {
6047 reset_x_origin (pixel_to_sample (new_pos));
6054 Editor::session_going_away ()
6056 _have_idled = false;
6058 _session_connections.drop_connections ();
6060 super_rapid_screen_update_connection.disconnect ();
6062 selection->clear ();
6063 cut_buffer->clear ();
6065 clicked_regionview = 0;
6066 clicked_axisview = 0;
6067 clicked_routeview = 0;
6068 entered_regionview = 0;
6070 _last_update_time = 0;
6073 playhead_cursor->hide ();
6075 /* rip everything out of the list displays */
6079 _route_groups->clear ();
6081 /* do this first so that deleting a track doesn't reset cms to null
6082 and thus cause a leak.
6085 if (current_mixer_strip) {
6086 if (current_mixer_strip->get_parent() != 0) {
6087 global_hpacker.remove (*current_mixer_strip);
6089 delete current_mixer_strip;
6090 current_mixer_strip = 0;
6093 /* delete all trackviews */
6095 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6098 track_views.clear ();
6100 nudge_clock->set_session (0);
6102 editor_list_button.set_active(false);
6103 editor_list_button.set_sensitive(false);
6105 /* clear tempo/meter rulers */
6106 remove_metric_marks ();
6107 clear_marker_display ();
6113 stop_step_editing ();
6117 /* get rid of any existing editor mixer strip */
6119 WindowTitle title(Glib::get_application_name());
6120 title += _("Editor");
6122 own_window()->set_title (title.get_string());
6125 SessionHandlePtr::session_going_away ();
6129 Editor::trigger_script (int i)
6131 LuaInstance::instance()-> call_action (i);
6135 Editor::show_editor_list (bool yn)
6138 _editor_list_vbox.show ();
6140 _editor_list_vbox.hide ();
6145 Editor::change_region_layering_order (bool from_context_menu)
6147 const samplepos_t position = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context_menu);
6149 if (!clicked_routeview) {
6150 if (layering_order_editor) {
6151 layering_order_editor->hide ();
6156 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
6162 boost::shared_ptr<Playlist> pl = track->playlist();
6168 if (layering_order_editor == 0) {
6169 layering_order_editor = new RegionLayeringOrderEditor (*this);
6172 layering_order_editor->set_context (clicked_routeview->name(), _session, clicked_routeview, pl, position);
6173 layering_order_editor->maybe_present ();
6177 Editor::update_region_layering_order_editor ()
6179 if (layering_order_editor && layering_order_editor->is_visible ()) {
6180 change_region_layering_order (true);
6185 Editor::setup_fade_images ()
6187 _xfade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadein-linear")));
6188 _xfade_in_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadein-symmetric")));
6189 _xfade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadein-fast-cut")));
6190 _xfade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadein-slow-cut")));
6191 _xfade_in_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadein-constant-power")));
6193 _xfade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadeout-linear")));
6194 _xfade_out_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-symmetric")));
6195 _xfade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut")));
6196 _xfade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut")));
6197 _xfade_out_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-constant-power")));
6201 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
6203 Editor::action_menu_item (std::string const & name)
6205 Glib::RefPtr<Action> a = editor_actions->get_action (name);
6208 return *manage (a->create_menu_item ());
6212 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
6214 EventBox* b = manage (new EventBox);
6215 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
6216 Label* l = manage (new Label (name));
6220 _the_notebook.append_page (widget, *b);
6224 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
6226 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
6227 _the_notebook.set_current_page (_the_notebook.page_num (*page));
6230 if (ev->type == GDK_2BUTTON_PRESS) {
6232 /* double-click on a notebook tab shrinks or expands the notebook */
6234 if (_notebook_shrunk) {
6235 if (pre_notebook_shrink_pane_width) {
6236 edit_pane.set_divider (0, *pre_notebook_shrink_pane_width);
6238 _notebook_shrunk = false;
6240 pre_notebook_shrink_pane_width = edit_pane.get_divider();
6242 /* this expands the LHS of the edit pane to cover the notebook
6243 PAGE but leaves the tabs visible.
6245 edit_pane.set_divider (0, edit_pane.get_divider() + page->get_width());
6246 _notebook_shrunk = true;
6254 Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
6256 using namespace Menu_Helpers;
6258 MenuList& items = _control_point_context_menu.items ();
6261 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &Editor::edit_control_point), item)));
6262 items.push_back (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun (*this, &Editor::remove_control_point), item)));
6263 if (!can_remove_control_point (item)) {
6264 items.back().set_sensitive (false);
6267 _control_point_context_menu.popup (event->button.button, event->button.time);
6271 Editor::popup_note_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
6273 using namespace Menu_Helpers;
6275 NoteBase* note = reinterpret_cast<NoteBase*>(item->get_data("notebase"));
6280 /* We need to get the selection here and pass it to the operations, since
6281 popping up the menu will cause a region leave event which clears
6282 entered_regionview. */
6284 MidiRegionView& mrv = note->region_view();
6285 const RegionSelection rs = get_regions_from_selection_and_entered ();
6286 const uint32_t sel_size = mrv.selection_size ();
6288 MenuList& items = _note_context_menu.items();
6292 items.push_back(MenuElem(_("Delete"),
6293 sigc::mem_fun(mrv, &MidiRegionView::delete_selection)));
6296 items.push_back(MenuElem(_("Edit..."),
6297 sigc::bind(sigc::mem_fun(*this, &Editor::edit_notes), &mrv)));
6298 if (sel_size != 1) {
6299 items.back().set_sensitive (false);
6302 items.push_back(MenuElem(_("Transpose..."),
6303 sigc::bind(sigc::mem_fun(*this, &Editor::transpose_regions), rs)));
6306 items.push_back(MenuElem(_("Legatize"),
6307 sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, false)));
6309 items.back().set_sensitive (false);
6312 items.push_back(MenuElem(_("Quantize..."),
6313 sigc::bind(sigc::mem_fun(*this, &Editor::quantize_regions), rs)));
6315 items.push_back(MenuElem(_("Remove Overlap"),
6316 sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, true)));
6318 items.back().set_sensitive (false);
6321 items.push_back(MenuElem(_("Transform..."),
6322 sigc::bind(sigc::mem_fun(*this, &Editor::transform_regions), rs)));
6324 _note_context_menu.popup (event->button.button, event->button.time);
6328 Editor::zoom_vertical_modifier_released()
6330 _stepping_axis_view = 0;
6334 Editor::ui_parameter_changed (string parameter)
6336 if (parameter == "icon-set") {
6337 while (!_cursor_stack.empty()) {
6338 _cursor_stack.pop_back();
6340 _cursors->set_cursor_set (UIConfiguration::instance().get_icon_set());
6341 _cursor_stack.push_back(_cursors->grabber);
6342 edit_pane.set_drag_cursor (*_cursors->expand_left_right);
6343 editor_summary_pane.set_drag_cursor (*_cursors->expand_up_down);
6345 } else if (parameter == "draggable-playhead") {
6346 if (_verbose_cursor) {
6347 playhead_cursor->set_sensitive (UIConfiguration::instance().get_draggable_playhead());
6349 } else if (parameter == "use-note-bars-for-velocity") {
6350 ArdourCanvas::Note::set_show_velocity_bars (UIConfiguration::instance().get_use_note_bars_for_velocity());
6351 _track_canvas->request_redraw (_track_canvas->visible_area());
6352 } else if (parameter == "use-note-color-for-velocity") {
6353 /* handled individually by each MidiRegionView */
6358 Editor::use_own_window (bool and_fill_it)
6360 bool new_window = !own_window();
6362 Gtk::Window* win = Tabbable::use_own_window (and_fill_it);
6364 if (win && new_window) {
6365 win->set_name ("EditorWindow");
6367 ARDOUR_UI::instance()->setup_toplevel_window (*win, _("Editor"), this);
6369 // win->signal_realize().connect (*this, &Editor::on_realize);
6370 win->signal_event().connect (sigc::bind (sigc::ptr_fun (&Keyboard::catch_user_event_for_pre_dialog_focus), win));
6371 win->signal_event().connect (sigc::mem_fun (*this, &Editor::generic_event_handler));
6372 win->set_data ("ardour-bindings", bindings);
6377 DisplaySuspender ds;
6378 contents().show_all ();
6380 /* XXX: this is a bit unfortunate; it would probably
6381 be nicer if we could just call show () above rather
6382 than needing the show_all ()
6385 /* re-hide stuff if necessary */
6386 editor_list_button_toggled ();
6387 parameter_changed ("show-summary");
6388 parameter_changed ("show-group-tabs");
6389 parameter_changed ("show-zoom-tools");
6391 /* now reset all audio_time_axis heights, because widgets might need
6397 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6398 tv = (static_cast<TimeAxisView*>(*i));
6399 tv->reset_height ();
6402 if (current_mixer_strip) {
6403 current_mixer_strip->hide_things ();
6404 current_mixer_strip->parameter_changed ("mixer-element-visibility");