]> git.donarmstrong.com Git - lilypond.git/commitdiff
Issue 4427: clean up finding & changing of contexts in C++
authorDan Eble <nine.fierce.ballads@gmail.com>
Sun, 31 May 2015 19:14:22 +0000 (15:14 -0400)
committerDan Eble <nine.fierce.ballads@gmail.com>
Thu, 11 Jun 2015 11:31:25 +0000 (07:31 -0400)
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.

lily/accidental-engraver.cc
lily/auto-change-iterator.cc
lily/change-iterator.cc
lily/context.cc
lily/grace-engraver.cc
lily/include/change-iterator.hh
lily/include/context.hh
lily/lyric-combine-music-iterator.cc
lily/lyric-engraver.cc
lily/note-head-line-engraver.cc

index 016433d2ba491aae4524d4791c990855ea25039e..f9d2e15cd4c0e298f9c6a9fbcb3caea2a93fe172 100644 (file)
@@ -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;
         }
index 1709d4a86fc36e83c1948d2746155c113cd525c5..4a34540de96dfb6cc67735ed0a484f406ce13945 100644 (file)
@@ -17,6 +17,7 @@
   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#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);
         }
     }
 }
index f38344be553d6088cf7def8a8ba24e400f7aea76..5cf07bf0e2fd9244982498200016c01e65901309 100644 (file)
@@ -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);
 }
index 1167883b6a29007c9f2f8683b85b6f0eb23ac849..51aa8f024edb618ae4274c9d459a54b88c9e35ec 100644 (file)
@@ -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
 {
index b4fdbec695784a4de5cdfdbbd217fecb1e2c996d..5279cab4c0fb2ed3fa7f2dd03bc4846a84d998a0 100644 (file)
@@ -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);
index 99d2b7ec91443bc3f3201e42fe8b622d6877b0d8..1ba16c09071916727300214f903ffbb6d83ea6e5 100644 (file)
@@ -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&);
 };
index 62c77017bf7a6e4aaab4a43ed7e89f535931ac2f..048bbd6dd45af6a0e66952acda436ae1f27998c2 100644 (file)
@@ -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);
index e83d4e716c238701139515fb804910a017592528..b64251beeda3ec2a78df26f47ca288fdd68c25fb 100644 (file)
@@ -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;
index 58b8f6cdd294e7a6a17907d75c8e4e9418d3c542..fadb35d6b4c8dd3d10e7a541b5bdabc80da2d1b7 100644 (file)
@@ -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 *
index f9a6c91b2c690195188b5a0813fad2d98e98d137..4b21bb27673a5bf62fee58851947c79e3ebe5024 100644 (file)
@@ -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_)