X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor.cc;h=446965588d25a9e566b028f1ca279c0fe555117d;hb=eb15d37669f7b82b453bb992f253bfe6ee3e7f9e;hp=aebaa7fe8313da67786c63c89ca4eca1b527ac23;hpb=dd8cd579fc74e8affaadef77eb05588612c1535e;p=ardour.git diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index aebaa7fe83..b36833aff9 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -57,28 +57,34 @@ #include #include "gtkmm2ext/bindings.h" -#include "gtkmm2ext/grouped_buttons.h" #include "gtkmm2ext/gtk_ui.h" -#include "gtkmm2ext/tearoff.h" +#include "gtkmm2ext/keyboard.h" #include "gtkmm2ext/utils.h" #include "gtkmm2ext/window_title.h" -#include "gtkmm2ext/choice.h" #include "gtkmm2ext/cell_renderer_pixbuf_toggle.h" +#include "ardour/analysis_graph.h" #include "ardour/audio_track.h" #include "ardour/audioengine.h" #include "ardour/audioregion.h" #include "ardour/lmath.h" #include "ardour/location.h" #include "ardour/profile.h" +#include "ardour/route.h" #include "ardour/route_group.h" #include "ardour/session_playlists.h" #include "ardour/tempo.h" #include "ardour/utils.h" +#include "ardour/vca_manager.h" +#include "ardour/vca.h" #include "canvas/debug.h" #include "canvas/text.h" +#include "widgets/ardour_spacer.h" +#include "widgets/eventboxext.h" +#include "widgets/tooltips.h" + #include "control_protocol/control_protocol.h" #include "actions.h" @@ -92,6 +98,7 @@ #include "crossfade_edit.h" #include "debug.h" #include "editing.h" +#include "editing_convert.h" #include "editor.h" #include "editor_cursors.h" #include "editor_drag.h" @@ -102,10 +109,13 @@ #include "editor_routes.h" #include "editor_snapshots.h" #include "editor_summary.h" +#include "enums_convert.h" +#include "export_report.h" #include "global_port_matrix.h" #include "gui_object.h" #include "gui_thread.h" #include "keyboard.h" +#include "luainstance.h" #include "marker.h" #include "midi_region_view.h" #include "midi_time_axis.h" @@ -119,20 +129,24 @@ #include "region_layering_order_editor.h" #include "rgb_macros.h" #include "rhythm_ferret.h" +#include "route_sorter.h" #include "selection.h" +#include "simple_progress_dialog.h" #include "sfdb_ui.h" -#include "tempo_lines.h" +#include "grid_lines.h" #include "time_axis_view.h" +#include "time_info_box.h" #include "timers.h" -#include "tooltips.h" #include "ui_config.h" #include "utils.h" +#include "vca_time_axis.h" #include "verbose_cursor.h" -#include "i18n.h" +#include "pbd/i18n.h" using namespace std; using namespace ARDOUR; +using namespace ArdourWidgets; using namespace ARDOUR_UI_UTILS; using namespace PBD; using namespace Gtk; @@ -146,44 +160,28 @@ using Gtkmm2ext::Keyboard; double Editor::timebar_height = 15.0; -static const gchar *_snap_type_strings[] = { - N_("CD Frames"), - N_("TC Frames"), - N_("TC Seconds"), - N_("TC Minutes"), - N_("Seconds"), - N_("Minutes"), - N_("Beats/128"), - N_("Beats/64"), - N_("Beats/32"), - N_("Beats/28"), - N_("Beats/24"), - N_("Beats/20"), - N_("Beats/16"), - N_("Beats/14"), - N_("Beats/12"), - N_("Beats/10"), - N_("Beats/8"), - N_("Beats/7"), - N_("Beats/6"), - N_("Beats/5"), - N_("Beats/4"), - N_("Beats/3"), - N_("Beats/2"), - N_("Beats"), - N_("Bars"), - N_("Marks"), - N_("Region starts"), - N_("Region ends"), - N_("Region syncs"), - N_("Region bounds"), - 0 -}; - -static const gchar *_snap_mode_strings[] = { +static const gchar *_grid_type_strings[] = { N_("No Grid"), - N_("Grid"), - N_("Magnetic"), + N_("Bar"), + N_("1/4 Note"), + N_("1/8 Note"), + N_("1/16 Note"), + N_("1/32 Note"), + N_("1/64 Note"), + N_("1/128 Note"), + N_("1/3 (8th triplet)"), // or "1/12" ? + N_("1/6 (16th triplet)"), + N_("1/12 (32nd triplet)"), + N_("1/24 (64th triplet)"), + N_("1/5 (8th quintuplet)"), + N_("1/10 (16th quintuplet)"), + N_("1/20 (32nd quintuplet)"), + N_("1/7 (8th septuplet)"), + N_("1/14 (16th septuplet)"), + N_("1/28 (32nd septuplet)"), + N_("Timecode"), + N_("MinSec"), + N_("Samples"), 0 }; @@ -207,8 +205,8 @@ static const gchar *_zoom_focus_strings[] = { N_("Right"), N_("Center"), N_("Playhead"), - N_("Mouse"), - N_("Edit point"), + N_("Mouse"), + N_("Edit point"), 0 }; @@ -225,45 +223,27 @@ static const gchar *_rb_opt_strings[] = { }; #endif -#define COMBO_TRIANGLE_WIDTH 25 // ArdourButton _diameter (11) + 2 * arrow-padding (2*2) + 2 * text-padding (2*5) - -static void -pane_size_watcher (Paned* pane) -{ - /* if the handle of a pane vanishes into (at least) the tabs of a notebook, - it is: - - X: hard to access - Quartz: impossible to access - - so stop that by preventing it from ever getting too narrow. 35 - pixels is basically a rough guess at the tab width. - - ugh. - */ - - int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 35; - - gint pos = pane->get_position (); - - if (pos > max_width_of_lhs) { - pane->set_position (max_width_of_lhs); - } -} +//Robin says: this should be odd to accomodate cairo drawing offset ( width/2 rounds up to pixel boundary ) +#ifdef __APPLE__ +#define COMBO_TRIANGLE_WIDTH 19 // ArdourButton _diameter (11) + 2 * arrow-padding (2*2) + 2 * text-padding (2*5) +#else +#define COMBO_TRIANGLE_WIDTH 11 // as-measured for win/linux. +#endif Editor::Editor () : PublicEditor (global_hpacker) , editor_mixer_strip_width (Wide) , constructed (false) , _playlist_selector (0) + , _time_info_box (0) , no_save_visual (false) - , leftmost_frame (0) + , _leftmost_sample (0) , samples_per_pixel (2048) , zoom_focus (ZoomFocusPlayhead) , mouse_mode (MouseObject) - , pre_internal_snap_type (SnapToBeat) + , pre_internal_grid_type (GridTypeBeat) , pre_internal_snap_mode (SnapOff) - , internal_snap_type (SnapToBeat) + , internal_grid_type (GridTypeBeat) , internal_snap_mode (SnapOff) , _join_object_range_state (JOIN_OBJECT_RANGE_NONE) , _notebook_shrunk (false) @@ -291,7 +271,6 @@ Editor::Editor () , _track_canvas_viewport (0) , within_track_canvas (false) , _verbose_cursor (0) - , logo_item (0) , tempo_group (0) , meter_group (0) , marker_group (0) @@ -312,9 +291,11 @@ Editor::Editor () , minsec_mark_interval (0) , minsec_mark_modulo (0) , minsec_nmarks (0) + , timecode_ruler_scale (timecode_show_many_hours) , timecode_mark_modulo (0) , timecode_nmarks (0) , _samples_ruler_interval (0) + , bbt_ruler_scale (bbt_show_many) , bbt_bars (0) , bbt_nmarks (0) , bbt_bar_helper_on (0) @@ -343,7 +324,9 @@ Editor::Editor () , cd_mark_label (_("CD Markers")) , videotl_label (_("Video Timeline")) , videotl_group (0) + , snapped_cursor (0) , playhead_cursor (0) + , _region_boundary_cache_dirty (true) , edit_packer (4, 4, true) , vertical_adjustment (0.0, 0.0, 10.0, 400.0) , horizontal_adjustment (0.0, 0.0, 1e16) @@ -355,11 +338,13 @@ Editor::Editor () , _full_canvas_height (0) , edit_controls_left_menu (0) , edit_controls_right_menu (0) - , last_update_frame (0) + , visual_change_queued(false) + , _last_update_time (0) + , _err_screen_engine (0) , cut_buffer_start (0) , cut_buffer_length (0) , button_bindings (0) - , last_paste_pos (0) + , last_paste_pos (-1) , paste_count (0) , sfbrowser (0) , current_interthread_info (0) @@ -371,23 +356,22 @@ Editor::Editor () , scrub_reverse_distance (0) , have_pending_keyboard_selection (false) , pending_keyboard_selection_start (0) - , _snap_type (SnapToBeat) + , _grid_type (GridTypeBeat) , _snap_mode (SnapOff) - , snap_threshold (5.0) , ignore_gui_changes (false) , _drags (new DragManager (this)) , lock_dialog (0) /* , last_event_time { 0, 0 } */ /* this initialization style requires C++11 */ , _dragging_playhead (false) , _dragging_edit_point (false) - , _show_measures (true) , _follow_playhead (true) , _stationary_playhead (false) , _maximised (false) - , tempo_lines (0) + , grid_lines (0) , global_rect_group (0) , time_line_group (0) - , tempo_or_meter_marker_menu (0) + , tempo_marker_menu (0) + , meter_marker_menu (0) , marker_menu (0) , range_marker_menu (0) , transport_marker_menu (0) @@ -397,18 +381,15 @@ Editor::Editor () , bbt_beat_subdivision (4) , _visible_track_count (-1) , toolbar_selection_clock_table (2,3) - , _mouse_mode_tearoff (0) , automation_mode_button (_("mode")) - , _zoom_tearoff (0) - , _tools_tearoff (0) - , _toolbar_viewport (*manage (new Gtk::Adjustment (0, 0, 1e10)), *manage (new Gtk::Adjustment (0, 0, 1e10))) - , selection (new Selection (this)) - , cut_buffer (new Selection (this)) + , selection (new Selection (this, true)) + , cut_buffer (new Selection (this, false)) , _selection_memento (new SelectionMemento()) , _all_region_actions_sensitized (false) , _ignore_region_action (false) , _last_region_menu_was_main (false) - , _ignore_follow_edits (false) + , _track_selection_change_without_scroll (false) + , _editor_track_selection_change_without_scroll (false) , cd_marker_bar_drag_rect (0) , range_bar_drag_rect (0) , transport_bar_drag_rect (0) @@ -435,6 +416,7 @@ Editor::Editor () , show_gain_after_trim (false) , selection_op_cmd_depth (0) , selection_op_history_it (0) + , no_save_instant (false) , current_timefx (0) , current_mixer_strip (0) , show_editor_mixer_when_tracks_arrive (false) @@ -465,6 +447,7 @@ Editor::Editor () , _stepping_axis_view (0) , quantize_dialog (0) , _main_menu_disabler (0) + , myactions (X_("editor")) { /* we are a singleton */ @@ -475,13 +458,10 @@ Editor::Editor () last_event_time.tv_sec = 0; last_event_time.tv_usec = 0; - global_hpacker.set_data ("ardour-bindings", &key_bindings); - selection_op_history.clear(); before.clear(); - snap_type_strings = I18N (_snap_type_strings); - snap_mode_strings = I18N (_snap_mode_strings); + grid_type_strings = I18N (_grid_type_strings); zoom_focus_strings = I18N (_zoom_focus_strings); edit_mode_strings = I18N (_edit_mode_strings); edit_point_strings = I18N (_edit_point_strings); @@ -493,20 +473,20 @@ Editor::Editor () build_edit_mode_menu(); build_zoom_focus_menu(); build_track_count_menu(); - build_snap_mode_menu(); - build_snap_type_menu(); + build_grid_type_menu(); build_edit_point_menu(); - location_marker_color = ARDOUR_UI::config()->color ("location marker"); - location_range_color = ARDOUR_UI::config()->color ("location range"); - location_cd_marker_color = ARDOUR_UI::config()->color ("location cd marker"); - location_loop_color = ARDOUR_UI::config()->color ("location loop"); - location_punch_color = ARDOUR_UI::config()->color ("location punch"); + location_marker_color = UIConfiguration::instance().color ("location marker"); + location_range_color = UIConfiguration::instance().color ("location range"); + location_cd_marker_color = UIConfiguration::instance().color ("location cd marker"); + location_loop_color = UIConfiguration::instance().color ("location loop"); + location_punch_color = UIConfiguration::instance().color ("location punch"); - timebar_height = std::max(12., ceil (15. * ARDOUR_UI::ui_scale)); + timebar_height = std::max (12., ceil (15. * UIConfiguration::instance().get_ui_scale())); TimeAxisView::setup_sizes (); ArdourMarker::setup_sizes (timebar_height); + TempoCurve::setup_sizes (timebar_height); bbt_label.set_name ("EditorRulerLabel"); bbt_label.set_size_request (-1, (int)timebar_height); @@ -588,7 +568,7 @@ Editor::Editor () initialize_canvas (); - CairoWidget::set_focus_handler (sigc::mem_fun (*this, &Editor::reset_focus)); + CairoWidget::set_focus_handler (sigc::mem_fun (ARDOUR_UI::instance(), &ARDOUR_UI::reset_focus)); _summary = new EditorSummary (this); @@ -644,21 +624,31 @@ Editor::Editor () time_bars_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK); time_bars_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release)); + ArdourWidgets::ArdourDropShadow *axis_view_shadow = manage (new (ArdourWidgets::ArdourDropShadow)); + axis_view_shadow->set_size_request( 4, -1 ); + axis_view_shadow->set_name("EditorWindow"); + axis_view_shadow->show(); + + edit_packer.attach (*axis_view_shadow, 0, 1, 0, 2, FILL, FILL|EXPAND, 0, 0); + /* labels for the time bars */ - edit_packer.attach (time_bars_event_box, 0, 1, 0, 1, FILL, SHRINK, 0, 0); + edit_packer.attach (time_bars_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0); /* track controls */ - edit_packer.attach (controls_layout, 0, 1, 1, 2, FILL, FILL|EXPAND, 0, 0); + edit_packer.attach (controls_layout, 1, 2, 1, 2, FILL, FILL|EXPAND, 0, 0); /* canvas */ - edit_packer.attach (*_track_canvas_viewport, 1, 2, 0, 2, FILL|EXPAND, FILL|EXPAND, 0, 0); + edit_packer.attach (*_track_canvas_viewport, 2, 3, 0, 2, FILL|EXPAND, FILL|EXPAND, 0, 0); bottom_hbox.set_border_width (2); bottom_hbox.set_spacing (3); + PresentationInfo::Change.connect (*this, MISSING_INVALIDATOR, boost::bind (&Editor::presentation_info_changed, this, _1), gui_context()); + _route_groups = new EditorRouteGroups (this); _routes = new EditorRoutes (this); _regions = new EditorRegions (this); _snapshots = new EditorSnapshots (this); _locations = new EditorLocations (this); + _time_info_box = new TimeInfoBox ("EditorTimeInfo", true); /* these are static location signals */ @@ -680,79 +670,111 @@ Editor::Editor () _notebook_shrunk = false; - editor_summary_pane.pack1(edit_packer); - Button* summary_arrows_left_left = manage (new Button); - summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE))); - summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), LEFT))); - summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release)); + /* Pick up some settings we need to cache, early */ - Button* summary_arrows_left_right = manage (new Button); - summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE))); - summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), RIGHT))); - summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release)); + XMLNode* settings = ARDOUR_UI::instance()->editor_settings(); - VBox* summary_arrows_left = manage (new VBox); - summary_arrows_left->pack_start (*summary_arrows_left_left); - summary_arrows_left->pack_start (*summary_arrows_left_right); + if (settings) { + settings->get_property ("notebook-shrunk", _notebook_shrunk); + } - Button* summary_arrows_right_up = manage (new Button); - summary_arrows_right_up->add (*manage (new Arrow (ARROW_UP, SHADOW_NONE))); - summary_arrows_right_up->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), UP))); - summary_arrows_right_up->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release)); + editor_summary_pane.set_check_divider_position (true); + editor_summary_pane.add (edit_packer); - Button* summary_arrows_right_down = manage (new Button); - summary_arrows_right_down->add (*manage (new Arrow (ARROW_DOWN, SHADOW_NONE))); - summary_arrows_right_down->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), DOWN))); - summary_arrows_right_down->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release)); + Button* summary_arrow_left = manage (new Button); + summary_arrow_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE))); + summary_arrow_left->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), LEFT))); + summary_arrow_left->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release)); + + Button* summary_arrow_right = manage (new Button); + summary_arrow_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE))); + summary_arrow_right->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), RIGHT))); + summary_arrow_right->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release)); + + VBox* summary_arrows_left = manage (new VBox); + summary_arrows_left->pack_start (*summary_arrow_left); VBox* summary_arrows_right = manage (new VBox); - summary_arrows_right->pack_start (*summary_arrows_right_up); - summary_arrows_right->pack_start (*summary_arrows_right_down); + summary_arrows_right->pack_start (*summary_arrow_right); - Frame* summary_frame = manage (new Frame); - summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN); + Frame* summary_sample = manage (new Frame); + summary_sample->set_shadow_type (Gtk::SHADOW_ETCHED_IN); - summary_frame->add (*_summary); - summary_frame->show (); + summary_sample->add (*_summary); + summary_sample->show (); _summary_hbox.pack_start (*summary_arrows_left, false, false); - _summary_hbox.pack_start (*summary_frame, true, true); + _summary_hbox.pack_start (*summary_sample, true, true); _summary_hbox.pack_start (*summary_arrows_right, false, false); if (!ARDOUR::Profile->get_trx()) { - editor_summary_pane.pack2 (_summary_hbox); + editor_summary_pane.add (_summary_hbox); } - edit_pane.pack1 (editor_summary_pane, true, true); + edit_pane.set_check_divider_position (true); + edit_pane.add (editor_summary_pane); if (!ARDOUR::Profile->get_trx()) { - edit_pane.pack2 (_the_notebook, false, true); + _editor_list_vbox.pack_start (*_time_info_box, false, false, 0); + _editor_list_vbox.pack_start (_the_notebook); + edit_pane.add (_editor_list_vbox); + edit_pane.set_child_minsize (_editor_list_vbox, 30); /* rough guess at width of notebook tabs */ + } + + edit_pane.set_drag_cursor (*_cursors->expand_left_right); + editor_summary_pane.set_drag_cursor (*_cursors->expand_up_down); + + float fract; + if (!settings || !settings->get_property ("edit-horizontal-pane-pos", fract) || fract > 1.0) { + /* initial allocation is 90% to canvas, 10% to notebook */ + fract = 0.90; + } + edit_pane.set_divider (0, fract); + + if (!settings || !settings->get_property ("edit-vertical-pane-pos", fract) || fract > 1.0) { + /* initial allocation is 90% to canvas, 10% to summary */ + fract = 0.90; } + editor_summary_pane.set_divider (0, fract); - editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast (&editor_summary_pane))); + global_vpacker.set_spacing (0); + global_vpacker.set_border_width (0); - /* XXX: editor_summary_pane might need similar to the edit_pane */ + //the next three EventBoxes provide the ability for their child widgets to have a background color. That is all. - edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast (&edit_pane))); + Gtk::EventBox* ebox = manage (new Gtk::EventBox); //a themeable box + ebox->set_name("EditorWindow"); + ebox->add (ebox_hpacker); - Glib::PropertyProxy proxy = edit_pane.property_position(); - proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast (&edit_pane))); + Gtk::EventBox* epane_box = manage (new EventBoxExt); //a themeable box + epane_box->set_name("EditorWindow"); + epane_box->add (edit_pane); - top_hbox.pack_start (toolbar_frame); + Gtk::EventBox* epane_box2 = manage (new EventBoxExt); //a themeable box + epane_box2->set_name("EditorWindow"); + epane_box2->add (global_vpacker); - HBox *hbox = manage (new HBox); - hbox->pack_start (edit_pane, true, true); + ArdourWidgets::ArdourDropShadow *toolbar_shadow = manage (new (ArdourWidgets::ArdourDropShadow)); + toolbar_shadow->set_size_request( -1, 4 ); + toolbar_shadow->set_mode(ArdourWidgets::ArdourDropShadow::DropShadowBoth); + toolbar_shadow->set_name("EditorWindow"); + toolbar_shadow->show(); - global_vpacker.pack_start (top_hbox, false, false); - global_vpacker.pack_start (*hbox, true, true); - global_hpacker.pack_start (global_vpacker, true, true); + global_vpacker.pack_start (*toolbar_shadow, false, false); + global_vpacker.pack_start (*ebox, false, false); + global_vpacker.pack_start (*epane_box, true, true); + global_hpacker.pack_start (*epane_box2, true, true); + + /* need to show the "contents" widget so that notebook will show if tab is switched to + */ + + global_hpacker.show (); + ebox_hpacker.show(); + ebox->show(); /* register actions now so that set_state() can find them and set toggles/checks etc */ register_actions (); - /* when we start using our own keybinding system for the editor, this - * will be uncommented - */ load_bindings (); setup_toolbar (); @@ -791,14 +813,18 @@ Editor::Editor () ControlProtocol::VerticalZoomInSelected.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_in_selected, this), gui_context()); ControlProtocol::VerticalZoomOutSelected.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_out_selected, this), gui_context()); - ControlProtocol::AddRouteToSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Add), gui_context()); - ControlProtocol::RemoveRouteFromSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Toggle), gui_context()); - ControlProtocol::SetRouteSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Set), gui_context()); - ControlProtocol::ToggleRouteSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Toggle), gui_context()); - ControlProtocol::ClearRouteSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_unselect, this), gui_context()); + ControlProtocol::AddStripableToSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Add), gui_context()); + ControlProtocol::RemoveStripableFromSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Toggle), gui_context()); + ControlProtocol::SetStripableSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Set), gui_context()); + ControlProtocol::ToggleStripableSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Toggle), gui_context()); + ControlProtocol::ClearStripableSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_unselect, this), gui_context()); BasicUI::AccessAction.connect (*this, invalidator (*this), boost::bind (&Editor::access_action, this, _1, _2), gui_context()); + /* handle escape */ + + ARDOUR_UI::instance()->Escape.connect (*this, invalidator (*this), boost::bind (&Editor::escape, this), gui_context()); + /* problematic: has to return a value and thus cannot be x-thread */ Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1)); @@ -812,20 +838,18 @@ Editor::Editor () _last_region_menu_was_main = false; _popup_region_menu_item = 0; - _ignore_follow_edits = false; - _show_marker_lines = false; - /* Button bindings */ + /* Button bindings */ - button_bindings = new Bindings; + button_bindings = new Bindings ("editor-mouse"); XMLNode* node = button_settings(); - if (node) { - for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) { - button_bindings->load (**i); - } - } + if (node) { + for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) { + button_bindings->load_operation (**i); + } + } constructed = true; @@ -835,17 +859,20 @@ Editor::Editor () setup_fade_images (); + set_grid_to (GridTypeNone); + instant_save (); } Editor::~Editor() { - delete button_bindings; + delete button_bindings; delete _routes; delete _route_groups; delete _track_canvas_viewport; delete _drags; delete nudge_clock; + delete _verbose_cursor; delete quantize_dialog; delete _summary; delete _group_tabs; @@ -853,10 +880,22 @@ Editor::~Editor() delete _snapshots; delete _locations; delete _playlist_selector; + delete _time_info_box; + delete selection; + delete cut_buffer; + delete _cursors; + + LuaInstance::destroy_instance (); for (list::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) { delete *i; } + for (std::map::const_iterator i = _xfade_in_images.begin(); i != _xfade_in_images.end (); ++i) { + delete i->second; + } + for (std::map::const_iterator i = _xfade_out_images.begin(); i != _xfade_out_images.end (); ++i) { + delete i->second; + } } XMLNode* @@ -875,7 +914,7 @@ Editor::button_settings () const bool Editor::get_smart_mode () const { - return ((current_mouse_mode() == Editing::MouseObject) && smart_mode_action->get_active()); + return ((current_mouse_mode() == MouseObject) && smart_mode_action->get_active()); } void @@ -944,7 +983,7 @@ Editor::set_entered_track (TimeAxisView* tav) void Editor::instant_save () { - if (!constructed || !ARDOUR_UI::instance()->session_loaded) { + if (!constructed || !ARDOUR_UI::instance()->session_loaded || no_save_instant) { return; } @@ -992,23 +1031,9 @@ Editor::control_unselect () } void -Editor::control_select (uint32_t rid, Selection::Operation op) +Editor::control_select (boost::shared_ptr s, Selection::Operation op) { - /* handles the (static) signal from the ControlProtocol class that - * requests setting the selected track to a given RID - */ - - if (!_session) { - return; - } - - boost::shared_ptr r = _session->route_by_remote_id (rid); - - if (!r) { - return; - } - - TimeAxisView* tav = axis_view_from_route (r); + TimeAxisView* tav = time_axis_view_from_stripable (s); if (tav) { switch (op) { @@ -1055,25 +1080,25 @@ Editor::control_scroll (float fraction) /* _control_scroll_target is an optional - it acts like a pointer to an framepos_t, with + it acts like a pointer to an samplepos_t, with a operator conversion to boolean to check that it has a value could possibly use - playhead_cursor->current_frame to store the + playhead_cursor->current_sample to store the value and a boolean in the class to know when it's out of date */ if (!_control_scroll_target) { - _control_scroll_target = _session->transport_frame(); + _control_scroll_target = _session->transport_sample(); _dragging_playhead = true; } - if ((fraction < 0.0f) && (*_control_scroll_target <= (framepos_t) fabs(step))) { + if ((fraction < 0.0f) && (*_control_scroll_target <= (samplepos_t) fabs(step))) { *_control_scroll_target = 0; - } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) { - *_control_scroll_target = max_framepos - (current_page_samples()*2); // allow room for slop in where the PH is on the screen + } else if ((fraction > 0.0f) && (max_samplepos - *_control_scroll_target < step)) { + *_control_scroll_target = max_samplepos - (current_page_samples()*2); // allow room for slop in where the PH is on the screen } else { - *_control_scroll_target += (framepos_t) trunc (step); + *_control_scroll_target += (samplepos_t) trunc (step); } /* move visuals, we'll catch up with it later */ @@ -1104,7 +1129,7 @@ Editor::control_scroll (float fraction) } bool -Editor::deferred_control_scroll (framepos_t /*target*/) +Editor::deferred_control_scroll (samplepos_t /*target*/) { _session->request_locate (*_control_scroll_target, _session->transport_rolling()); // reset for next stream @@ -1114,7 +1139,7 @@ Editor::deferred_control_scroll (framepos_t /*target*/) } void -Editor::access_action (std::string action_group, std::string action_item) +Editor::access_action (const std::string& action_group, const std::string& action_item) { if (!_session) { return; @@ -1130,6 +1155,12 @@ Editor::access_action (std::string action_group, std::string action_item) } } +void +Editor::set_toggleaction (const std::string& action_group, const std::string& action_item, bool s) +{ + ActionManager::set_toggleaction_state (action_group.c_str(), action_item.c_str(), s); +} + void Editor::on_realize () { @@ -1174,7 +1205,7 @@ Editor::generic_event_handler (GdkEvent* ev) /* leaving window, so reset focus, thus ending any and all text entry operations. */ - reset_focus(); + ARDOUR_UI::instance()->reset_focus (&contents()); break; } break; @@ -1210,46 +1241,46 @@ Editor::lock_timeout_callback () } void -Editor::map_position_change (framepos_t frame) +Editor::map_position_change (samplepos_t sample) { - ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame) + ENSURE_GUI_THREAD (*this, &Editor::map_position_change, sample) if (_session == 0) { return; } if (_follow_playhead) { - center_screen (frame); + center_screen (sample); } - playhead_cursor->set_position (frame); + playhead_cursor->set_position (sample); } void -Editor::center_screen (framepos_t frame) +Editor::center_screen (samplepos_t sample) { - framecnt_t const page = _visible_canvas_width * samples_per_pixel; + samplecnt_t const page = _visible_canvas_width * samples_per_pixel; /* if we're off the page, then scroll. */ - if (frame < leftmost_frame || frame >= leftmost_frame + page) { - center_screen_internal (frame, page); + if (sample < _leftmost_sample || sample >= _leftmost_sample + page) { + center_screen_internal (sample, page); } } void -Editor::center_screen_internal (framepos_t frame, float page) +Editor::center_screen_internal (samplepos_t sample, float page) { page /= 2; - if (frame > page) { - frame -= (framepos_t) page; + if (sample > page) { + sample -= (samplepos_t) page; } else { - frame = 0; + sample = 0; } - reset_x_origin (frame); + reset_x_origin (sample); } @@ -1261,7 +1292,7 @@ Editor::update_title () if (!own_window()) { return; } - + if (_session) { bool dirty = _session->dirty(); @@ -1278,6 +1309,7 @@ Editor::update_title () } WindowTitle title(session_name); + title += S_("Window|Editor"); title += Glib::get_application_name(); own_window()->set_title (title.get_string()); } else { @@ -1294,6 +1326,10 @@ Editor::set_session (Session *t) return; } + //initialize _leftmost_sample to the extents of the session + //this prevents a bogus setting of leftmost = "0" if the summary view asks for the leftmost sample before the visible state has been loaded from instant.xml + _leftmost_sample = session_gui_extents().first; + _playlist_selector->set_session (_session); nudge_clock->set_session (_session); _summary->set_session (_session); @@ -1303,6 +1339,7 @@ Editor::set_session (Session *t) _snapshots->set_session (_session); _routes->set_session (_session); _locations->set_session (_session); + _time_info_box->set_session (_session); if (rhythm_ferret) { rhythm_ferret->set_session (_session); @@ -1339,9 +1376,15 @@ Editor::set_session (Session *t) XMLNode* node = ARDOUR_UI::instance()->editor_settings(); set_state (*node, Stateful::loading_state_version); + /* catch up on selection state, etc. */ + + PropertyChange sc; + sc.add (Properties::selected); + presentation_info_changed (sc); + /* catch up with the playhead */ - _session->request_locate (playhead_cursor->current_frame ()); + _session->request_locate (playhead_cursor->current_sample ()); _pending_initial_locate = true; update_title (); @@ -1354,10 +1397,13 @@ Editor::set_session (Session *t) _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::step_edit_status_change, this, _1), gui_context()); _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context()); + _session->TransportLooped.connect (_session_connections, invalidator (*this), boost::bind (&Editor::transport_looped, this), gui_context()); _session->PositionChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_position_change, this, _1), gui_context()); + _session->vca_manager().VCAAdded.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_vcas, this, _1), gui_context()); _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_routes, this, _1), gui_context()); _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context()); _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::tempo_map_changed, this, _1), gui_context()); + _session->tempo_map().MetricPositionChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::tempometric_position_changed, this, _1), gui_context()); _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context()); _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::parameter_changed, this, _1), gui_context()); _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Editor::session_state_saved, this, _1), gui_context()); @@ -1366,8 +1412,13 @@ Editor::set_session (Session *t) _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context()); _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context()); + playhead_cursor->track_canvas_item().reparent ((ArdourCanvas::Item*) get_cursor_scroll_group()); playhead_cursor->show (); + snapped_cursor->track_canvas_item().reparent ((ArdourCanvas::Item*) get_cursor_scroll_group()); + snapped_cursor->set_color (UIConfiguration::instance().color ("edit point")); + snapped_cursor->show (); + boost::function pc (boost::bind (&Editor::parameter_changed, this, _1)); Config->map_parameters (pc); _session->config.map_parameters (pc); @@ -1384,44 +1435,15 @@ Editor::set_session (Session *t) sigc::mem_fun (*this, &Editor::super_rapid_screen_update) ); - switch (_snap_type) { - case SnapToRegionStart: - case SnapToRegionEnd: - case SnapToRegionSync: - case SnapToRegionBoundary: - build_region_boundary_cache (); - break; - - default: - break; - } - /* register for undo history */ _session->register_with_memento_command_factory(id(), this); _session->register_with_memento_command_factory(_selection_memento->id(), _selection_memento); - ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated)); + LuaInstance::instance()->set_session(_session); start_updating_meters (); } -void -Editor::action_pre_activated (Glib::RefPtr const & a) -{ - if (a->get_name() == "RegionMenu") { - /* When the main menu's region menu is opened, we setup the actions so that they look right - in the menu. I can't find a way of getting a signal when this menu is subsequently closed, - so we resensitize all region actions when the entered regionview or the region selection - changes. HOWEVER we can't always resensitize on entered_regionview change because that - happens after the region context menu is opened. So we set a flag here, too. - - What a carry on :( - */ - sensitize_the_right_region_actions (); - _last_region_menu_was_main = true; - } -} - void Editor::fill_xfade_menu (Menu_Helpers::MenuList& items, bool start) { @@ -1649,7 +1671,7 @@ Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, /* When the region menu is opened, we setup the actions so that they look right in the menu. */ - sensitize_the_right_region_actions (); + sensitize_the_right_region_actions (false); _last_region_menu_was_main = false; menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true)); @@ -1661,7 +1683,7 @@ Editor::build_track_context_menu () { using namespace Menu_Helpers; - MenuList& edit_items = track_context_menu.items(); + MenuList& edit_items = track_context_menu.items(); edit_items.clear(); add_dstream_context_items (edit_items); @@ -1673,7 +1695,7 @@ Editor::build_track_bus_context_menu () { using namespace Menu_Helpers; - MenuList& edit_items = track_context_menu.items(); + MenuList& edit_items = track_context_menu.items(); edit_items.clear(); add_bus_context_items (edit_items); @@ -1710,7 +1732,103 @@ Editor::build_track_region_context_menu () } void -Editor::analyze_region_selection () +Editor::loudness_analyze_region_selection () +{ + if (!_session) { + return; + } + Selection& s (PublicEditor::instance ().get_selection ()); + RegionSelection ars = s.regions; + ARDOUR::AnalysisGraph ag (_session); + samplecnt_t total_work = 0; + + for (RegionSelection::iterator j = ars.begin (); j != ars.end (); ++j) { + AudioRegionView* arv = dynamic_cast (*j); + if (!arv) { + continue; + } + if (!boost::dynamic_pointer_cast (arv->region ())) { + continue; + } + assert (dynamic_cast (&arv->get_time_axis_view ())); + total_work += arv->region ()->length (); + } + + SimpleProgressDialog spd (_("Region Loudness Analysis"), sigc::mem_fun (ag, &AnalysisGraph::cancel)); + ScopedConnection c; + ag.set_total_samples (total_work); + ag.Progress.connect_same_thread (c, boost::bind (&SimpleProgressDialog::update_progress, &spd, _1, _2)); + spd.show(); + + for (RegionSelection::iterator j = ars.begin (); j != ars.end (); ++j) { + AudioRegionView* arv = dynamic_cast (*j); + if (!arv) { + continue; + } + boost::shared_ptr ar = boost::dynamic_pointer_cast (arv->region ()); + if (!ar) { + continue; + } + ag.analyze_region (ar); + } + spd.hide(); + if (!ag.canceled ()) { + ExportReport er (_("Audio Report/Analysis"), ag.results ()); + er.run(); + } +} + +void +Editor::loudness_analyze_range_selection () +{ + if (!_session) { + return; + } + Selection& s (PublicEditor::instance ().get_selection ()); + TimeSelection ts = s.time; + ARDOUR::AnalysisGraph ag (_session); + samplecnt_t total_work = 0; + + for (TrackSelection::iterator i = s.tracks.begin (); i != s.tracks.end (); ++i) { + boost::shared_ptr pl = boost::dynamic_pointer_cast ((*i)->playlist ()); + if (!pl) { + continue; + } + RouteUI *rui = dynamic_cast (*i); + if (!pl || !rui) { + continue; + } + for (std::list::iterator j = ts.begin (); j != ts.end (); ++j) { + total_work += j->length (); + } + } + + SimpleProgressDialog spd (_("Range Loudness Analysis"), sigc::mem_fun (ag, &AnalysisGraph::cancel)); + ScopedConnection c; + ag.set_total_samples (total_work); + ag.Progress.connect_same_thread (c, boost::bind (&SimpleProgressDialog::update_progress, &spd, _1, _2)); + spd.show(); + + for (TrackSelection::iterator i = s.tracks.begin (); i != s.tracks.end (); ++i) { + boost::shared_ptr pl = boost::dynamic_pointer_cast ((*i)->playlist ()); + if (!pl) { + continue; + } + RouteUI *rui = dynamic_cast (*i); + if (!pl || !rui) { + continue; + } + ag.analyze_range (rui->route (), pl, ts); + } + spd.hide(); + if (!ag.canceled ()) { + ExportReport er (_("Audio Report/Analysis"), ag.results ()); + er.run(); + } +} + +void +Editor::spectral_analyze_region_selection () { if (analysis_window == 0) { analysis_window = new AnalysisWindow(); @@ -1728,7 +1846,7 @@ Editor::analyze_region_selection () } void -Editor::analyze_range_selection() +Editor::spectral_analyze_range_selection() { if (analysis_window == 0) { analysis_window = new AnalysisWindow(); @@ -1770,27 +1888,17 @@ Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::sha RegionSelection rs = get_regions_from_selection_and_entered (); - string::size_type pos = 0; string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions"); - /* we have to hack up the region name because "_" has a special - meaning for menu titles. - */ - - while ((pos = menu_item_name.find ("_", pos)) != string::npos) { - menu_item_name.replace (pos, 1, "__"); - pos += 2; - } - if (_popup_region_menu_item == 0) { - _popup_region_menu_item = new MenuItem (menu_item_name); + _popup_region_menu_item = new MenuItem (menu_item_name, false); _popup_region_menu_item->set_submenu (*dynamic_cast (ActionManager::get_widget (X_("/PopupRegionMenu")))); _popup_region_menu_item->show (); } else { _popup_region_menu_item->set_label (menu_item_name); } - /* No latering allowed in later is higher layering model */ + /* No layering allowed in later is higher layering model */ RefPtr act = ActionManager::get_action (X_("EditorMenu"), X_("RegionMenuLayering")); if (act && Config->get_layer_model() == LaterHigher) { act->set_sensitive (false); @@ -1798,7 +1906,7 @@ Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::sha act->set_sensitive (true); } - const framepos_t position = get_preferred_edit_position (EDIT_IGNORE_NONE, true); + const samplepos_t position = get_preferred_edit_position (EDIT_IGNORE_NONE, true); edit_items.push_back (*_popup_region_menu_item); if (Config->get_layer_model() == Manual && track->playlist()->count_regions_at (position) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) { @@ -1819,10 +1927,11 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items) edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true))); edit_items.push_back (SeparatorElem()); - edit_items.push_back (MenuElem (_("Zoom to Range"), sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_selection), false))); + edit_items.push_back (MenuElem (_("Zoom to Range"), sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_selection), Horizontal))); edit_items.push_back (SeparatorElem()); - edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection))); + edit_items.push_back (MenuElem (_("Loudness Analysis"), sigc::mem_fun(*this, &Editor::loudness_analyze_range_selection))); + edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::spectral_analyze_range_selection))); edit_items.push_back (SeparatorElem()); @@ -2023,12 +2132,73 @@ Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items) edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu)); } -SnapType -Editor::snap_type() const +GridType +Editor::grid_type() const { - return _snap_type; + return _grid_type; +} + +bool +Editor::grid_musical() const +{ + switch (_grid_type) { + case GridTypeBeatDiv32: + case GridTypeBeatDiv28: + case GridTypeBeatDiv24: + case GridTypeBeatDiv20: + case GridTypeBeatDiv16: + case GridTypeBeatDiv14: + case GridTypeBeatDiv12: + case GridTypeBeatDiv10: + case GridTypeBeatDiv8: + case GridTypeBeatDiv7: + case GridTypeBeatDiv6: + case GridTypeBeatDiv5: + case GridTypeBeatDiv4: + case GridTypeBeatDiv3: + case GridTypeBeatDiv2: + case GridTypeBeat: + case GridTypeBar: + return true; + case GridTypeNone: + case GridTypeSmpte: + case GridTypeMinSec: + case GridTypeSamples: + return false; + } + return false; } +bool +Editor::grid_nonmusical() const +{ + switch (_grid_type) { + case GridTypeSmpte: + case GridTypeMinSec: + case GridTypeSamples: + return true; + case GridTypeBeatDiv32: + case GridTypeBeatDiv28: + case GridTypeBeatDiv24: + case GridTypeBeatDiv20: + case GridTypeBeatDiv16: + case GridTypeBeatDiv14: + case GridTypeBeatDiv12: + case GridTypeBeatDiv10: + case GridTypeBeatDiv8: + case GridTypeBeatDiv7: + case GridTypeBeatDiv6: + case GridTypeBeatDiv5: + case GridTypeBeatDiv4: + case GridTypeBeatDiv3: + case GridTypeBeatDiv2: + case GridTypeBeat: + case GridTypeBar: + case GridTypeNone: + return false; + } + return false; +} SnapMode Editor::snap_mode() const { @@ -2036,73 +2206,79 @@ Editor::snap_mode() const } void -Editor::set_snap_to (SnapType st) +Editor::set_grid_to (GridType gt) { - unsigned int snap_ind = (unsigned int)st; + if (_grid_type == gt) { //already set + return; + } + + unsigned int grid_ind = (unsigned int)gt; if (internal_editing()) { - internal_snap_type = st; + internal_grid_type = gt; } else { - pre_internal_snap_type = st; + pre_internal_grid_type = gt; } - _snap_type = st; + _grid_type = gt; - if (snap_ind > snap_type_strings.size() - 1) { - snap_ind = 0; - _snap_type = (SnapType)snap_ind; + if (grid_ind > grid_type_strings.size() - 1) { + grid_ind = 0; + _grid_type = (GridType)grid_ind; } - string str = snap_type_strings[snap_ind]; + string str = grid_type_strings[grid_ind]; - if (str != snap_type_selector.get_text()) { - snap_type_selector.set_text (str); + if (str != grid_type_selector.get_text()) { + grid_type_selector.set_text (str); } - instant_save (); + //show appropriate rulers for this grid setting. (ToDo: perhaps make this optional) + //Currently this is 'required' because the RULER calculates the grid_marks which will be used by grid_lines + if ( grid_musical() ) { + ruler_tempo_action->set_active(true); + ruler_meter_action->set_active(true); - switch (_snap_type) { - case SnapToBeatDiv128: - case SnapToBeatDiv64: - case SnapToBeatDiv32: - case SnapToBeatDiv28: - case SnapToBeatDiv24: - case SnapToBeatDiv20: - case SnapToBeatDiv16: - case SnapToBeatDiv14: - case SnapToBeatDiv12: - case SnapToBeatDiv10: - case SnapToBeatDiv8: - case SnapToBeatDiv7: - case SnapToBeatDiv6: - case SnapToBeatDiv5: - case SnapToBeatDiv4: - case SnapToBeatDiv3: - case SnapToBeatDiv2: { - ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_begin; - ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_end; - - compute_current_bbt_points (leftmost_frame, leftmost_frame + current_page_samples(), - current_bbt_points_begin, current_bbt_points_end); - compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples(), - current_bbt_points_begin, current_bbt_points_end); - update_tempo_based_rulers (current_bbt_points_begin, current_bbt_points_end); - break; + ruler_bbt_action->set_active(true); + ruler_timecode_action->set_active(false); + ruler_minsec_action->set_active(false); + ruler_samples_action->set_active(false); + } else if (_grid_type == GridTypeSmpte ) { + ruler_tempo_action->set_active(false); + ruler_meter_action->set_active(false); + + ruler_bbt_action->set_active(false); + ruler_timecode_action->set_active(true); + ruler_minsec_action->set_active(false); + ruler_samples_action->set_active(false); + } else if (_grid_type == GridTypeMinSec ) { + ruler_tempo_action->set_active(false); + ruler_meter_action->set_active(false); + + ruler_bbt_action->set_active(false); + ruler_timecode_action->set_active(false); + ruler_minsec_action->set_active(true); + ruler_samples_action->set_active(false); + } else if (_grid_type == GridTypeSamples ) { + ruler_tempo_action->set_active(false); + ruler_meter_action->set_active(false); + + ruler_bbt_action->set_active(false); + ruler_timecode_action->set_active(false); + ruler_minsec_action->set_active(false); + ruler_samples_action->set_active(true); } - case SnapToRegionStart: - case SnapToRegionEnd: - case SnapToRegionSync: - case SnapToRegionBoundary: - build_region_boundary_cache (); - break; + instant_save (); - default: - /* relax */ - break; + if ( grid_musical() ) { + compute_bbt_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples()); + update_tempo_based_rulers (); } - redisplay_tempo (false); + mark_region_boundary_cache_dirty (); + + redisplay_grid (false); SnapChanged (); /* EMIT SIGNAL */ } @@ -2110,8 +2286,6 @@ Editor::set_snap_to (SnapType st) void Editor::set_snap_mode (SnapMode mode) { - string str = snap_mode_strings[(int)mode]; - if (internal_editing()) { internal_snap_mode = mode; } else { @@ -2119,9 +2293,11 @@ Editor::set_snap_mode (SnapMode mode) } _snap_mode = mode; - - if (str != snap_mode_selector.get_text ()) { - snap_mode_selector.set_text (str); + + if (_snap_mode == SnapOff ) { + snap_mode_button.set_active_state (Gtkmm2ext::Off); + } else { + snap_mode_button.set_active_state (Gtkmm2ext::ExplicitActive); } instant_save (); @@ -2167,14 +2343,15 @@ Editor::set_edit_point_preference (EditPoint ep, bool force) Glib::RefPtr::cast_dynamic(act)->set_active (true); } - framepos_t foo; + samplepos_t foo; bool in_track_canvas; - if (!mouse_frame (foo, in_track_canvas)) { + if (!mouse_sample (foo, in_track_canvas)) { in_track_canvas = false; } reset_canvas_action_sensitivity (in_track_canvas); + sensitize_the_right_region_actions (false); instant_save (); } @@ -2182,16 +2359,16 @@ Editor::set_edit_point_preference (EditPoint ep, bool force) int Editor::set_state (const XMLNode& node, int version) { - const XMLProperty* prop; set_id (node); + PBD::Unwinder nsi (no_save_instant, true); + bool yn; Tabbable::set_state (node, version); - - if (_session && (prop = node.property ("playhead"))) { - framepos_t pos; - sscanf (prop->value().c_str(), "%" PRIi64, &pos); - if (pos >= 0) { - playhead_cursor->set_position (pos); + + samplepos_t ph_pos; + if (_session && node.get_property ("playhead", ph_pos)) { + if (ph_pos >= 0) { + playhead_cursor->set_position (ph_pos); } else { warning << _("Playhead position stored with a negative value - ignored (use zero instead)") << endmsg; playhead_cursor->set_position (0); @@ -2200,74 +2377,70 @@ Editor::set_state (const XMLNode& node, int version) playhead_cursor->set_position (0); } - if ((prop = node.property ("mixer-width"))) { - editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width)); - } + node.get_property ("mixer-width", editor_mixer_strip_width); - if ((prop = node.property ("zoom-focus"))) { - zoom_focus_selection_done ((ZoomFocus) string_2_enum (prop->value(), zoom_focus)); - } + node.get_property ("zoom-focus", zoom_focus); + zoom_focus_selection_done (zoom_focus); - if ((prop = node.property ("zoom"))) { + double z; + if (node.get_property ("zoom", z)) { /* older versions of ardour used floating point samples_per_pixel */ - double f = PBD::atof (prop->value()); - reset_zoom (llrintf (f)); + reset_zoom (llrintf (z)); } else { reset_zoom (samples_per_pixel); } - if ((prop = node.property ("visible-track-count"))) { - set_visible_track_count (PBD::atoi (prop->value())); - } - - if ((prop = node.property ("snap-to"))) { - snap_type_selection_done ((SnapType) string_2_enum (prop->value(), _snap_type)); - } - - if ((prop = node.property ("snap-mode"))) { - snap_mode_selection_done((SnapMode) string_2_enum (prop->value(), _snap_mode)); - } - - if ((prop = node.property ("internal-snap-to"))) { - internal_snap_type = (SnapType) string_2_enum (prop->value(), internal_snap_type); + int32_t cnt; + if (node.get_property ("visible-track-count", cnt)) { + set_visible_track_count (cnt); } - if ((prop = node.property ("internal-snap-mode"))) { - internal_snap_mode = (SnapMode) string_2_enum (prop->value(), internal_snap_mode); + GridType grid_type; + if (!node.get_property ("grid-type", grid_type)) { + grid_type = _grid_type; } + set_grid_to (grid_type); - if ((prop = node.property ("pre-internal-snap-to"))) { - pre_internal_snap_type = (SnapType) string_2_enum (prop->value(), pre_internal_snap_type); + SnapMode sm; + if (node.get_property ("snap-mode", sm)) { + snap_mode_selection_done(sm); + /* set text of Dropdown. in case _snap_mode == SnapOff (default) + * snap_mode_selection_done() will only mark an already active item as active + * which does not trigger set_text(). + */ + set_snap_mode (sm); + } else { + set_snap_mode (_snap_mode); } - if ((prop = node.property ("pre-internal-snap-mode"))) { - pre_internal_snap_mode = (SnapMode) string_2_enum (prop->value(), pre_internal_snap_mode); - } + node.get_property ("internal-grid-type", internal_grid_type); + node.get_property ("internal-snap-mode", internal_snap_mode); + node.get_property ("pre-internal-grid-type", pre_internal_grid_type); + node.get_property ("pre-internal-snap-mode", pre_internal_snap_mode); - if ((prop = node.property ("mouse-mode"))) { - MouseMode m = str2mousemode(prop->value()); + std::string mm_str; + if (node.get_property ("mouse-mode", mm_str)) { + MouseMode m = str2mousemode(mm_str); set_mouse_mode (m, true); } else { set_mouse_mode (MouseObject, true); } - if ((prop = node.property ("left-frame")) != 0) { - framepos_t pos; - if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) { - if (pos < 0) { - pos = 0; - } - reset_x_origin (pos); + samplepos_t lf_pos; + if (node.get_property ("left-frame", lf_pos)) { + if (lf_pos < 0) { + lf_pos = 0; } + reset_x_origin (lf_pos); } - if ((prop = node.property ("y-origin")) != 0) { - reset_y_origin (atof (prop->value ())); + double y_origin; + if (node.get_property ("y-origin", y_origin)) { + reset_y_origin (y_origin); } - if ((prop = node.property ("join-object-range"))) { + if (node.get_property ("join-object-range", yn)) { RefPtr act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object-range")); - bool yn = string_is_affirmative (prop->value()); if (act) { RefPtr tact = RefPtr::cast_dynamic(act); tact->set_active (!yn); @@ -2276,37 +2449,32 @@ Editor::set_state (const XMLNode& node, int version) set_mouse_mode(mouse_mode, true); } - if ((prop = node.property ("edit-point"))) { - set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true); - } - - if ((prop = node.property ("show-measures"))) { - bool yn = string_is_affirmative (prop->value()); - _show_measures = yn; + EditPoint ep; + if (node.get_property ("edit-point", ep)) { + set_edit_point_preference (ep, true); + } else { + set_edit_point_preference (_edit_point); } - if ((prop = node.property ("follow-playhead"))) { - bool yn = string_is_affirmative (prop->value()); + if (node.get_property ("follow-playhead", yn)) { set_follow_playhead (yn); } - if ((prop = node.property ("stationary-playhead"))) { - bool yn = string_is_affirmative (prop->value()); + if (node.get_property ("stationary-playhead", yn)) { set_stationary_playhead (yn); } - if ((prop = node.property ("region-list-sort-type"))) { - RegionListSortType st; - _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true); + RegionListSortType sort_type; + if (node.get_property ("region-list-sort-type", sort_type)) { + _regions->reset_sort_type (sort_type, true); } - if ((prop = node.property ("show-editor-mixer"))) { + if (node.get_property ("show-editor-mixer", yn)) { Glib::RefPtr act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer")); assert (act); Glib::RefPtr tact = Glib::RefPtr::cast_dynamic(act); - bool yn = string_is_affirmative (prop->value()); /* do it twice to force the change */ @@ -2314,13 +2482,12 @@ Editor::set_state (const XMLNode& node, int version) tact->set_active (yn); } - if ((prop = node.property ("show-editor-list"))) { + if (node.get_property ("show-editor-list", yn)) { Glib::RefPtr act = ActionManager::get_action (X_("Editor"), X_("show-editor-list")); assert (act); Glib::RefPtr tact = Glib::RefPtr::cast_dynamic(act); - bool yn = string_is_affirmative (prop->value()); /* do it twice to force the change */ @@ -2328,15 +2495,15 @@ Editor::set_state (const XMLNode& node, int version) tact->set_active (yn); } - if ((prop = node.property (X_("editor-list-page")))) { - _the_notebook.set_current_page (atoi (prop->value ())); + int32_t el_page; + if (node.get_property (X_("editor-list-page"), el_page)) { + _the_notebook.set_current_page (el_page); } - if ((prop = node.property (X_("show-marker-lines")))) { + if (node.get_property (X_("show-marker-lines"), yn)) { Glib::RefPtr act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines")); assert (act); Glib::RefPtr tact = Glib::RefPtr::cast_dynamic (act); - bool yn = string_is_affirmative (prop->value ()); tact->set_active (!yn); tact->set_active (yn); @@ -2346,10 +2513,10 @@ Editor::set_state (const XMLNode& node, int version) for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) { selection->set_state (**i, Stateful::current_state_version); _regions->set_state (**i); + _locations->set_state (**i); } - if ((prop = node.property ("maximised"))) { - bool yn = string_is_affirmative (prop->value()); + if (node.get_property ("maximised", yn)) { Glib::RefPtr act = ActionManager::get_action (X_("Common"), X_("ToggleMaximalEditor")); assert (act); Glib::RefPtr tact = Glib::RefPtr::cast_dynamic(act); @@ -2359,13 +2526,12 @@ Editor::set_state (const XMLNode& node, int version) } } - if ((prop = node.property ("nudge-clock-value"))) { - framepos_t f; - sscanf (prop->value().c_str(), "%" PRId64, &f); - nudge_clock->set (f); + samplepos_t nudge_clock_value; + if (node.get_property ("nudge-clock-value", nudge_clock_value)) { + nudge_clock->set (nudge_clock_value); } else { nudge_clock->set_mode (AudioClock::Timecode); - nudge_clock->set (_session->frame_rate() * 5, true); + nudge_clock->set (_session->sample_rate() * 5, true); } { @@ -2374,16 +2540,6 @@ Editor::set_state (const XMLNode& node, int version) * those that are linked to a private variable may need changing */ RefPtr act; - bool yn; - - act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility")); - if (act) { - yn = _show_measures; - RefPtr tact = RefPtr::cast_dynamic(act); - /* do it twice to force the change */ - tact->set_active (!yn); - tact->set_active (yn); - } act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead")); yn = _follow_playhead; @@ -2404,85 +2560,77 @@ Editor::set_state (const XMLNode& node, int version) } } - return 0; + return LuaInstance::instance()->set_state(node); } XMLNode& Editor::get_state () { XMLNode* node = new XMLNode (X_("Editor")); - char buf[32]; - id().print (buf, sizeof (buf)); - node->add_property ("id", buf); + node->set_property ("id", id().to_s ()); node->add_child_nocopy (Tabbable::get_state()); - - snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast(&edit_pane)->gobj())); - node->add_property("edit-horizontal-pane-pos", string(buf)); - node->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0"); - snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast(&editor_summary_pane)->gobj())); - node->add_property("edit-vertical-pane-pos", string(buf)); - + + node->set_property("edit-horizontal-pane-pos", edit_pane.get_divider ()); + node->set_property("notebook-shrunk", _notebook_shrunk); + node->set_property("edit-vertical-pane-pos", editor_summary_pane.get_divider()); + maybe_add_mixer_strip_width (*node); - node->add_property ("zoom-focus", enum_2_string (zoom_focus)); - - snprintf (buf, sizeof(buf), "%" PRId64, samples_per_pixel); - node->add_property ("zoom", buf); - node->add_property ("snap-to", enum_2_string (_snap_type)); - node->add_property ("snap-mode", enum_2_string (_snap_mode)); - node->add_property ("internal-snap-to", enum_2_string (internal_snap_type)); - node->add_property ("internal-snap-mode", enum_2_string (internal_snap_mode)); - node->add_property ("pre-internal-snap-to", enum_2_string (pre_internal_snap_type)); - node->add_property ("pre-internal-snap-mode", enum_2_string (pre_internal_snap_mode)); - node->add_property ("edit-point", enum_2_string (_edit_point)); - snprintf (buf, sizeof(buf), "%d", _visible_track_count); - node->add_property ("visible-track-count", buf); - - snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame ()); - node->add_property ("playhead", buf); - snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame); - node->add_property ("left-frame", buf); - snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ()); - node->add_property ("y-origin", buf); - - node->add_property ("show-measures", _show_measures ? "yes" : "no"); - node->add_property ("maximised", _maximised ? "yes" : "no"); - node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no"); - node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no"); - node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ())); - node->add_property ("mouse-mode", enum2str(mouse_mode)); - node->add_property ("join-object-range", smart_mode_action->get_active () ? "yes" : "no"); + node->set_property ("zoom-focus", zoom_focus); + + node->set_property ("zoom", samples_per_pixel); + node->set_property ("grid-type", _grid_type); + node->set_property ("snap-mode", _snap_mode); + node->set_property ("internal-grid-type", internal_grid_type); + node->set_property ("internal-snap-mode", internal_snap_mode); + node->set_property ("pre-internal-grid-type", pre_internal_grid_type); + node->set_property ("pre-internal-snap-mode", pre_internal_snap_mode); + node->set_property ("edit-point", _edit_point); + node->set_property ("visible-track-count", _visible_track_count); + + node->set_property ("playhead", playhead_cursor->current_sample ()); + node->set_property ("left-frame", _leftmost_sample); + node->set_property ("y-origin", vertical_adjustment.get_value ()); + + node->set_property ("maximised", _maximised); + node->set_property ("follow-playhead", _follow_playhead); + node->set_property ("stationary-playhead", _stationary_playhead); + node->set_property ("region-list-sort-type", _regions->sort_type ()); + node->set_property ("mouse-mode", mouse_mode); + node->set_property ("join-object-range", smart_mode_action->get_active ()); Glib::RefPtr act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer")); if (act) { Glib::RefPtr tact = Glib::RefPtr::cast_dynamic(act); - node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no"); + node->set_property (X_("show-editor-mixer"), tact->get_active()); } act = ActionManager::get_action (X_("Editor"), X_("show-editor-list")); if (act) { Glib::RefPtr tact = Glib::RefPtr::cast_dynamic(act); - node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no"); + node->set_property (X_("show-editor-list"), tact->get_active()); } - snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ()); - node->add_property (X_("editor-list-page"), buf); + node->set_property (X_("editor-list-page"), _the_notebook.get_current_page ()); - if (button_bindings) { - XMLNode* bb = new XMLNode (X_("Buttons")); - button_bindings->save (*bb); - node->add_child_nocopy (*bb); - } + if (button_bindings) { + XMLNode* bb = new XMLNode (X_("Buttons")); + button_bindings->save (*bb); + node->add_child_nocopy (*bb); + } - node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no"); + node->set_property (X_("show-marker-lines"), _show_marker_lines); node->add_child_nocopy (selection->get_state ()); node->add_child_nocopy (_regions->get_state ()); - snprintf (buf, sizeof (buf), "%" PRId64, nudge_clock->current_duration()); - node->add_property ("nudge-clock-value", buf); + node->set_property ("nudge-clock-value", nudge_clock->current_duration()); + + node->add_child_nocopy (LuaInstance::instance()->get_action_state()); + node->add_child_nocopy (LuaInstance::instance()->get_hook_state()); + node->add_child_nocopy (_locations->get_state ()); return *node; } @@ -2518,13 +2666,22 @@ Editor::trackview_by_y_position (double y, bool trackview_relative_offset) const return std::make_pair ( (TimeAxisView *) 0, 0); } +void +Editor::set_snapped_cursor_position (samplepos_t pos) +{ + if ( _edit_point == EditAtMouse ) { + snapped_cursor->set_position(pos); + } +} + + /** Snap a position to the grid, if appropriate, taking into account current * grid settings and also the state of any snap modifier keys that may be pressed. * @param start Position to snap. * @param event Event to get current key modifier information from, or 0. */ void -Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, RoundMode direction, bool for_mark) +Editor::snap_to_with_modifier (MusicSample& start, GdkEvent const * event, RoundMode direction, SnapPref pref, bool for_mark) { if (!_session || !event) { return; @@ -2532,285 +2689,210 @@ Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, RoundM if (ArdourKeyboard::indicates_snap (event->button.state)) { if (_snap_mode == SnapOff) { - snap_to_internal (start, direction, for_mark); + snap_to_internal (start, direction, pref, for_mark); + } else { + start.set (start.sample, 0); } } else { if (_snap_mode != SnapOff) { - snap_to_internal (start, direction, for_mark); + snap_to_internal (start, direction, pref, for_mark); } else if (ArdourKeyboard::indicates_snap_delta (event->button.state)) { /* SnapOff, but we pressed the snap_delta modifier */ - snap_to_internal (start, direction, for_mark); + snap_to_internal (start, direction, pref, for_mark); + } else { + start.set (start.sample, 0); } } } void -Editor::snap_to (framepos_t& start, RoundMode direction, bool for_mark, bool ensure_snap) +Editor::snap_to (MusicSample& start, RoundMode direction, SnapPref pref, bool for_mark, bool ensure_snap) { if (!_session || (_snap_mode == SnapOff && !ensure_snap)) { + start.set (start.sample, 0); return; } - snap_to_internal (start, direction, for_mark, ensure_snap); + snap_to_internal (start, direction, pref, for_mark, ensure_snap); } void -Editor::timecode_snap_to_internal (framepos_t& start, RoundMode direction, bool /*for_mark*/) +check_best_snap ( samplepos_t presnap, samplepos_t &test, samplepos_t &dist, samplepos_t &best ) { - const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame()); - framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60); + samplepos_t diff = abs( test - presnap ); + if ( diff < dist ) { + dist = diff; + best = test; + } - switch (_snap_type) { - case SnapToTimecodeFrame: - if ((direction == RoundUpMaybe || direction == RoundDownMaybe) && - fmod((double)start, (double)_session->frames_per_timecode_frame()) == 0) { - /* start is already on a whole timecode frame, do nothing */ - } else if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) { - start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame()); - } else { - start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame()); - } - break; + test = max_samplepos; //reset this so it doesn't get accidentally reused +} - case SnapToTimecodeSeconds: - if (_session->config.get_timecode_offset_negative()) { - start += _session->config.get_timecode_offset (); - } else { - start -= _session->config.get_timecode_offset (); - } - if ((direction == RoundUpMaybe || direction == RoundDownMaybe) && - (start % one_timecode_second == 0)) { - /* start is already on a whole second, do nothing */ - } else if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) { - start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second; - } else { - start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second; - } +samplepos_t +Editor::snap_to_grid (vector marks, samplepos_t presnap, RoundMode direction) +{ + if (marks.empty() ) return presnap; - if (_session->config.get_timecode_offset_negative()) { - start -= _session->config.get_timecode_offset (); - } else { - start += _session->config.get_timecode_offset (); - } - break; + samplepos_t before; + samplepos_t after; + samplepos_t test = presnap; - case SnapToTimecodeMinutes: - if (_session->config.get_timecode_offset_negative()) { - start += _session->config.get_timecode_offset (); - } else { - start -= _session->config.get_timecode_offset (); - } - if ((direction == RoundUpMaybe || direction == RoundDownMaybe) && - (start % one_timecode_minute == 0)) { - /* start is already on a whole minute, do nothing */ - } else if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) { - start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute; - } else { - start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute; + before = after = max_samplepos; + + //get marks to either side of presnap + vector::const_iterator m = marks.begin(); + while ( m != marks.end() && (m->position < presnap) ) { + ++m; + } + + if (m == marks.end ()) { + /* ran out of marks */ + before = marks.back().position; + } + + after = m->position; + + if (m != marks.begin ()) { + --m; + before = m->position; + } + + if (before == max_samplepos && after == max_samplepos) { + /* No grid to snap to, so just don't snap */ + return presnap; + } else if (before == max_samplepos) { + test = after; + } else if (after == max_samplepos) { + test = before; + } else { + if ((direction == RoundUpMaybe || direction == RoundUpAlways)) + test = after; + else if ((direction == RoundDownMaybe || direction == RoundDownAlways)) + test = before; + else if (direction == 0 ) { + if ((presnap - before) < (after - presnap)) { + test = before; + } else { + test = after; + } } - if (_session->config.get_timecode_offset_negative()) { - start -= _session->config.get_timecode_offset (); - } else { - start += _session->config.get_timecode_offset (); + } + + return test; +} + +samplepos_t +Editor::marker_snap_to_internal (samplepos_t presnap, RoundMode direction) +{ + samplepos_t before; + samplepos_t after; + samplepos_t test; + + _session->locations()->marks_either_side (presnap, before, after); + + if (before == max_samplepos && after == max_samplepos) { + /* No marks to snap to, so just don't snap */ + return presnap; + } else if (before == max_samplepos) { + test = after; + } else if (after == max_samplepos) { + test = before; + } else { + if ((direction == RoundUpMaybe || direction == RoundUpAlways)) { + test = after; + } else if ((direction == RoundDownMaybe || direction == RoundDownAlways)) { + test = before; + } else if (direction == 0 ) { + if ((presnap - before) < (after - presnap)) { + test = before; + } else { + test = after; + } } - break; - default: - fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg; - abort(); /*NOTREACHED*/ } + + return test; } void -Editor::snap_to_internal (framepos_t& start, RoundMode direction, bool for_mark, bool ensure_snap) +Editor::snap_to_internal (MusicSample& start, RoundMode direction, SnapPref pref, bool for_mark, bool ensure_snap) { - const framepos_t one_second = _session->frame_rate(); - const framepos_t one_minute = _session->frame_rate() * 60; - framepos_t presnap = start; - framepos_t before; - framepos_t after; + const samplepos_t presnap = start.sample; - switch (_snap_type) { - case SnapToTimecodeFrame: - case SnapToTimecodeSeconds: - case SnapToTimecodeMinutes: - return timecode_snap_to_internal (start, direction, for_mark); + samplepos_t test = max_samplepos; //for each snap, we'll use this value + samplepos_t dist = max_samplepos; //this records the distance of the best snap result we've found so far + samplepos_t best = max_samplepos; //this records the best snap-result we've found so far - case SnapToCDFrame: - if ((direction == RoundUpMaybe || direction == RoundDownMaybe) && - start % (one_second/75) == 0) { - /* start is already on a whole CD frame, do nothing */ - } else if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) { - start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75); - } else { - start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75); + //check snap-to-marker + if ( UIConfiguration::instance().get_snap_to_marks() ) { + if (for_mark) { + return; } - break; - case SnapToSeconds: - if ((direction == RoundUpMaybe || direction == RoundDownMaybe) && - start % one_second == 0) { - /* start is already on a whole second, do nothing */ - } else if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) { - start = (framepos_t) ceil ((double) start / one_second) * one_second; - } else { - start = (framepos_t) floor ((double) start / one_second) * one_second; - } - break; + test = marker_snap_to_internal ( presnap, direction ); + check_best_snap(presnap, test, dist, best); + } - case SnapToMinutes: - if ((direction == RoundUpMaybe || direction == RoundDownMaybe) && - start % one_minute == 0) { - /* start is already on a whole minute, do nothing */ - } else if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) { - start = (framepos_t) ceil ((double) start / one_minute) * one_minute; - } else { - start = (framepos_t) floor ((double) start / one_minute) * one_minute; - } - break; + //check snap-to-region-{start/end/sync} + if ( UIConfiguration::instance().get_snap_to_region_start() || UIConfiguration::instance().get_snap_to_region_end() || UIConfiguration::instance().get_snap_to_region_sync() ) { + if (!region_boundary_cache.empty()) { - case SnapToBar: - start = _session->tempo_map().round_to_bar (start, direction); - break; + vector::iterator prev = region_boundary_cache.end (); + vector::iterator next = region_boundary_cache.end (); - case SnapToBeat: - start = _session->tempo_map().round_to_beat (start, direction); - break; - - case SnapToBeatDiv128: - start = _session->tempo_map().round_to_beat_subdivision (start, 128, direction); - break; - case SnapToBeatDiv64: - start = _session->tempo_map().round_to_beat_subdivision (start, 64, direction); - break; - case SnapToBeatDiv32: - start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction); - break; - case SnapToBeatDiv28: - start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction); - break; - case SnapToBeatDiv24: - start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction); - break; - case SnapToBeatDiv20: - start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction); - break; - case SnapToBeatDiv16: - start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction); - break; - case SnapToBeatDiv14: - start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction); - break; - case SnapToBeatDiv12: - start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction); - break; - case SnapToBeatDiv10: - start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction); - break; - case SnapToBeatDiv8: - start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction); - break; - case SnapToBeatDiv7: - start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction); - break; - case SnapToBeatDiv6: - start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction); - break; - case SnapToBeatDiv5: - start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction); - break; - case SnapToBeatDiv4: - start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction); - break; - case SnapToBeatDiv3: - start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction); - break; - case SnapToBeatDiv2: - start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction); - break; - - case SnapToMark: - if (for_mark) { - return; - } - - _session->locations()->marks_either_side (start, before, after); - - if (before == max_framepos && after == max_framepos) { - /* No marks to snap to, so just don't snap */ - return; - } else if (before == max_framepos) { - start = after; - } else if (after == max_framepos) { - start = before; - } else if (before != max_framepos && after != max_framepos) { - /* have before and after */ - if ((start - before) < (after - start)) { - start = before; - } else { - start = after; - } - } - - break; - - case SnapToRegionStart: - case SnapToRegionEnd: - case SnapToRegionSync: - case SnapToRegionBoundary: - if (!region_boundary_cache.empty()) { - - vector::iterator prev = region_boundary_cache.end (); - vector::iterator next = region_boundary_cache.end (); - - if (direction > 0) { - next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start); - } else { - next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start); - } + if (direction > 0) { + next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), presnap); + } else { + next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), presnap); + } if (next != region_boundary_cache.begin ()) { prev = next; prev--; } - framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev; - framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next; + samplepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev; + samplepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next; - if (start > (p + n) / 2) { - start = n; + if (presnap > (p + n) / 2) { + test = n; } else { - start = p; + test = p; } } - break; - } - - switch (_snap_mode) { - case SnapNormal: - return; - case SnapMagnetic: + check_best_snap(presnap, test, dist, best); + } - if (ensure_snap) { - return; - } + //check Grid + if (UIConfiguration::instance().get_snap_to_grid() && (_grid_type != GridTypeNone) ) { - if (presnap > start) { - if (presnap > (start + pixel_to_sample(snap_threshold))) { - start = presnap; - } + //if SnapToGrid is selected, the user wants to prioritize the music grid + //in this case we should reset the best distance, so Grid will prevail + dist = max_samplepos; - } else if (presnap < start) { - if (presnap < (start - pixel_to_sample(snap_threshold))) { - start = presnap; - } - } + test = snap_to_grid (grid_marks, presnap, direction); + check_best_snap(presnap, test, dist, best); + } - default: - /* handled at entry */ + //now check "magnetic" state: is the grid within reasonable on-screen distance to trigger a snap? + //this also helps to avoid snapping to somewhere the user can't see. ( i.e.: I clicked on a region and it disappeared!! ) + //ToDo: perhaps this should only occur if EditPointMouse? + int snap_threshold_s = pixel_to_sample(UIConfiguration::instance().get_snap_threshold()); + if (ensure_snap) { + start.set (best, 0); return; - + } else if (presnap > best) { + if (presnap > (best+ snap_threshold_s)) { + best = presnap; + } + } else if (presnap < best) { + if (presnap < (best - snap_threshold_s)) { + best = presnap; + } } + + start.set (best, 0); } @@ -2836,18 +2918,20 @@ Editor::setup_toolbar () mouse_mode_size_group->add_widget (mouse_draw_button); mouse_mode_size_group->add_widget (mouse_content_button); - mouse_mode_size_group->add_widget (zoom_in_button); - mouse_mode_size_group->add_widget (zoom_out_button); - mouse_mode_size_group->add_widget (zoom_preset_selector); - mouse_mode_size_group->add_widget (zoom_out_full_button); - mouse_mode_size_group->add_widget (zoom_focus_selector); - - mouse_mode_size_group->add_widget (tav_shrink_button); - mouse_mode_size_group->add_widget (tav_expand_button); - mouse_mode_size_group->add_widget (visible_tracks_selector); + if (!Profile->get_mixbus()) { + mouse_mode_size_group->add_widget (zoom_in_button); + mouse_mode_size_group->add_widget (zoom_out_button); + mouse_mode_size_group->add_widget (zoom_out_full_button); + mouse_mode_size_group->add_widget (zoom_focus_selector); + mouse_mode_size_group->add_widget (tav_shrink_button); + mouse_mode_size_group->add_widget (tav_expand_button); + } else { + mouse_mode_size_group->add_widget (zoom_preset_selector); + mouse_mode_size_group->add_widget (visible_tracks_selector); + } - mouse_mode_size_group->add_widget (snap_type_selector); - mouse_mode_size_group->add_widget (snap_mode_selector); + mouse_mode_size_group->add_widget (grid_type_selector); + mouse_mode_size_group->add_widget (snap_mode_button); mouse_mode_size_group->add_widget (edit_point_selector); mouse_mode_size_group->add_widget (edit_mode_selector); @@ -2867,11 +2951,11 @@ Editor::setup_toolbar () if (!ARDOUR::Profile->get_mixbus()) { mouse_mode_hbox->pack_start (mouse_cut_button, false, false); + mouse_mode_hbox->pack_start (mouse_audition_button, false, false); } if (!ARDOUR::Profile->get_trx()) { mouse_mode_hbox->pack_start (mouse_timefx_button, false, false); - mouse_mode_hbox->pack_start (mouse_audition_button, false, false); mouse_mode_hbox->pack_start (mouse_draw_button, false, false); mouse_mode_hbox->pack_start (mouse_content_button, false, false); } @@ -2887,25 +2971,12 @@ Editor::setup_toolbar () if (!ARDOUR::Profile->get_trx()) { mode_box->pack_start (edit_mode_selector, false, false); - } - mode_box->pack_start (*mouse_mode_box, false, false); - - _mouse_mode_tearoff = manage (new TearOff (*mode_box)); - _mouse_mode_tearoff->set_name ("MouseModeBase"); - _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false); - - if (Profile->get_sae() || Profile->get_mixbus() ) { - _mouse_mode_tearoff->set_can_be_torn_off (false); + mode_box->pack_start (*(manage (new ArdourVSpacer ())), false, false, 3); + mode_box->pack_start (edit_point_selector, false, false); + mode_box->pack_start (*(manage (new ArdourVSpacer ())), false, false, 3); } - _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast(&toolbar_hbox), - &_mouse_mode_tearoff->tearoff_window())); - _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast (&toolbar_hbox), - &_mouse_mode_tearoff->tearoff_window(), 1)); - _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast(&toolbar_hbox), - &_mouse_mode_tearoff->tearoff_window())); - _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast (&toolbar_hbox), - &_mouse_mode_tearoff->tearoff_window(), 1)); + mode_box->pack_start (*mouse_mode_box, false, false); /* Zoom */ @@ -2915,8 +2986,7 @@ Editor::setup_toolbar () RefPtr act; zoom_preset_selector.set_name ("zoom button"); - zoom_preset_selector.set_image(::get_icon ("time_exp")); - zoom_preset_selector.set_size_request (42, -1); + zoom_preset_selector.set_icon (ArdourIcon::ZoomExpand); zoom_in_button.set_name ("zoom button"); zoom_in_button.set_icon (ArdourIcon::ZoomIn); @@ -2948,10 +3018,12 @@ Editor::setup_toolbar () } /* Track zoom buttons */ + _track_box.set_spacing (2); + _track_box.set_border_width (2); + visible_tracks_selector.set_name ("zoom button"); if (Profile->get_mixbus()) { - visible_tracks_selector.set_image(::get_icon ("tav_exp")); - visible_tracks_selector.set_size_request (42, -1); + visible_tracks_selector.set_icon (ArdourIcon::TimeAxisExpand); } else { set_size_request_to_display_given_text (visible_tracks_selector, _("All"), 30, 2); } @@ -2967,45 +3039,27 @@ Editor::setup_toolbar () tav_shrink_button.set_related_action (act); if (ARDOUR::Profile->get_mixbus()) { - _zoom_box.pack_start (visible_tracks_selector); + _track_box.pack_start (visible_tracks_selector); } else if (ARDOUR::Profile->get_trx()) { - _zoom_box.pack_start (tav_shrink_button); - _zoom_box.pack_start (tav_expand_button); + _track_box.pack_start (tav_shrink_button); + _track_box.pack_start (tav_expand_button); } else { - _zoom_box.pack_start (visible_tracks_selector); - _zoom_box.pack_start (tav_shrink_button); - _zoom_box.pack_start (tav_expand_button); - } - - if (!ARDOUR::Profile->get_trx()) { - _zoom_tearoff = manage (new TearOff (_zoom_box)); - - _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast(&toolbar_hbox), - &_zoom_tearoff->tearoff_window())); - _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast (&toolbar_hbox), - &_zoom_tearoff->tearoff_window(), 0)); - _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast(&toolbar_hbox), - &_zoom_tearoff->tearoff_window())); - _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast (&toolbar_hbox), - &_zoom_tearoff->tearoff_window(), 0)); - } - - if (Profile->get_sae() || Profile->get_mixbus() ) { - _zoom_tearoff->set_can_be_torn_off (false); + _track_box.pack_start (visible_tracks_selector); + _track_box.pack_start (tav_shrink_button); + _track_box.pack_start (tav_expand_button); } snap_box.set_spacing (2); snap_box.set_border_width (2); - snap_type_selector.set_name ("mouse mode button"); + grid_type_selector.set_name ("mouse mode button"); - snap_mode_selector.set_name ("mouse mode button"); + snap_mode_button.set_name ("mouse mode button"); edit_point_selector.set_name ("mouse mode button"); - snap_box.pack_start (snap_mode_selector, false, false); - snap_box.pack_start (snap_type_selector, false, false); - snap_box.pack_start (edit_point_selector, false, false); + snap_box.pack_start (snap_mode_button, false, false); + snap_box.pack_start (grid_type_selector, false, false); /* Nudge */ @@ -3023,53 +3077,48 @@ Editor::setup_toolbar () /* Pack everything in... */ - HBox* hbox = manage (new HBox); - hbox->set_spacing(2); + toolbar_hbox.set_spacing (2); + toolbar_hbox.set_border_width (2); - _tools_tearoff = manage (new TearOff (*hbox)); - _tools_tearoff->set_name ("MouseModeBase"); - _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false); + ArdourWidgets::ArdourDropShadow *tool_shadow = manage (new (ArdourWidgets::ArdourDropShadow)); + tool_shadow->set_size_request( 4, -1 ); + tool_shadow->show(); - if (Profile->get_sae() || Profile->get_mixbus()) { - _tools_tearoff->set_can_be_torn_off (false); - } + ebox_hpacker.pack_start (*tool_shadow, false, false); + ebox_hpacker.pack_start(ebox_vpacker, true, true); - _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast(&toolbar_hbox), - &_tools_tearoff->tearoff_window())); - _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast (&toolbar_hbox), - &_tools_tearoff->tearoff_window(), 0)); - _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast(&toolbar_hbox), - &_tools_tearoff->tearoff_window())); - _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast (&toolbar_hbox), - &_tools_tearoff->tearoff_window(), 0)); + Gtk::EventBox* spacer = manage (new Gtk::EventBox); //extra space under the mouse toolbar, for aesthetics + spacer->set_name("EditorWindow"); + spacer->set_size_request(-1,4); + spacer->show(); - toolbar_hbox.set_spacing (2); - toolbar_hbox.set_border_width (1); + ebox_vpacker.pack_start(toolbar_hbox, false, false); + ebox_vpacker.pack_start(*spacer, false, false); + ebox_vpacker.show(); - toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false); - if (!ARDOUR::Profile->get_trx()) { - toolbar_hbox.pack_start (*_zoom_tearoff, false, false); - toolbar_hbox.pack_start (*_tools_tearoff, false, false); - } + toolbar_hbox.pack_start (*mode_box, false, false); if (!ARDOUR::Profile->get_trx()) { - hbox->pack_start (snap_box, false, false); - hbox->pack_start (*nudge_box, false, false); - } - hbox->pack_start (panic_box, false, false); - hbox->show_all (); + toolbar_hbox.pack_start (*(manage (new ArdourVSpacer ())), false, false, 3); + + toolbar_hbox.pack_start (snap_box, false, false); + + toolbar_hbox.pack_start (*(manage (new ArdourVSpacer ())), false, false, 3); - toolbar_base.set_name ("ToolBarBase"); - toolbar_base.add (toolbar_hbox); + toolbar_hbox.pack_start (*nudge_box, false, false); - _toolbar_viewport.add (toolbar_base); - /* stick to the required height but allow width to vary if there's not enough room */ - _toolbar_viewport.set_size_request (1, -1); + //zoom tools on right ege - toolbar_frame.set_shadow_type (SHADOW_OUT); - toolbar_frame.set_name ("BaseFrame"); - toolbar_frame.add (_toolbar_viewport); + toolbar_hbox.pack_end (_zoom_box, false, false, 2); + + toolbar_hbox.pack_end (*(manage (new ArdourVSpacer ())), false, false, 3); + + toolbar_hbox.pack_end (_track_box, false, false); + + } + + toolbar_hbox.show_all (); } void @@ -3099,55 +3148,58 @@ Editor::build_edit_mode_menu () } void -Editor::build_snap_mode_menu () +Editor::build_grid_type_menu () { using namespace Menu_Helpers; - snap_mode_selector.AddMenuElem (MenuElem ( snap_mode_strings[(int)SnapOff], sigc::bind (sigc::mem_fun(*this, &Editor::snap_mode_selection_done), (SnapMode) SnapOff))); - snap_mode_selector.AddMenuElem (MenuElem ( snap_mode_strings[(int)SnapNormal], sigc::bind (sigc::mem_fun(*this, &Editor::snap_mode_selection_done), (SnapMode) SnapNormal))); - snap_mode_selector.AddMenuElem (MenuElem ( snap_mode_strings[(int)SnapMagnetic], sigc::bind (sigc::mem_fun(*this, &Editor::snap_mode_selection_done), (SnapMode) SnapMagnetic))); + //main grid: bars, quarter-notes, etc + grid_type_selector.AddMenuElem (MenuElem ( grid_type_strings[(int)GridTypeNone], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeNone))); + grid_type_selector.AddMenuElem (MenuElem ( grid_type_strings[(int)GridTypeBar], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBar))); + grid_type_selector.AddMenuElem (MenuElem ( grid_type_strings[(int)GridTypeBeat], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeat))); + grid_type_selector.AddMenuElem (MenuElem ( grid_type_strings[(int)GridTypeBeatDiv2], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv2))); + grid_type_selector.AddMenuElem (MenuElem ( grid_type_strings[(int)GridTypeBeatDiv4], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv4))); + grid_type_selector.AddMenuElem (MenuElem ( grid_type_strings[(int)GridTypeBeatDiv8], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv8))); + grid_type_selector.AddMenuElem (MenuElem ( grid_type_strings[(int)GridTypeBeatDiv16], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv16))); + grid_type_selector.AddMenuElem (MenuElem ( grid_type_strings[(int)GridTypeBeatDiv32], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv32))); + + //triplet grid + grid_type_selector.AddMenuElem(SeparatorElem()); + Gtk::Menu *_triplet_menu = manage (new Menu); + MenuList& triplet_items (_triplet_menu->items()); + { + triplet_items.push_back( MenuElem( grid_type_strings[(int)GridTypeBeatDiv3], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv3) )); + triplet_items.push_back( MenuElem( grid_type_strings[(int)GridTypeBeatDiv6], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv6) )); + triplet_items.push_back( MenuElem( grid_type_strings[(int)GridTypeBeatDiv12], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv12) )); + triplet_items.push_back( MenuElem( grid_type_strings[(int)GridTypeBeatDiv24], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv24) )); + } + grid_type_selector.AddMenuElem (Menu_Helpers::MenuElem (_("Triplets"), *_triplet_menu)); - set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_TRIANGLE_WIDTH, 2); -} + //quintuplet grid + Gtk::Menu *_quintuplet_menu = manage (new Menu); + MenuList& quintuplet_items (_quintuplet_menu->items()); + { + quintuplet_items.push_back( MenuElem( grid_type_strings[(int)GridTypeBeatDiv5], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv5) )); + quintuplet_items.push_back( MenuElem( grid_type_strings[(int)GridTypeBeatDiv10], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv10) )); + quintuplet_items.push_back( MenuElem( grid_type_strings[(int)GridTypeBeatDiv20], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv20) )); + } + grid_type_selector.AddMenuElem (Menu_Helpers::MenuElem (_("Quintuplets"), *_quintuplet_menu)); -void -Editor::build_snap_type_menu () -{ - using namespace Menu_Helpers; + //septuplet grid + Gtk::Menu *_septuplet_menu = manage (new Menu); + MenuList& septuplet_items (_septuplet_menu->items()); + { + septuplet_items.push_back( MenuElem( grid_type_strings[(int)GridTypeBeatDiv7], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv7) )); + septuplet_items.push_back( MenuElem( grid_type_strings[(int)GridTypeBeatDiv14], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv14) )); + septuplet_items.push_back( MenuElem( grid_type_strings[(int)GridTypeBeatDiv28], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeBeatDiv28) )); + } + grid_type_selector.AddMenuElem (Menu_Helpers::MenuElem (_("Septuplets"), *_septuplet_menu)); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToCDFrame], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToCDFrame))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToTimecodeFrame], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToTimecodeFrame))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToTimecodeSeconds], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToTimecodeSeconds))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToTimecodeMinutes], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToTimecodeMinutes))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToSeconds], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToSeconds))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToMinutes], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToMinutes))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToBeatDiv128], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToBeatDiv128))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToBeatDiv64], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToBeatDiv64))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToBeatDiv32], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToBeatDiv32))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToBeatDiv28], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToBeatDiv28))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToBeatDiv24], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToBeatDiv24))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToBeatDiv20], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToBeatDiv20))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToBeatDiv16], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToBeatDiv16))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToBeatDiv14], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToBeatDiv14))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToBeatDiv12], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToBeatDiv12))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToBeatDiv10], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToBeatDiv10))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToBeatDiv8], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToBeatDiv8))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToBeatDiv7], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToBeatDiv7))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToBeatDiv6], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToBeatDiv6))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToBeatDiv5], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToBeatDiv5))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToBeatDiv4], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToBeatDiv4))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToBeatDiv3], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToBeatDiv3))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToBeatDiv2], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToBeatDiv2))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToBeat], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToBeat))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToBar], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToBar))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToMark], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToMark))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToRegionStart], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToRegionStart))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToRegionEnd], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToRegionEnd))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToRegionSync], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToRegionSync))); - snap_type_selector.AddMenuElem (MenuElem ( snap_type_strings[(int)SnapToRegionBoundary], sigc::bind (sigc::mem_fun(*this, &Editor::snap_type_selection_done), (SnapType) SnapToRegionBoundary))); - - set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_TRIANGLE_WIDTH, 2); + grid_type_selector.AddMenuElem(SeparatorElem()); + grid_type_selector.AddMenuElem (MenuElem ( grid_type_strings[(int)GridTypeSmpte], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeSmpte))); + grid_type_selector.AddMenuElem (MenuElem ( grid_type_strings[(int)GridTypeMinSec], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeMinSec))); + grid_type_selector.AddMenuElem (MenuElem ( grid_type_strings[(int)GridTypeSamples], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeSamples))); + set_size_request_to_display_given_text (grid_type_selector, _("Long Grid"), COMBO_TRIANGLE_WIDTH, 2); //problem: some of the rarely-used grid names are very long. Just do something arbitary, translators: rename this if needed } void @@ -3172,8 +3224,8 @@ Editor::setup_tooltips () set_tooltip (tav_expand_button, _("Expand Tracks")); set_tooltip (tav_shrink_button, _("Shrink Tracks")); set_tooltip (visible_tracks_selector, _("Number of visible tracks")); - set_tooltip (snap_type_selector, _("Snap/Grid Units")); - set_tooltip (snap_mode_selector, _("Snap/Grid Mode")); + set_tooltip (grid_type_selector, _("Grid Mode")); + set_tooltip (snap_mode_button, _("Snap Mode\n\nRight-click to visit Snap preferences.")); set_tooltip (edit_point_selector, _("Edit Point")); set_tooltip (edit_mode_selector, _("Edit Mode")); set_tooltip (nudge_clock, _("Nudge Clock\n(controls distance used to nudge regions and selections)")); @@ -3281,6 +3333,15 @@ Editor::map_transport_state () update_loop_range_view (); } +void +Editor::transport_looped () +{ + /* reset Playhead position interpolation. + * see Editor::super_rapid_screen_update + */ + _last_update_time = 0; +} + /* UNDO/REDO */ void @@ -3515,7 +3576,7 @@ Editor::duplicate_range (bool with_dialog) times = adjustment.get_value(); } - if ((current_mouse_mode() == Editing::MouseRange)) { + if ((current_mouse_mode() == MouseRange)) { if (selection->time.length()) { duplicate_selection (times); } @@ -3540,11 +3601,7 @@ Editor::cycle_edit_mode () { switch (Config->get_edit_mode()) { case Slide: - if (Profile->get_sae()) { - Config->set_edit_mode (Lock); - } else { - Config->set_edit_mode (Ripple); - } + Config->set_edit_mode (Ripple); break; case Splice: case Ripple: @@ -3563,9 +3620,9 @@ Editor::edit_mode_selection_done ( EditMode m ) } void -Editor::snap_type_selection_done (SnapType snaptype) +Editor::grid_type_selection_done (GridType gridtype) { - RefPtr ract = snap_type_action (snaptype); + RefPtr ract = grid_type_action (gridtype); if (ract) { ract->set_active (); } @@ -3675,7 +3732,8 @@ Editor::build_track_count_menu () zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 8 hours"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 8 * 60 * 60 * 1000))); zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 24 hours"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 24 * 60 * 60 * 1000))); zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Session"), sigc::mem_fun(*this, &Editor::temporal_zoom_session))); - zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Range/Region Selection"), sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_selection), false))); + zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Extents"), sigc::mem_fun(*this, &Editor::temporal_zoom_extents))); + zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to Range/Region Selection"), sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_selection), Horizontal))); } } @@ -3687,7 +3745,7 @@ Editor::set_zoom_preset (int64_t ms) return; } - ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate(); + ARDOUR::samplecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate(); temporal_zoom( (sample_rate * ms / 1000) / _visible_canvas_width ); } @@ -3717,11 +3775,21 @@ Editor::set_visible_track_count (int32_t n) str = s.str(); } else if (_visible_track_count == 0) { uint32_t n = 0; - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { + for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) { if ((*i)->marked_for_display()) { ++n; + TimeAxisView::Children cl ((*i)->get_child_list ()); + for (TimeAxisView::Children::const_iterator j = cl.begin(); j != cl.end(); ++j) { + if ((*j)->marked_for_display()) { + ++n; + } + } } } + if (n == 0) { + visible_tracks_selector.set_text (X_("*")); + return; + } h = trackviews_height() / n; str = _("All"); } else { @@ -3752,7 +3820,7 @@ bool Editor::edit_controls_button_release (GdkEventButton* ev) { if (Keyboard::is_context_menu_event (ev)) { - ARDOUR_UI::instance()->add_route (current_toplevel()); + ARDOUR_UI::instance()->add_route (); } else if (ev->button == 1) { selection->clear_tracks (); } @@ -3813,110 +3881,18 @@ Editor::cycle_zoom_focus () } void -Editor::pane_allocation_handler (Allocation &alloc, Paned* which) -{ - /* recover or initialize pane positions. do this here rather than earlier because - we don't want the positions to change the child allocations, which they seem to do. - */ - - int pos; - XMLProperty* prop; - char buf[32]; - XMLNode* node = ARDOUR_UI::instance()->editor_settings(); - - enum Pane { - Horizontal = 0x1, - Vertical = 0x2 - }; - - static Pane done; - - XMLNode* geometry = find_named_node (*node, "geometry"); - - if (which == static_cast (&edit_pane)) { - - if (done & Horizontal) { - return; - } - - if (geometry && (prop = geometry->property ("notebook-shrunk"))) { - _notebook_shrunk = string_is_affirmative (prop->value ()); - } - - if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) { - /* initial allocation is 90% to canvas, 10% to notebook */ - pos = (int) floor (alloc.get_width() * 0.90f); - snprintf (buf, sizeof(buf), "%d", pos); - } else { - pos = atoi (prop->value()); - } - - if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) { - edit_pane.set_position (pos); - } - - done = (Pane) (done | Horizontal); - - } else if (which == static_cast (&editor_summary_pane)) { - - if (done & Vertical) { - return; - } - - if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) { - /* initial allocation is 90% to canvas, 10% to summary */ - pos = (int) floor (alloc.get_height() * 0.90f); - snprintf (buf, sizeof(buf), "%d", pos); - } else { - - pos = atoi (prop->value()); - } - - if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) { - editor_summary_pane.set_position (pos); - } - - done = (Pane) (done | Vertical); - } -} - -void -Editor::detach_tearoff (Box* /*b*/, Window* /*w*/) -{ - if ((_tools_tearoff->torn_off() || !_tools_tearoff->visible()) && - (_mouse_mode_tearoff->torn_off() || !_mouse_mode_tearoff->visible()) && - (_zoom_tearoff && (_zoom_tearoff->torn_off() || !_zoom_tearoff->visible()))) { - top_hbox.remove (toolbar_frame); - } -} - -void -Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/) +Editor::update_grid () { - if (toolbar_frame.get_parent() == 0) { - top_hbox.pack_end (toolbar_frame); - } -} - -void -Editor::set_show_measures (bool yn) -{ - if (_show_measures != yn) { - hide_measures (); - - if ((_show_measures = yn) == true) { - if (tempo_lines) { - tempo_lines->show(); - } - - ARDOUR::TempoMap::BBTPointList::const_iterator begin; - ARDOUR::TempoMap::BBTPointList::const_iterator end; - - compute_current_bbt_points (leftmost_frame, leftmost_frame + current_page_samples(), begin, end); - draw_measures (begin, end); + if ( grid_musical() ) { + std::vector grid; + if (bbt_ruler_scale != bbt_show_many) { + compute_current_bbt_points (grid, _leftmost_sample, _leftmost_sample + current_page_samples()); } - - instant_save (); + maybe_draw_grid_lines (); + } else if ( grid_nonmusical() ) { + maybe_draw_grid_lines (); + } else { + hide_grid_lines (); } } @@ -3974,8 +3950,8 @@ Editor::playlist_selector () const return *_playlist_selector; } -framecnt_t -Editor::get_paste_offset (framepos_t pos, unsigned paste_count, framecnt_t duration) +samplecnt_t +Editor::get_paste_offset (samplepos_t pos, unsigned paste_count, samplecnt_t duration) { if (paste_count == 0) { /* don't bother calculating an offset that will be zero anyway */ @@ -3983,58 +3959,105 @@ Editor::get_paste_offset (framepos_t pos, unsigned paste_count, framecnt_t durat } /* calculate basic unsnapped multi-paste offset */ - framecnt_t offset = paste_count * duration; + samplecnt_t offset = paste_count * duration; /* snap offset so pos + offset is aligned to the grid */ - framepos_t offset_pos = pos + offset; + MusicSample offset_pos (pos + offset, 0); snap_to(offset_pos, RoundUpMaybe); - offset = offset_pos - pos; + offset = offset_pos.sample - pos; return offset; } unsigned -Editor::get_grid_beat_divisions(framepos_t position) -{ - switch (_snap_type) { - case SnapToBeatDiv128: return 128; - case SnapToBeatDiv64: return 64; - case SnapToBeatDiv32: return 32; - case SnapToBeatDiv28: return 28; - case SnapToBeatDiv24: return 24; - case SnapToBeatDiv20: return 20; - case SnapToBeatDiv16: return 16; - case SnapToBeatDiv14: return 14; - case SnapToBeatDiv12: return 12; - case SnapToBeatDiv10: return 10; - case SnapToBeatDiv8: return 8; - case SnapToBeatDiv7: return 7; - case SnapToBeatDiv6: return 6; - case SnapToBeatDiv5: return 5; - case SnapToBeatDiv4: return 4; - case SnapToBeatDiv3: return 3; - case SnapToBeatDiv2: return 2; - default: return 0; +Editor::get_grid_beat_divisions(samplepos_t position) +{ + switch (_grid_type) { + case GridTypeBeatDiv32: return 32; + case GridTypeBeatDiv28: return 28; + case GridTypeBeatDiv24: return 24; + case GridTypeBeatDiv20: return 20; + case GridTypeBeatDiv16: return 16; + case GridTypeBeatDiv14: return 14; + case GridTypeBeatDiv12: return 12; + case GridTypeBeatDiv10: return 10; + case GridTypeBeatDiv8: return 8; + case GridTypeBeatDiv7: return 7; + case GridTypeBeatDiv6: return 6; + case GridTypeBeatDiv5: return 5; + case GridTypeBeatDiv4: return 4; + case GridTypeBeatDiv3: return 3; + case GridTypeBeatDiv2: return 2; + + case GridTypeNone: return 0; + case GridTypeSmpte: return 0; + case GridTypeMinSec: return 0; + case GridTypeSamples: return 0; + default: return 0; + } + return 0; +} + +/** returns the current musical grid divisiions using the supplied modifier mask from a GtkEvent. + if the grid is non-musical, returns 0. + if the grid is snapped to bars, returns -1. + @param event_state the current keyboard modifier mask. +*/ +int32_t +Editor::get_grid_music_divisions (uint32_t event_state) +{ + if (snap_mode() == SnapOff && !ArdourKeyboard::indicates_snap (event_state)) { + return 0; + } + + if (snap_mode() != SnapOff && ArdourKeyboard::indicates_snap (event_state)) { + return 0; + } + + switch (_grid_type) { + case GridTypeBeatDiv32: return 32; + case GridTypeBeatDiv28: return 28; + case GridTypeBeatDiv24: return 24; + case GridTypeBeatDiv20: return 20; + case GridTypeBeatDiv16: return 16; + case GridTypeBeatDiv14: return 14; + case GridTypeBeatDiv12: return 12; + case GridTypeBeatDiv10: return 10; + case GridTypeBeatDiv8: return 8; + case GridTypeBeatDiv7: return 7; + case GridTypeBeatDiv6: return 6; + case GridTypeBeatDiv5: return 5; + case GridTypeBeatDiv4: return 4; + case GridTypeBeatDiv3: return 3; + case GridTypeBeatDiv2: return 2; + case GridTypeBeat: return 1; + case GridTypeBar : return -1; + + case GridTypeNone: return 0; + case GridTypeSmpte: return 0; + case GridTypeMinSec: return 0; + case GridTypeSamples: return 0; } return 0; } -Evoral::Beats -Editor::get_grid_type_as_beats (bool& success, framepos_t position) +Temporal::Beats +Editor::get_grid_type_as_beats (bool& success, samplepos_t position) { success = true; const unsigned divisions = get_grid_beat_divisions(position); if (divisions) { - return Evoral::Beats(1.0 / (double)get_grid_beat_divisions(position)); + return Temporal::Beats(1.0 / (double)get_grid_beat_divisions(position)); } - switch (_snap_type) { - case SnapToBeat: - return Evoral::Beats(1.0); - case SnapToBar: + switch (_grid_type) { + case GridTypeBeat: + return Temporal::Beats(4.0 / _session->tempo_map().meter_at_sample (position).note_divisor()); + case GridTypeBar: if (_session) { - return Evoral::Beats(_session->tempo_map().meter_at (position).divisions_per_bar()); + const Meter& m = _session->tempo_map().meter_at_sample (position); + return Temporal::Beats((4.0 * m.divisions_per_bar()) / m.note_divisor()); } break; default: @@ -4042,13 +4065,13 @@ Editor::get_grid_type_as_beats (bool& success, framepos_t position) break; } - return Evoral::Beats(); + return Temporal::Beats(); } -framecnt_t -Editor::get_nudge_distance (framepos_t pos, framecnt_t& next) +samplecnt_t +Editor::get_nudge_distance (samplepos_t pos, samplecnt_t& next) { - framecnt_t ret; + samplecnt_t ret; ret = nudge_clock->current_duration (pos); next = ret + 1; /* XXXX fix me */ @@ -4108,7 +4131,7 @@ Editor::playlist_deletion_dialog (boost::shared_ptr pl) } bool -Editor::audio_region_selection_covers (framepos_t where) +Editor::audio_region_selection_covers (samplepos_t where) { for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) { if ((*a)->region()->covers (where)) { @@ -4177,25 +4200,6 @@ Editor::session_state_saved (string) _snapshots->redisplay (); } -void -Editor::update_tearoff_visibility() -{ - bool visible = UIConfiguration::instance().get_keep_tearoffs(); - _mouse_mode_tearoff->set_visible (visible); - _tools_tearoff->set_visible (visible); - if (_zoom_tearoff) { - _zoom_tearoff->set_visible (visible); - } -} - -void -Editor::reattach_all_tearoffs () -{ - if (_mouse_mode_tearoff) _mouse_mode_tearoff->put_it_back (); - if (_tools_tearoff) _tools_tearoff->put_it_back (); - if (_zoom_tearoff) _zoom_tearoff->put_it_back (); -} - void Editor::maximise_editing_space () { @@ -4203,9 +4207,12 @@ Editor::maximise_editing_space () return; } - current_toplevel()->fullscreen (); + Gtk::Window* toplevel = current_toplevel(); - _maximised = true; + if (toplevel) { + toplevel->fullscreen (); + _maximised = true; + } } void @@ -4215,9 +4222,12 @@ Editor::restore_editing_space () return; } - current_toplevel()->unfullscreen(); + Gtk::Window* toplevel = current_toplevel(); - _maximised = false; + if (toplevel) { + toplevel->unfullscreen(); + _maximised = false; + } } /** @@ -4232,7 +4242,7 @@ Editor::new_playlists (TimeAxisView* v) begin_reversible_command (_("new playlists")); vector > playlists; _session->playlists->get (playlists); - mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::select.property_id); + mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::group_select.property_id); commit_reversible_command (); } @@ -4248,7 +4258,7 @@ Editor::copy_playlists (TimeAxisView* v) begin_reversible_command (_("copy playlists")); vector > playlists; _session->playlists->get (playlists); - mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::select.property_id); + mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::group_select.property_id); commit_reversible_command (); } @@ -4263,20 +4273,20 @@ Editor::clear_playlists (TimeAxisView* v) begin_reversible_command (_("clear playlists")); vector > playlists; _session->playlists->get (playlists); - mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::select.property_id); + mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::group_select.property_id); commit_reversible_command (); } void Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector > const & playlists) { - atv.use_new_playlist (sz > 1 ? false : true, playlists); + atv.use_new_playlist (sz > 1 ? false : true, playlists, false); } void Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector > const & playlists) { - atv.use_copy_playlist (sz > 1 ? false : true, playlists); + atv.use_new_playlist (sz > 1 ? false : true, playlists, true); } void @@ -4292,13 +4302,13 @@ Editor::get_y_origin () const } /** Queue up a change to the viewport x origin. - * @param frame New x origin. + * @param sample New x origin. */ void -Editor::reset_x_origin (framepos_t frame) +Editor::reset_x_origin (samplepos_t sample) { pending_visual_change.add (VisualChange::TimeOrigin); - pending_visual_change.time_origin = frame; + pending_visual_change.time_origin = sample; ensure_visual_change_idle_handler (); } @@ -4311,7 +4321,7 @@ Editor::reset_y_origin (double y) } void -Editor::reset_zoom (framecnt_t spp) +Editor::reset_zoom (samplecnt_t spp) { if (spp == samples_per_pixel) { return; @@ -4323,9 +4333,9 @@ Editor::reset_zoom (framecnt_t spp) } void -Editor::reposition_and_zoom (framepos_t frame, double fpu) +Editor::reposition_and_zoom (samplepos_t sample, double fpu) { - reset_x_origin (frame); + reset_x_origin (sample); reset_zoom (fpu); if (!no_save_visual) { @@ -4349,11 +4359,11 @@ Editor::current_visual_state (bool with_tracks) VisualState* vs = new VisualState (with_tracks); vs->y_position = vertical_adjustment.get_value(); vs->samples_per_pixel = samples_per_pixel; - vs->leftmost_frame = leftmost_frame; + vs->_leftmost_sample = _leftmost_sample; vs->zoom_focus = zoom_focus; if (with_tracks) { - *vs->gui_state = *ARDOUR_UI::instance()->gui_object_state; + vs->gui_state->set_state (ARDOUR_UI::instance()->gui_object_state->get_state()); } return vs; @@ -4415,10 +4425,10 @@ Editor::use_visual_state (VisualState& vs) vertical_adjustment.set_value (vs.y_position); set_zoom_focus (vs.zoom_focus); - reposition_and_zoom (vs.leftmost_frame, vs.samples_per_pixel); + reposition_and_zoom (vs._leftmost_sample, vs.samples_per_pixel); if (vs.gui_state) { - *ARDOUR_UI::instance()->gui_object_state = *vs.gui_state; + ARDOUR_UI::instance()->gui_object_state->set_state (vs.gui_state->get_state()); for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { (*i)->clear_property_cache(); @@ -4434,14 +4444,14 @@ Editor::use_visual_state (VisualState& vs) * @param spp new number of samples per pixel */ void -Editor::set_samples_per_pixel (framecnt_t spp) +Editor::set_samples_per_pixel (samplecnt_t spp) { if (spp < 1) { return; } - const framecnt_t three_days = 3 * 24 * 60 * 60 * (_session ? _session->frame_rate() : 48000); - const framecnt_t lots_of_pixels = 4000; + const samplecnt_t three_days = 3 * 24 * 60 * 60 * (_session ? _session->sample_rate() : 48000); + const samplecnt_t lots_of_pixels = 4000; /* if the zoom level is greater than what you'd get trying to display 3 * days of audio on a really big screen, then it's too big. @@ -4452,14 +4462,14 @@ Editor::set_samples_per_pixel (framecnt_t spp) } samples_per_pixel = spp; +} - if (tempo_lines) { - tempo_lines->tempo_map_changed(); - } - +void +Editor::on_samples_per_pixel_changed () +{ bool const showing_time_selection = selection->time.length() > 0; - if (showing_time_selection && selection->time.start () != selection->time.end_frame ()) { + if (showing_time_selection && selection->time.start () != selection->time.end_sample ()) { for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { (*i)->reshow_selection (selection->time); } @@ -4475,7 +4485,7 @@ Editor::set_samples_per_pixel (framecnt_t spp) } if (playhead_cursor) { - playhead_cursor->set_position (playhead_cursor->current_frame ()); + playhead_cursor->set_position (playhead_cursor->current_sample ()); } refresh_location_display(); @@ -4486,16 +4496,16 @@ Editor::set_samples_per_pixel (framecnt_t spp) instant_save (); } +samplepos_t +Editor::playhead_cursor_sample () const +{ + return playhead_cursor->current_sample(); +} + void Editor::queue_visual_videotimeline_update () { - /* TODO: - * pending_visual_change.add (VisualChange::VideoTimeline); - * or maybe even more specific: which videotimeline-image - * currently it calls update_video_timeline() to update - * _all outdated_ images on the video-timeline. - * see 'exposeimg()' in video_image_frame.cc - */ + pending_visual_change.add (VisualChange::VideoTimeline); ensure_visual_change_idle_handler (); } @@ -4515,9 +4525,25 @@ Editor::_idle_visual_changer (void* arg) return static_cast(arg)->idle_visual_changer (); } +void +Editor::pre_render () +{ + visual_change_queued = false; + + if (pending_visual_change.pending != 0) { + ensure_visual_change_idle_handler(); + } +} + int Editor::idle_visual_changer () { + pending_visual_change.idle_handler_id = -1; + + if (pending_visual_change.pending == 0) { + return 0; + } + /* set_horizontal_position() below (and maybe other calls) call gtk_main_iteration(), so it's possible that a signal will be handled half-way through this method. If this signal wants an @@ -4528,7 +4554,10 @@ Editor::idle_visual_changer () the last one. */ - pending_visual_change.idle_handler_id = -1; + if (visual_change_queued) { + return 0; + } + pending_visual_change.being_handled = true; VisualChange vc = pending_visual_change; @@ -4539,46 +4568,63 @@ Editor::idle_visual_changer () pending_visual_change.being_handled = false; + visual_change_queued = true; + return 0; /* this is always a one-shot call */ } void Editor::visual_changer (const VisualChange& vc) { - double const last_time_origin = horizontal_position (); - + /** + * Changed first so the correct horizontal canvas position is calculated in + * Editor::set_horizontal_position + */ if (vc.pending & VisualChange::ZoomLevel) { set_samples_per_pixel (vc.samples_per_pixel); - - compute_fixed_ruler_scale (); - - ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_begin; - ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_end; - - compute_current_bbt_points (vc.time_origin, pending_visual_change.time_origin + current_page_samples(), - current_bbt_points_begin, current_bbt_points_end); - compute_bbt_ruler_scale (vc.time_origin, pending_visual_change.time_origin + current_page_samples(), - current_bbt_points_begin, current_bbt_points_end); - update_tempo_based_rulers (current_bbt_points_begin, current_bbt_points_end); - - update_video_timeline(); } if (vc.pending & VisualChange::TimeOrigin) { - set_horizontal_position (vc.time_origin / samples_per_pixel); + double new_time_origin = sample_to_pixel_unrounded (vc.time_origin); + set_horizontal_position (new_time_origin); } if (vc.pending & VisualChange::YOrigin) { vertical_adjustment.set_value (vc.y_origin); } - if (last_time_origin == horizontal_position ()) { - /* changed signal not emitted */ - update_fixed_rulers (); - redisplay_tempo (true); + /** + * Now the canvas is in the final state before render the canvas items that + * support the Item::prepare_for_render interface can calculate the correct + * item to visible canvas intersection. + */ + if (vc.pending & VisualChange::ZoomLevel) { + on_samples_per_pixel_changed (); + + compute_fixed_ruler_scale (); + + compute_bbt_ruler_scale (vc.time_origin, pending_visual_change.time_origin + current_page_samples()); + update_tempo_based_rulers (); } if (!(vc.pending & VisualChange::ZoomLevel)) { + /** + * If the canvas is not being zoomed then the canvas items will not change + * and cause Item::prepare_for_render to be called so do it here manually. + * + * Not ideal, but I can't think of a better solution atm. + */ + _track_canvas->prepare_for_render(); + } + + // If we are only scrolling vertically there is no need to update these + if (vc.pending != VisualChange::YOrigin) { + update_fixed_rulers (); + redisplay_grid (true); + + /* video frames & position need to be updated for zoom, horiz-scroll + * and (explicitly) VisualChange::VideoTimeline. + */ update_video_timeline(); } @@ -4598,25 +4644,27 @@ Editor::sort_track_selection (TrackViewList& sel) sel.sort (cmp); } -framepos_t +samplepos_t Editor::get_preferred_edit_position (EditIgnoreOption ignore, bool from_context_menu, bool from_outside_canvas) { bool ignored; - framepos_t where = 0; + samplepos_t where = 0; EditPoint ep = _edit_point; - if (Profile->get_mixbus()) - if (ep == EditAtSelectedMarker) + if (Profile->get_mixbus()) { + if (ep == EditAtSelectedMarker) { ep = EditAtPlayhead; + } + } if (from_outside_canvas && (ep == EditAtMouse)) { ep = EditAtPlayhead; } else if (from_context_menu && (ep == EditAtMouse)) { - return canvas_event_sample (&context_click_event, 0, 0); + return canvas_event_sample (&context_click_event, 0, 0); } if (entered_marker) { - DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position())); + DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position())); return entered_marker->position(); } @@ -4628,14 +4676,16 @@ Editor::get_preferred_edit_position (EditIgnoreOption ignore, bool from_context_ ep = EditAtPlayhead; } + MusicSample snap_mf (0, 0); + switch (ep) { case EditAtPlayhead: - if (_dragging_playhead) { + if (_dragging_playhead && _control_scroll_target) { where = *_control_scroll_target; } else { - where = _session->audible_frame(); + where = _session->audible_sample(); } - DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where)); + DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where)); break; case EditAtSelectedMarker: @@ -4648,7 +4698,7 @@ Editor::get_preferred_edit_position (EditIgnoreOption ignore, bool from_context_ } else { where = loc->end(); } - DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where)); + DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where)); break; } } @@ -4656,12 +4706,14 @@ Editor::get_preferred_edit_position (EditIgnoreOption ignore, bool from_context_ default: case EditAtMouse: - if (!mouse_frame (where, ignored)) { + if (!mouse_sample (where, ignored)) { /* XXX not right but what can we do ? */ return 0; } - snap_to (where); - DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where)); + snap_mf.sample = where; + snap_to (snap_mf); + where = snap_mf.sample; + DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where)); break; } @@ -4669,7 +4721,7 @@ Editor::get_preferred_edit_position (EditIgnoreOption ignore, bool from_context_ } void -Editor::set_loop_range (framepos_t start, framepos_t end, string cmd) +Editor::set_loop_range (samplepos_t start, samplepos_t end, string cmd) { if (!_session) return; @@ -4678,7 +4730,7 @@ Editor::set_loop_range (framepos_t start, framepos_t end, string cmd) Location* tll; if ((tll = transport_loop_location()) == 0) { - Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop); + Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop, get_grid_music_divisions(0)); XMLNode &before = _session->locations()->get_state(); _session->locations()->add (loc, true); _session->set_auto_loop_location (loc); @@ -4696,7 +4748,7 @@ Editor::set_loop_range (framepos_t start, framepos_t end, string cmd) } void -Editor::set_punch_range (framepos_t start, framepos_t end, string cmd) +Editor::set_punch_range (samplepos_t start, samplepos_t end, string cmd) { if (!_session) return; @@ -4705,7 +4757,7 @@ Editor::set_punch_range (framepos_t start, framepos_t end, string cmd) Location* tpl; if ((tpl = transport_punch_location()) == 0) { - Location* loc = new Location (*_session, start, end, _("Punch"), Location::IsAutoPunch); + Location* loc = new Location (*_session, start, end, _("Punch"), Location::IsAutoPunch, get_grid_music_divisions(0)); XMLNode &before = _session->locations()->get_state(); _session->locations()->add (loc, true); _session->set_auto_punch_location (loc); @@ -4728,7 +4780,7 @@ Editor::set_punch_range (framepos_t start, framepos_t end, string cmd) * @param ts Tracks to look on; if this is empty, all tracks are examined. */ void -Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const +Editor::get_regions_at (RegionSelection& rs, samplepos_t where, const TrackViewList& ts) const { const TrackViewList* tracks; @@ -4748,8 +4800,7 @@ Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewLi if ((tr = rtv->track()) && ((pl = tr->playlist()))) { - boost::shared_ptr regions = pl->regions_at ( - (framepos_t) floor ( (double) where * tr->speed())); + boost::shared_ptr regions = pl->regions_at (where); for (RegionList::iterator i = regions->begin(); i != regions->end(); ++i) { RegionView* rv = rtv->view()->find_view (*i); @@ -4763,7 +4814,7 @@ Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewLi } void -Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const +Editor::get_regions_after (RegionSelection& rs, samplepos_t where, const TrackViewList& ts) const { const TrackViewList* tracks; @@ -4781,8 +4832,7 @@ Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackVie if ((tr = rtv->track()) && ((pl = tr->playlist()))) { - boost::shared_ptr regions = pl->regions_touched ( - (framepos_t) floor ( (double)where * tr->speed()), max_framepos); + boost::shared_ptr regions = pl->regions_touched (where, max_samplepos); for (RegionList::iterator i = regions->begin(); i != regions->end(); ++i) { @@ -4810,7 +4860,7 @@ Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackVie */ RegionSelection -Editor::get_regions_from_selection_and_edit_point () +Editor::get_regions_from_selection_and_edit_point (EditIgnoreOption ignore, bool from_context_menu, bool from_outside_canvas) { RegionSelection regions; @@ -4827,7 +4877,7 @@ Editor::get_regions_from_selection_and_edit_point () /* no region selected or entered, but some selected tracks: * act on all regions on the selected tracks at the edit point */ - framepos_t const where = get_preferred_edit_position (); + samplepos_t const where = get_preferred_edit_position (ignore, from_context_menu, from_outside_canvas); get_regions_at(regions, where, tracks); } } @@ -4847,7 +4897,7 @@ Editor::get_regions_from_selection_and_edit_point () * Note that we have forced the rule that selected regions and selected tracks are mutually exclusive */ RegionSelection -Editor::get_regions_from_selection_and_mouse (framepos_t pos) +Editor::get_regions_from_selection_and_mouse (samplepos_t pos) { RegionSelection regions; @@ -4877,7 +4927,7 @@ Editor::get_regions_from_selection_and_mouse (framepos_t pos) */ RegionSelection -Editor::get_regions_from_selection_and_entered () +Editor::get_regions_from_selection_and_entered () const { RegionSelection regions = selection->regions; @@ -4918,7 +4968,7 @@ Editor::get_regionviews_by_id (PBD::ID const id, RegionSelection & regions) cons } void -Editor::get_per_region_note_selection (list > > > > &selection) const +Editor::get_per_region_note_selection (list > > > > &selection) const { for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) { @@ -4969,6 +5019,38 @@ Editor::get_regions_corresponding_to (boost::shared_ptr region, vector region) const +{ + for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) { + RouteTimeAxisView* tatv; + if ((tatv = dynamic_cast (*i)) != 0) { + if (!tatv->track()) { + continue; + } + RegionView* marv = tatv->view()->find_view (region); + if (marv) { + return marv; + } + } + } + return NULL; +} + +RouteTimeAxisView* +Editor::rtav_from_route (boost::shared_ptr route) const +{ + for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) { + RouteTimeAxisView* rtav; + if ((rtav = dynamic_cast (*i)) != 0) { + if (rtav->route() == route) { + return rtav; + } + } + } + return NULL; +} + void Editor::show_rhythm_ferret () { @@ -4989,18 +5071,28 @@ Editor::first_idle () if (track_views.size() > 1) { Timers::TimerSuspender t; dialog = new MessageDialog ( - *current_toplevel(), string_compose (_("Please wait while %1 loads visual data."), PROGRAM_NAME), true ); dialog->present (); - ARDOUR_UI::instance()->flush_pending (); + ARDOUR_UI::instance()->flush_pending (60); } for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) { (*t)->first_idle(); } + /* now that all regionviews should exist, setup region selection */ + + RegionSelection rs; + + for (list::iterator pr = selection->regions.pending.begin (); pr != selection->regions.pending.end (); ++pr) { + /* this is cumulative: rs is NOT cleared each time */ + get_regionviews_by_id (*pr, rs); + } + + selection->set (rs); + // first idle adds route children (automation tracks), so we need to redisplay here _routes->redisplay (); @@ -5088,7 +5180,7 @@ Editor::located () ENSURE_GUI_THREAD (*this, &Editor::located); if (_session) { - playhead_cursor->set_position (_session->audible_frame ()); + playhead_cursor->set_position (_session->audible_sample ()); if (_follow_playhead && !_pending_initial_locate) { reset_x_origin_to_follow_playhead (); } @@ -5096,22 +5188,15 @@ Editor::located () _pending_locate_request = false; _pending_initial_locate = false; + _last_update_time = 0; } void Editor::region_view_added (RegionView * rv) { - for (list::iterator pr = selection->regions.pending.begin (); pr != selection->regions.pending.end (); ++pr) { - if (rv->region ()->id () == (*pr)) { - selection->add (rv); - selection->regions.pending.erase (pr); - break; - } - } - MidiRegionView* mrv = dynamic_cast (rv); if (mrv) { - list > > > >::iterator rnote; + list > >::iterator rnote; for (rnote = selection->pending_midi_note_selection.begin(); rnote != selection->pending_midi_note_selection.end(); ++rnote) { if (rv->region()->id () == (*rnote).first) { mrv->select_notes ((*rnote).second); @@ -5122,29 +5207,49 @@ Editor::region_view_added (RegionView * rv) } _summary->set_background_dirty (); + + mark_region_boundary_cache_dirty (); } void Editor::region_view_removed () { _summary->set_background_dirty (); + + mark_region_boundary_cache_dirty (); } -RouteTimeAxisView* -Editor::axis_view_from_route (boost::shared_ptr r) const +AxisView* +Editor::axis_view_by_stripable (boost::shared_ptr s) const { - TrackViewList::const_iterator j = track_views.begin (); - while (j != track_views.end()) { - RouteTimeAxisView* rtv = dynamic_cast (*j); - if (rtv && rtv->route() == r) { - return rtv; + for (TrackViewList::const_iterator j = track_views.begin (); j != track_views.end(); ++j) { + if ((*j)->stripable() == s) { + return *j; } - ++j; } return 0; } +AxisView* +Editor::axis_view_by_control (boost::shared_ptr c) const +{ + for (TrackViewList::const_iterator j = track_views.begin (); j != track_views.end(); ++j) { + if ((*j)->control() == c) { + return *j; + } + + TimeAxisView::Children kids = (*j)->get_child_list (); + + for (TimeAxisView::Children::iterator k = kids.begin(); k != kids.end(); ++k) { + if ((*k)->control() == c) { + return (*k).get(); + } + } + } + + return 0; +} TrackViewList Editor::axis_views_from_routes (boost::shared_ptr r) const @@ -5152,7 +5257,7 @@ Editor::axis_views_from_routes (boost::shared_ptr r) const TrackViewList t; for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) { - TimeAxisView* tv = axis_view_from_route (*i); + TimeAxisView* tv = time_axis_view_from_stripable (*i); if (tv) { t.push_back (tv); } @@ -5179,56 +5284,93 @@ Editor::resume_route_redisplay () } void -Editor::add_routes (RouteList& routes) +Editor::add_vcas (VCAList& vlist) +{ + StripableList sl; + + for (VCAList::iterator v = vlist.begin(); v != vlist.end(); ++v) { + sl.push_back (boost::dynamic_pointer_cast (*v)); + } + + add_stripables (sl); +} + +void +Editor::add_routes (RouteList& rlist) { - ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes) + StripableList sl; - RouteTimeAxisView *rtv; - list new_views; + for (RouteList::iterator r = rlist.begin(); r != rlist.end(); ++r) { + sl.push_back (*r); + } + + add_stripables (sl); +} + +void +Editor::add_stripables (StripableList& sl) +{ + list new_views; + boost::shared_ptr v; + boost::shared_ptr r; TrackViewList new_selection; bool from_scratch = (track_views.size() == 0); - for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) { - boost::shared_ptr route = (*x); + sl.sort (Stripable::Sorter()); - if (route->is_auditioner() || route->is_monitor()) { - continue; - } + for (StripableList::iterator s = sl.begin(); s != sl.end(); ++s) { - DataType dt = route->input()->default_type(); + if ((v = boost::dynamic_pointer_cast (*s)) != 0) { - if (dt == ARDOUR::DataType::AUDIO) { - rtv = new AudioTimeAxisView (*this, _session, *_track_canvas); - rtv->set_route (route); - } else if (dt == ARDOUR::DataType::MIDI) { - rtv = new MidiTimeAxisView (*this, _session, *_track_canvas); - rtv->set_route (route); - } else { - throw unknown_type(); - } + VCATimeAxisView* vtv = new VCATimeAxisView (*this, _session, *_track_canvas); + vtv->set_vca (v); + new_views.push_back (vtv); + + } else if ((r = boost::dynamic_pointer_cast (*s)) != 0) { + + if (r->is_auditioner() || r->is_monitor()) { + continue; + } + + RouteTimeAxisView* rtv; + DataType dt = r->input()->default_type(); + + if (dt == ARDOUR::DataType::AUDIO) { + rtv = new AudioTimeAxisView (*this, _session, *_track_canvas); + rtv->set_route (r); + } else if (dt == ARDOUR::DataType::MIDI) { + rtv = new MidiTimeAxisView (*this, _session, *_track_canvas); + rtv->set_route (r); + } else { + throw unknown_type(); + } - new_views.push_back (rtv); - track_views.push_back (rtv); - new_selection.push_back (rtv); + new_views.push_back (rtv); + track_views.push_back (rtv); + new_selection.push_back (rtv); - rtv->effective_gain_display (); + rtv->effective_gain_display (); - rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added)); - rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed)); + rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added)); + rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed)); + } } if (new_views.size() > 0) { - _routes->routes_added (new_views); - _summary->routes_added (new_views); + _routes->time_axis_views_added (new_views); + //_summary->routes_added (new_selection); /* XXX requires RouteTimeAxisViewList */ } - if (!from_scratch) { - selection->tracks.clear(); - selection->add (new_selection); + /* note: !new_selection.empty() means that we got some routes rather + * than just VCAs + */ + + if (!from_scratch && !new_selection.empty()) { + selection->set (new_selection); begin_selection_op_history(); } - if (show_editor_mixer_when_tracks_arrive) { + if (show_editor_mixer_when_tracks_arrive && !new_selection.empty()) { show_editor_mixer (true); } @@ -5288,6 +5430,16 @@ Editor::timeaxisview_deleted (TimeAxisView *tv) next_tv = (*i); } + // skip VCAs (cannot be selected, n/a in editor-mixer) + if (dynamic_cast (next_tv)) { + /* VCAs are sorted last in line -- route_sorter.h, jump to top */ + next_tv = track_views.front(); + } + if (dynamic_cast (next_tv)) { + /* just in case: no master, only a VCA remains */ + next_tv = 0; + } + if (next_tv) { set_selected_mixer_strip (*next_tv); @@ -5304,6 +5456,13 @@ Editor::timeaxisview_deleted (TimeAxisView *tv) void Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection) { + if (!tv) { + return; + } + + DisplaySuspender ds; + PresentationInfo::ChangeSuspender cs; + if (apply_to_selection) { for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ) { @@ -5326,6 +5485,18 @@ Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection) } } +void +Editor::show_track_in_display (TimeAxisView* tv, bool move_into_view) +{ + if (!tv) { + return; + } + _routes->show_track_in_display (*tv); + if (move_into_view) { + ensure_time_axis_view_is_visible (*tv, false); + } +} + bool Editor::sync_track_view_list_and_routes () { @@ -5345,15 +5516,15 @@ Editor::foreach_time_axis_view (sigc::slot theslot) } } -/** Find a RouteTimeAxisView by the ID of its route */ -RouteTimeAxisView* -Editor::get_route_view_by_route_id (const PBD::ID& id) const +/** Find a StripableTimeAxisView by the ID of its stripable */ +StripableTimeAxisView* +Editor::get_stripable_time_axis_by_id (const PBD::ID& id) const { - RouteTimeAxisView* v; + StripableTimeAxisView* v; for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) { - if((v = dynamic_cast(*i)) != 0) { - if(v->route()->id() == id) { + if((v = dynamic_cast(*i)) != 0) { + if(v->stripable()->id() == id) { return v; } } @@ -5507,39 +5678,39 @@ Editor::scroll_release () void Editor::reset_x_origin_to_follow_playhead () { - framepos_t const frame = playhead_cursor->current_frame (); + samplepos_t const sample = playhead_cursor->current_sample (); - if (frame < leftmost_frame || frame > leftmost_frame + current_page_samples()) { + if (sample < _leftmost_sample || sample > _leftmost_sample + current_page_samples()) { if (_session->transport_speed() < 0) { - if (frame > (current_page_samples() / 2)) { - center_screen (frame-(current_page_samples()/2)); + if (sample > (current_page_samples() / 2)) { + center_screen (sample-(current_page_samples()/2)); } else { center_screen (current_page_samples()/2); } } else { - framepos_t l = 0; + samplepos_t l = 0; - if (frame < leftmost_frame) { + if (sample < _leftmost_sample) { /* moving left */ if (_session->transport_rolling()) { /* rolling; end up with the playhead at the right of the page */ - l = frame - current_page_samples (); + l = sample - current_page_samples (); } else { /* not rolling: end up with the playhead 1/4 of the way along the page */ - l = frame - current_page_samples() / 4; + l = sample - current_page_samples() / 4; } } else { /* moving right */ if (_session->transport_rolling()) { /* rolling: end up with the playhead on the left of the page */ - l = frame; + l = sample; } else { /* not rolling: end up with the playhead 3/4 of the way along the page */ - l = frame - 3 * current_page_samples() / 4; + l = sample - 3 * current_page_samples() / 4; } } @@ -5576,9 +5747,74 @@ Editor::super_rapid_screen_update () current_mixer_strip->fast_update (); } - /* PLAYHEAD AND VIEWPORT */ + bool latent_locate = false; + samplepos_t sample = _session->audible_sample (&latent_locate); + const int64_t now = g_get_monotonic_time (); + double err = 0; + + if (_session->exporting ()) { + /* freewheel/export may be faster or slower than transport_speed() / SR. + * Also exporting multiple ranges locates/jumps without a _pending_locate_request. + */ + _last_update_time = 0; + } + + if (_session->transport_stopped()) { + //we are stopped. don't interpolate the playhead position; just set it + _last_update_time = 0; + } + + if (_last_update_time > 0) { + /* interpolate and smoothen playhead position */ + const double ds = (now - _last_update_time) * _session->transport_speed() * _session->nominal_sample_rate () * 1e-6; + samplepos_t guess = playhead_cursor->current_sample () + rint (ds); + err = sample - guess; + + guess += err * .12 + _err_screen_engine; // time-constant based on 25fps (super_rapid_screen_update) + _err_screen_engine += .0144 * (err - _err_screen_engine); // tc^2 + +#if 0 // DEBUG + printf ("eng: %ld gui:%ld (%+6.1f) diff: %6.1f (err: %7.2f)\n", + sample, guess, ds, + err, _err_screen_engine); +#endif + + sample = guess; + } else { + _err_screen_engine = 0; + } + + if (err > 8192 || latent_locate) { + // in case of x-runs or freewheeling + _last_update_time = 0; + sample = _session->audible_sample (); + } else { + _last_update_time = now; + } - framepos_t const frame = _session->audible_frame(); + //snapped cursor stuff ( the snapped_cursor shows where an operation is going to occur ) + bool ignored; + MusicSample where (sample, 0); + if ( !UIConfiguration::instance().get_show_snapped_cursor() ) { + snapped_cursor->hide (); + } else if ( _edit_point == EditAtPlayhead && !_dragging_playhead) { + snap_to (where); // can't use snap_to_with_modifier? + snapped_cursor->set_position (where.sample); + snapped_cursor->show (); + } else if ( _edit_point == EditAtSelectedMarker ) { + //NOTE: I don't think EditAtSelectedMarker should snap. they are what they are. + //however, the current editing code -does- snap so I'll draw it that way for now. + if ( !selection->markers.empty() ) { + MusicSample ms (selection->markers.front()->position(), 0); + snap_to (ms); // should use snap_to_with_modifier? + snapped_cursor->set_position ( ms.sample ); + snapped_cursor->show (); + } + } else if (mouse_sample (where.sample, ignored)) { //cursor is in the editing canvas. show it. + snapped_cursor->show (); + } else { //mouse is out of the editing canvas. hide the snapped_cursor + snapped_cursor->hide (); + } /* There are a few reasons why we might not update the playhead / viewport stuff: * @@ -5589,46 +5825,52 @@ Editor::super_rapid_screen_update () * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere). * 3. if we're still at the same frame that we were last time, there's nothing to do. */ + if (_pending_locate_request) { + _last_update_time = 0; + return; + } - if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) { - - last_update_frame = frame; - - if (!_dragging_playhead) { - playhead_cursor->set_position (frame); - } + if (_dragging_playhead) { + _last_update_time = 0; + return; + } - if (!_stationary_playhead) { + if (playhead_cursor->current_sample () == sample) { + return; + } - if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0 && !pending_visual_change.being_handled) { - /* We only do this if we aren't already - handling a visual change (ie if - pending_visual_change.being_handled is - false) so that these requests don't stack - up there are too many of them to handle in - time. - */ - reset_x_origin_to_follow_playhead (); - } + playhead_cursor->set_position (sample); - } else { + if (_session->requested_return_sample() >= 0) { + _last_update_time = 0; + return; + } - if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0 && !pending_visual_change.being_handled) { - framepos_t const frame = playhead_cursor->current_frame (); - double target = ((double)frame - (double)current_page_samples()/2.0); - if (target <= 0.0) { - target = 0.0; - } - // compare to EditorCursor::set_position() - double const old_pos = sample_to_pixel_unrounded (leftmost_frame); - double const new_pos = sample_to_pixel_unrounded (target); - if (rint (new_pos) != rint (old_pos)) { - reset_x_origin (pixel_to_sample (floor (new_pos))); - } - } + if (!_follow_playhead || pending_visual_change.being_handled) { + /* We only do this if we aren't already + * handling a visual change (ie if + * pending_visual_change.being_handled is + * false) so that these requests don't stack + * up there are too many of them to handle in + * time. + */ + return; + } + if (!_stationary_playhead) { + reset_x_origin_to_follow_playhead (); + } else { + samplepos_t const sample = playhead_cursor->current_sample (); + double target = ((double)sample - (double)current_page_samples() / 2.0); + if (target <= 0.0) { + target = 0.0; + } + // compare to EditorCursor::set_position() + double const old_pos = sample_to_pixel_unrounded (_leftmost_sample); + double const new_pos = sample_to_pixel_unrounded (target); + if (rint (new_pos) != rint (old_pos)) { + reset_x_origin (pixel_to_sample (new_pos)); } - } } @@ -5650,7 +5892,7 @@ Editor::session_going_away () clicked_routeview = 0; entered_regionview = 0; entered_track = 0; - last_update_frame = 0; + _last_update_time = 0; _drags->abort (); playhead_cursor->hide (); @@ -5687,39 +5929,47 @@ Editor::session_going_away () /* clear tempo/meter rulers */ remove_metric_marks (); - hide_measures (); clear_marker_display (); + hide_grid_lines (); + delete grid_lines; + grid_lines = 0; + stop_step_editing (); if (own_window()) { - + /* get rid of any existing editor mixer strip */ - + WindowTitle title(Glib::get_application_name()); title += _("Editor"); - + own_window()->set_title (title.get_string()); } SessionHandlePtr::session_going_away (); } +void +Editor::trigger_script (int i) +{ + LuaInstance::instance()-> call_action (i); +} void Editor::show_editor_list (bool yn) { if (yn) { - _the_notebook.show (); + _editor_list_vbox.show (); } else { - _the_notebook.hide (); + _editor_list_vbox.hide (); } } void Editor::change_region_layering_order (bool from_context_menu) { - const framepos_t position = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context_menu); + const samplepos_t position = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context_menu); if (!clicked_routeview) { if (layering_order_editor) { @@ -5759,18 +6009,6 @@ Editor::update_region_layering_order_editor () void Editor::setup_fade_images () { - _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadein-linear"))); - _fade_in_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadein-symmetric"))); - _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadein-fast-cut"))); - _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadein-slow-cut"))); - _fade_in_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadein-constant-power"))); - - _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadeout-linear"))); - _fade_out_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-symmetric"))); - _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut"))); - _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut"))); - _fade_out_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-constant-power"))); - _xfade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadein-linear"))); _xfade_in_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadein-symmetric"))); _xfade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadein-fast-cut"))); @@ -5820,16 +6058,16 @@ Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page) if (_notebook_shrunk) { if (pre_notebook_shrink_pane_width) { - edit_pane.set_position (*pre_notebook_shrink_pane_width); + edit_pane.set_divider (0, *pre_notebook_shrink_pane_width); } _notebook_shrunk = false; } else { - pre_notebook_shrink_pane_width = edit_pane.get_position(); + pre_notebook_shrink_pane_width = edit_pane.get_divider(); /* this expands the LHS of the edit pane to cover the notebook PAGE but leaves the tabs visible. */ - edit_pane.set_position (edit_pane.get_position() + page->get_width()); + edit_pane.set_divider (0, edit_pane.get_divider() + page->get_width()); _notebook_shrunk = true; } } @@ -5926,6 +6164,9 @@ Editor::ui_parameter_changed (string parameter) } _cursors->set_cursor_set (UIConfiguration::instance().get_icon_set()); _cursor_stack.push_back(_cursors->grabber); + edit_pane.set_drag_cursor (*_cursors->expand_left_right); + editor_summary_pane.set_drag_cursor (*_cursors->expand_up_down); + } else if (parameter == "draggable-playhead") { if (_verbose_cursor) { playhead_cursor->set_sensitive (UIConfiguration::instance().get_draggable_playhead()); @@ -5937,7 +6178,7 @@ Gtk::Window* Editor::use_own_window (bool and_fill_it) { bool new_window = !own_window(); - + Gtk::Window* win = Tabbable::use_own_window (and_fill_it); if (win && new_window) { @@ -5946,41 +6187,42 @@ Editor::use_own_window (bool and_fill_it) ARDOUR_UI::instance()->setup_toplevel_window (*win, _("Editor"), this); // win->signal_realize().connect (*this, &Editor::on_realize); + win->signal_event().connect (sigc::bind (sigc::ptr_fun (&Keyboard::catch_user_event_for_pre_dialog_focus), win)); win->signal_event().connect (sigc::mem_fun (*this, &Editor::generic_event_handler)); - win->set_data ("ardour-bindings", &key_bindings); - + win->set_data ("ardour-bindings", bindings); + update_title (); } DisplaySuspender ds; contents().show_all (); - + /* XXX: this is a bit unfortunate; it would probably be nicer if we could just call show () above rather than needing the show_all () */ - + /* re-hide stuff if necessary */ editor_list_button_toggled (); parameter_changed ("show-summary"); parameter_changed ("show-group-tabs"); parameter_changed ("show-zoom-tools"); - + /* now reset all audio_time_axis heights, because widgets might need to be re-hidden */ - + TimeAxisView *tv; - + for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { tv = (static_cast(*i)); tv->reset_height (); } - + if (current_mixer_strip) { current_mixer_strip->hide_things (); current_mixer_strip->parameter_changed ("mixer-element-visibility"); } - + return win; }