From: Dan Eble Date: Sun, 31 May 2015 19:14:22 +0000 (-0400) Subject: Issue 4427: clean up finding & changing of contexts in C++ X-Git-Tag: release/2.19.22-1~57 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=43c2a7d76764f0e9c7a8f09067af885c53b018dc;p=lilypond.git Issue 4427: clean up finding & changing of contexts in C++ Combine the key feature of Change_iterator and Auto_change_iterator into a common function (favoring the logic of Change_iterator). Define functions for various context-search loops. --- diff --git a/lily/accidental-engraver.cc b/lily/accidental-engraver.cc index 016433d2ba..f9d2e15cd4 100644 --- a/lily/accidental-engraver.cc +++ b/lily/accidental-engraver.cc @@ -199,10 +199,7 @@ check_pitch_against_rules (Pitch const &pitch, Context *origin, */ else if (scm_is_symbol (rule)) { - Context *dad = origin; - while (dad && !dad->is_alias (rule)) - dad = dad->get_parent_context (); - + Context *dad = find_context_above (origin, rule); if (dad) origin = dad; } diff --git a/lily/auto-change-iterator.cc b/lily/auto-change-iterator.cc index 1709d4a86f..4a34540de9 100644 --- a/lily/auto-change-iterator.cc +++ b/lily/auto-change-iterator.cc @@ -17,6 +17,7 @@ along with LilyPond. If not, see . */ +#include "change-iterator.hh" #include "context.hh" #include "direction.hh" #include "international.hh" @@ -38,64 +39,12 @@ protected: private: SCM split_list_; Direction where_dir_; - void change_to (Music_iterator *, SCM, const string&); Moment start_moment_; Context_handle up_; Context_handle down_; }; -void -Auto_change_iterator::change_to (Music_iterator *it, SCM to_type_sym, - const string &to_id) -{ - Context *current = it->get_outlet (); - Context *last = 0; - - /* - Cut & Paste from Change_iterator (ugh). - - TODO: abstract this function - */ - - /* find the type of translator that we're changing. - - If \translator Staff = bass, then look for Staff = * - */ - while (current && !current->is_alias (to_type_sym)) - { - last = current; - current = current->get_parent_context (); - } - - if (current && current->id_string () == to_id) - { - string msg; - msg += _f ("cannot change, already in translator: %s", to_id); - } - - if (current) - { - if (last) - { - Context *dest - = it->get_outlet ()->find_create_context (to_type_sym, to_id, SCM_EOL); - - send_stream_event (last, "ChangeParent", get_music ()->origin (), - ly_symbol2scm ("context"), dest->self_scm ()); - } - else - { - /* - We could change the current translator's id, but that would make - errors hard to catch - - */ - ; - } - } -} - void Auto_change_iterator::process (Moment m) { @@ -119,9 +68,11 @@ Auto_change_iterator::process (Moment m) { where_dir_ = d; string to_id = (d >= 0) ? "up" : "down"; - change_to (child_iter_, - ly_symbol2scm ("Staff"), - to_id); + // N.B. change_to() returns an error message. Silence is the legacy + // behavior here, but maybe that should be changed. + Change_iterator::change_to (*child_iter_, + ly_symbol2scm ("Staff"), + to_id); } } } diff --git a/lily/change-iterator.cc b/lily/change-iterator.cc index f38344be55..5cf07bf0e2 100644 --- a/lily/change-iterator.cc +++ b/lily/change-iterator.cc @@ -44,66 +44,58 @@ Change_iterator::error (const string &reason) get_music ()->origin ()->warning (warn1); } +string +Change_iterator::change_to (Music_iterator &it, + SCM to_type, + const string &to_id) +{ + string result; // error message + + // Find the context that should have its parent changed. + Context *last = find_context_above_by_parent_type (it.get_outlet (), to_type); + if (last) + { + // Find the new parent. + Context *dest = find_context_near (it.get_outlet (), to_type, to_id); + if (dest) + { + send_stream_event (last, "ChangeParent", it.get_music ()->origin (), + ly_symbol2scm ("context"), dest->self_scm ()); + } + else + /* FIXME: constant error message. */ + it.get_music ()->origin ()->warning (_ ("cannot find context to switch to")); + } + else if (it.get_outlet ()->is_alias (to_type)) + { + // No enclosing context was found because the iterator's immediate + // context is the kind that was sought. + /* We could change the current translator's id, but that would make + errors hard to catch. + + last->translator_id_string () = get_change + ()->change_to_id_string (); */ + result = _f ("not changing to same context type: %s", ly_symbol2string (to_type).c_str ()); + } + else + /* FIXME: uncomprehensable message */ + result = _ ("none of these in my family"); + + return result; +} + /* move to construct_children ? */ void Change_iterator::process (Moment m) { - Context *current = get_outlet (); - Context *last = 0; - SCM to_type = get_music ()->get_property ("change-to-type"); string to_id = ly_scm2string (get_music ()->get_property ("change-to-id")); - /* find the type of translator that we're changing. - - If \translator Staff = bass, then look for Staff = * - */ - while (current && !current->is_alias (to_type)) - { - last = current; - current = current->get_parent_context (); - } - - if (current && current->id_string () == to_id) - { - string msg; - msg += _f ("cannot change, already in translator: %s", to_id); - } - - if (current) - if (last) - { - Context *dest = 0; - Context *where = get_outlet (); - while (!dest && where) - { - dest = find_context_below (where, to_type, to_id); - where = where->get_parent_context (); - } - - if (dest) - { - send_stream_event (last, "ChangeParent", get_music ()->origin (), - ly_symbol2scm ("context"), dest->self_scm ()); - } - else - /* FIXME: constant error message. */ - get_music ()->origin ()->warning (_ ("cannot find context to switch to")); - } - else - { - /* We could change the current translator's id, but that would make - errors hard to catch. - - last->translator_id_string () = get_change - ()->change_to_id_string (); */ - error (_f ("not changing to same context type: %s", ly_symbol2string (to_type).c_str ())); - } - else - /* FIXME: uncomprehensable message */ - error (_ ("none of these in my family")); + string msg = change_to (*this, to_type, to_id); + if (!msg.empty ()) + error (msg); Simple_music_iterator::process (m); } diff --git a/lily/context.cc b/lily/context.cc index 1167883b6a..51aa8f024e 100644 --- a/lily/context.cc +++ b/lily/context.cc @@ -578,9 +578,26 @@ Context::disconnect_from_parent () daddy_context_ = 0; } -/* - ID == "" means accept any ID. -*/ +Context * +find_context_above (Context *where, SCM type) +{ + while (where && !where->is_alias (type)) + where = where->get_parent_context (); + + return where; +} + +Context * +find_context_above_by_parent_type (Context *where, SCM parent_type) +{ + for (Context *child = 0; where; + child = where, where = where->get_parent_context ()) + if (where->is_alias (parent_type)) + return child; + + return 0; +} + Context * find_context_below (Context *where, SCM type, const string &id) @@ -603,6 +620,29 @@ find_context_below (Context *where, return found; } +Context * +find_context_near (Context *where, + SCM type, const string &id) +{ + for ( ; where; where = where->get_parent_context ()) + { + Context *found = find_context_below (where, type, id); + if (found) + return found; + } + + return 0; +} + +Context * +find_top_context (Context *where) +{ + Context *top = where; + for ( ; where; where = where->get_parent_context()) + top = where; + return top; +} + SCM Context::properties_as_alist () const { diff --git a/lily/grace-engraver.cc b/lily/grace-engraver.cc index b4fdbec695..5279cab4c0 100644 --- a/lily/grace-engraver.cc +++ b/lily/grace-engraver.cc @@ -120,10 +120,7 @@ Grace_engraver::consider_change_grace_settings () if (!scm_is_pair (sym)) sym = scm_list_1 (sym); - Context *c = context (); - while (c && !c->is_alias (context_name)) - c = c->get_parent_context (); - + Context *c = find_context_above (context (), context_name); if (c) { SCM cell = Grob_property_info (c, grob).push (sym, val); diff --git a/lily/include/change-iterator.hh b/lily/include/change-iterator.hh index 99d2b7ec91..1ba16c0907 100644 --- a/lily/include/change-iterator.hh +++ b/lily/include/change-iterator.hh @@ -30,6 +30,10 @@ public: DECLARE_SCHEME_CALLBACK (constructor, ()); DECLARE_CLASSNAME (Change_iterator); + // returns an error message (empty on success) + static string change_to (Music_iterator &it, + SCM to_type, const string &to_id); + private: void error (const string&); }; diff --git a/lily/include/context.hh b/lily/include/context.hh index 62c77017bf..048bbd6dd4 100644 --- a/lily/include/context.hh +++ b/lily/include/context.hh @@ -144,8 +144,32 @@ public: void apply_property_operations (Context *tg, SCM pre_init_ops); void execute_pushpop_property (Context *trg, SCM prop, SCM eltprop, SCM val); +// Search for a context of the given type starting from the given context and +// moving toward the root of the tree. If the starting context matches, it is +// returned. +Context *find_context_above (Context *where, SCM type_sym); + +// Search for a context of the given type starting from the given context and +// moving toward the root of the tree. If found, return its child that was +// found on the way there. +Context *find_context_above_by_parent_type (Context *where, SCM parent_type); + +// Search for a context of the given type and ID starting from the given +// context and moving toward the leaves of the tree. If the starting context +// matches, it is returned. An empty ID matches any context of the given type. Context *find_context_below (Context *where, SCM type_sym, const string &id); + +// Search for a context of the given type and ID starting with the given +// context, then searching its descendants, then its parent's descendants, etc. +// An empty ID matches any context of the given type. +Context *find_context_near (Context *where, + SCM type_sym, const string &id); + +// Search for the top context (i.e. the ancestor with no parent) starting with +// the given context. +Context *find_top_context (Context *where); + bool melisma_busy (Context *); Context *get_voice_to_lyrics (Context *lyrics); diff --git a/lily/lyric-combine-music-iterator.cc b/lily/lyric-combine-music-iterator.cc index e83d4e716c..b64251beed 100644 --- a/lily/lyric-combine-music-iterator.cc +++ b/lily/lyric-combine-music-iterator.cc @@ -265,12 +265,8 @@ Lyric_combine_music_iterator::find_voice () && (!music_context_ || ly_scm2string (voice_name) != music_context_->id_string ()) && scm_is_symbol (voice_type)) { - Context *t = get_outlet (); - while (t && t->get_parent_context ()) - t = t->get_parent_context (); - - string name = ly_scm2string (voice_name); - return find_context_below (t, voice_type, name); + return find_context_below (find_top_context (get_outlet ()), + voice_type, ly_scm2string (voice_name)); } return 0; diff --git a/lily/lyric-engraver.cc b/lily/lyric-engraver.cc index 58b8f6cdd2..fadb35d6b4 100644 --- a/lily/lyric-engraver.cc +++ b/lily/lyric-engraver.cc @@ -116,26 +116,11 @@ get_voice_to_lyrics (Context *lyrics) if (!scm_is_symbol (voice_type)) return 0; - Context *parent = lyrics; - Context *voice = 0; - while (parent && !voice) - { - voice = find_context_below (parent, voice_type, nm); - parent = parent->get_parent_context (); - } - + Context *voice = find_context_near (lyrics, voice_type, nm); if (voice) return voice; - parent = lyrics; - voice = 0; - while (parent && !voice) - { - voice = find_context_below (parent, voice_type, ""); - parent = parent->get_parent_context (); - } - - return voice; + return find_context_near (lyrics, voice_type, ""); } Grob * diff --git a/lily/note-head-line-engraver.cc b/lily/note-head-line-engraver.cc index f9a6c91b2c..4b21bb2767 100644 --- a/lily/note-head-line-engraver.cc +++ b/lily/note-head-line-engraver.cc @@ -64,13 +64,9 @@ void Note_head_line_engraver::acknowledge_rhythmic_head (Grob_info info) { head_ = info.grob (); - Context *tr = context (); - - while (tr && !tr->is_alias (ly_symbol2scm ("Staff"))) - tr = tr->get_parent_context (); - + Context *tr = find_context_above (context (), ly_symbol2scm ("Staff")); if (tr - && tr->is_alias (ly_symbol2scm ("Staff")) && tr != last_staff_ + && tr != last_staff_ && to_boolean (get_property ("followVoice"))) { if (last_head_)