X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor.cc;h=d525761140384c43e4fa42f62ba05cf14d06d60e;hb=e9f4c5fc1c9bfd5fa6d5abb88a5161630ad56ef0;hp=ba07222bdf8d694aaa0e4f9302a5e7dc7e01ebf3;hpb=1e4e97019da3e90f89614da51ac537ed2041bb55;p=ardour.git diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index ba07222bdf..d525761140 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -57,12 +57,10 @@ #include #include "gtkmm2ext/bindings.h" -#include "gtkmm2ext/grouped_buttons.h" #include "gtkmm2ext/gtk_ui.h" -#include +#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" @@ -83,11 +81,14 @@ #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" #include "analysis_window.h" -#include "ardour_spacer.h" #include "audio_clock.h" #include "audio_region_view.h" #include "audio_streamview.h" @@ -97,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" @@ -107,6 +109,7 @@ #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" @@ -134,7 +137,6 @@ #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" @@ -144,6 +146,7 @@ using namespace std; using namespace ARDOUR; +using namespace ArdourWidgets; using namespace ARDOUR_UI_UTILS; using namespace PBD; using namespace Gtk; @@ -299,9 +302,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) @@ -342,11 +347,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) @@ -386,8 +393,8 @@ Editor::Editor () , _visible_track_count (-1) , toolbar_selection_clock_table (2,3) , automation_mode_button (_("mode")) - , 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) @@ -487,7 +494,7 @@ Editor::Editor () 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); @@ -638,12 +645,14 @@ Editor::Editor () 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 (true); + _time_info_box = new TimeInfoBox ("EditorTimeInfo", true); /* these are static location signals */ @@ -669,42 +678,29 @@ Editor::Editor () /* Pick up some settings we need to cache, early */ XMLNode* settings = ARDOUR_UI::instance()->editor_settings(); - XMLProperty* prop; - if (settings && (prop = settings->property ("notebook-shrunk"))) { - _notebook_shrunk = string_is_affirmative (prop->value ()); + if (settings) { + settings->get_property ("notebook-shrunk", _notebook_shrunk); } editor_summary_pane.set_check_divider_position (true); editor_summary_pane.add (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)); + 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_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)); + 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_arrows_left_left); - summary_arrows_left->pack_start (*summary_arrows_left_right); - - 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)); - - 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)); + 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); @@ -733,25 +729,17 @@ Editor::Editor () 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); - { - LocaleGuard lg; - - if (!settings || ((prop = settings->property ("edit-horizontal-pane-pos")) == 0) || ((fract = atof (prop->value())) > 1.0)) { - /* initial allocation is 90% to canvas, 10% to notebook */ - edit_pane.set_divider (0, 0.90); - } else { - edit_pane.set_divider (0, fract); - } - - if (!settings || ((prop = settings->property ("edit-vertical-pane-pos")) == 0) || ((fract = atof (prop->value())) > 1.0)) { - /* initial allocation is 90% to canvas, 10% to summary */ - editor_summary_pane.set_divider (0, 0.90); - } else { - - editor_summary_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); global_vpacker.set_spacing (2); global_vpacker.set_border_width (0); @@ -762,11 +750,11 @@ Editor::Editor () ebox->set_name("EditorWindow"); ebox->add (toolbar_hbox); - Gtk::EventBox* epane_box = manage (new Gtk::EventBox); //a themeable box + Gtk::EventBox* epane_box = manage (new EventBoxExt); //a themeable box epane_box->set_name("EditorWindow"); epane_box->add (edit_pane); - Gtk::EventBox* epane_box2 = manage (new Gtk::EventBox); //a themeable box + Gtk::EventBox* epane_box2 = manage (new EventBoxExt); //a themeable box epane_box2->set_name("EditorWindow"); epane_box2->add (global_vpacker); @@ -828,8 +816,6 @@ Editor::Editor () BasicUI::AccessAction.connect (*this, invalidator (*this), boost::bind (&Editor::access_action, this, _1, _2), gui_context()); - PresentationInfo::Change.connect (*this, invalidator (*this), boost::bind (&Editor::presentation_info_changed, this, _1), gui_context()); - /* handle escape */ ARDOUR_UI::instance()->Escape.connect (*this, invalidator (*this), boost::bind (&Editor::escape, this), gui_context()); @@ -849,16 +835,16 @@ Editor::Editor () _show_marker_lines = false; - /* Button bindings */ + /* Button 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_operation (**i); - } - } + if (node) { + for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) { + button_bindings->load_operation (**i); + } + } constructed = true; @@ -905,14 +891,6 @@ Editor::~Editor() } } -void -Editor::presentation_info_changed (PropertyChange const & what_changed) -{ - if (what_changed.contains (Properties::selected)) { - track_selection_changed (); - } -} - XMLNode* Editor::button_settings () const { @@ -1048,7 +1026,7 @@ Editor::control_unselect () void Editor::control_select (boost::shared_ptr s, Selection::Operation op) { - TimeAxisView* tav = axis_view_from_stripable (s); + TimeAxisView* tav = time_axis_view_from_stripable (s); if (tav) { switch (op) { @@ -1154,7 +1132,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; @@ -1170,6 +1148,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 () { @@ -1335,6 +1319,10 @@ Editor::set_session (Session *t) return; } + //initialize leftmost_frame to the extents of the session + //this prevents a bogus setting of leftmost = "0" if the summary view asks for the leftmost frame before the visible state has been loaded from instant.xml + leftmost_frame = session_gui_extents().first; + _playlist_selector->set_session (_session); nudge_clock->set_session (_session); _summary->set_session (_session); @@ -1381,6 +1369,12 @@ 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 ()); @@ -1396,6 +1390,7 @@ 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()); @@ -1440,25 +1435,6 @@ Editor::set_session (Session *t) break; } - /* catch up on selection of stripables (other selection state is lost - * when a session is closed - */ - - StripableList sl; - TrackViewList tl; - _session->get_stripables (sl); - for (StripableList::const_iterator s = sl.begin(); s != sl.end(); ++s) { - if ((*s)->presentation_info().selected()) { - RouteTimeAxisView* rtav = get_route_view_by_route_id ((*s)->id()); - if (rtav) { - tl.push_back (rtav); - } - } - } - if (!tl.empty()) { - selection->set (tl); - } - /* register for undo history */ _session->register_with_memento_command_factory(id(), this); _session->register_with_memento_command_factory(_selection_memento->id(), _selection_memento); @@ -1912,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); @@ -2351,18 +2317,16 @@ Editor::set_edit_point_preference (EditPoint ep, bool force) int Editor::set_state (const XMLNode& node, int version) { - XMLProperty const * prop; set_id (node); PBD::Unwinder nsi (no_save_instant, true); - LocaleGuard lg; + 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); + framepos_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); @@ -2371,86 +2335,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)); - } else { - zoom_focus_selection_done (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())); + int32_t cnt; + if (node.get_property ("visible-track-count", cnt)) { + set_visible_track_count (cnt); } - if ((prop = node.property ("snap-to"))) { - snap_type_selection_done ((SnapType) string_2_enum (prop->value(), _snap_type)); - set_snap_to ((SnapType) string_2_enum (prop->value(), _snap_type)); - } else { - set_snap_to (_snap_type); + SnapType snap_type; + if (!node.get_property ("snap-to", snap_type)) { + snap_type = _snap_type; } + set_snap_to (snap_type); - if ((prop = node.property ("snap-mode"))) { - snap_mode_selection_done((SnapMode) string_2_enum (prop->value(), _snap_mode)); + 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 ((SnapMode) string_2_enum (prop->value(), _snap_mode)); + set_snap_mode (sm); } else { set_snap_mode (_snap_mode); } - if ((prop = node.property ("internal-snap-to"))) { - internal_snap_type = (SnapType) string_2_enum (prop->value(), internal_snap_type); - } - - if ((prop = node.property ("internal-snap-mode"))) { - internal_snap_mode = (SnapMode) string_2_enum (prop->value(), internal_snap_mode); - } - - if ((prop = node.property ("pre-internal-snap-to"))) { - pre_internal_snap_type = (SnapType) string_2_enum (prop->value(), pre_internal_snap_type); - } - - 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-snap-to", internal_snap_type); + node.get_property ("internal-snap-mode", internal_snap_mode); + node.get_property ("pre-internal-snap-to", pre_internal_snap_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); + framepos_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); @@ -2459,39 +2407,34 @@ 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); + 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 ("show-measures"))) { - bool yn = string_is_affirmative (prop->value()); - _show_measures = yn; - } + node.get_property ("show-measures", _show_measures); - 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 */ @@ -2499,13 +2442,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 */ @@ -2513,15 +2455,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); @@ -2534,8 +2476,7 @@ Editor::set_state (const XMLNode& node, int version) _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); @@ -2545,10 +2486,9 @@ 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); + framepos_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); @@ -2560,7 +2500,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) { @@ -2597,79 +2536,67 @@ XMLNode& Editor::get_state () { XMLNode* node = new XMLNode (X_("Editor")); - char buf[32]; - LocaleGuard lg; - 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), "%f", edit_pane.get_divider ()); - node->add_property("edit-horizontal-pane-pos", string(buf)); - node->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0"); - snprintf(buf,sizeof(buf), "%f", editor_summary_pane.get_divider()); - 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 ("snap-to", _snap_type); + node->set_property ("snap-mode", _snap_mode); + node->set_property ("internal-snap-to", internal_snap_type); + node->set_property ("internal-snap-mode", internal_snap_mode); + node->set_property ("pre-internal-snap-to", pre_internal_snap_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_frame ()); + node->set_property ("left-frame", leftmost_frame); + node->set_property ("y-origin", vertical_adjustment.get_value ()); + + node->set_property ("show-measures", _show_measures); + 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()); @@ -3456,6 +3383,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 @@ -3888,11 +3824,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 { @@ -4564,9 +4510,13 @@ Editor::set_samples_per_pixel (framecnt_t spp) } samples_per_pixel = spp; +} +void +Editor::on_samples_per_pixel_changed () +{ if (tempo_lines) { - tempo_lines->tempo_map_changed(); + tempo_lines->tempo_map_changed(_session->tempo_map().music_origin()); } bool const showing_time_selection = selection->time.length() > 0; @@ -4607,13 +4557,7 @@ Editor::playhead_cursor_sample () const 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 (); } @@ -4633,9 +4577,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 @@ -4646,7 +4606,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; @@ -4657,40 +4620,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 (); - - compute_bbt_ruler_scale (vc.time_origin, pending_visual_change.time_origin + current_page_samples()); - update_tempo_based_rulers (); - - 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_tempo (true); + + /* video frames & position need to be updated for zoom, horiz-scroll + * and (explicitly) VisualChange::VideoTimeline. + */ update_video_timeline(); } @@ -4726,11 +4712,11 @@ Editor::get_preferred_edit_position (EditIgnoreOption ignore, bool from_context_ 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(); } @@ -4746,12 +4732,12 @@ Editor::get_preferred_edit_position (EditIgnoreOption ignore, bool from_context_ switch (ep) { case EditAtPlayhead: - if (_dragging_playhead) { + if (_dragging_playhead && _control_scroll_target) { where = *_control_scroll_target; } else { where = _session->audible_frame(); } - 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: @@ -4764,7 +4750,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; } } @@ -4779,7 +4765,7 @@ Editor::get_preferred_edit_position (EditIgnoreOption ignore, bool from_context_ snap_mf.frame = where; snap_to (snap_mf); where = snap_mf.frame; - DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where)); + DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where)); break; } @@ -5088,7 +5074,7 @@ Editor::get_regions_corresponding_to (boost::shared_ptr region, vector region) const +Editor::regionview_from_region (boost::shared_ptr region) const { for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) { RouteTimeAxisView* tatv; @@ -5105,6 +5091,20 @@ Editor::get_regionview_from_region (boost::shared_ptr region) const 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 () { @@ -5136,6 +5136,17 @@ Editor::first_idle () (*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 (); @@ -5231,19 +5242,12 @@ 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; @@ -5265,8 +5269,8 @@ Editor::region_view_removed () _summary->set_background_dirty (); } -TimeAxisView* -Editor::axis_view_from_stripable (boost::shared_ptr s) const +AxisView* +Editor::axis_view_by_stripable (boost::shared_ptr s) const { for (TrackViewList::const_iterator j = track_views.begin (); j != track_views.end(); ++j) { if ((*j)->stripable() == s) { @@ -5277,6 +5281,25 @@ Editor::axis_view_from_stripable (boost::shared_ptr s) const 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 @@ -5284,7 +5307,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_stripable (*i); + TimeAxisView* tv = time_axis_view_from_stripable (*i); if (tv) { t.push_back (tv); } @@ -5343,7 +5366,7 @@ Editor::add_stripables (StripableList& sl) TrackViewList new_selection; bool from_scratch = (track_views.size() == 0); - sl.sort (StripablePresentationInfoSorter()); + sl.sort (Stripable::Sorter()); for (StripableList::iterator s = sl.begin(); s != sl.end(); ++s) { @@ -5393,8 +5416,7 @@ Editor::add_stripables (StripableList& sl) */ if (!from_scratch && !new_selection.empty()) { - selection->tracks.clear(); - selection->add (new_selection); + selection->set (new_selection); begin_selection_op_history(); } @@ -5484,6 +5506,9 @@ Editor::timeaxisview_deleted (TimeAxisView *tv) void Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection) { + if (!tv) { + return; + } if (apply_to_selection) { for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ) { @@ -5506,6 +5531,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 () { @@ -5525,15 +5562,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; } } @@ -5758,8 +5795,6 @@ Editor::super_rapid_screen_update () /* PLAYHEAD AND VIEWPORT */ - framepos_t const frame = _session->audible_frame(); - /* There are a few reasons why we might not update the playhead / viewport stuff: * * 1. we don't update things when there's a pending locate request, otherwise @@ -5769,46 +5804,92 @@ 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 || !_session->transport_rolling ()) { + _last_update_time = 0; + return; + } - if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) { + if (_dragging_playhead) { + _last_update_time = 0; + return; + } - last_update_frame = frame; + bool latent_locate = false; + framepos_t frame = _session->audible_frame (&latent_locate); + const int64_t now = g_get_monotonic_time (); + double err = 0; - if (!_dragging_playhead) { - playhead_cursor->set_position (frame); - } + 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 (!_stationary_playhead) { + if (_last_update_time > 0) { + /* interpolate and smoothen playhead position */ + const double ds = (now - _last_update_time) * _session->transport_speed() * _session->nominal_frame_rate () * 1e-6; + framepos_t guess = playhead_cursor->current_frame () + rint (ds); + err = frame - guess; - 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 (); - } + 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 - } else { +#if 0 // DEBUG + printf ("eng: %ld gui:%ld (%+6.1f) diff: %6.1f (err: %7.2f)\n", + frame, guess, ds, + err, _err_screen_engine); +#endif - 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))); - } - } + frame = guess; + } else { + _err_screen_engine = 0; + } - } + if (err > 8192 || latent_locate) { + // in case of x-runs or freewheeling + _last_update_time = 0; + frame = _session->audible_frame (); + } else { + _last_update_time = now; + } + if (playhead_cursor->current_frame () == frame) { + return; + } + + playhead_cursor->set_position (frame); + + if (_session->requested_return_frame() >= 0) { + _last_update_time = 0; + return; + } + + 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 { + 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 (new_pos)); + } } } @@ -5830,7 +5911,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 (); @@ -5870,6 +5951,9 @@ Editor::session_going_away () hide_measures (); clear_marker_display (); + delete tempo_lines; + tempo_lines = 0; + stop_step_editing (); if (own_window()) {