*/
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;
}
along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "change-iterator.hh"
#include "context.hh"
#include "direction.hh"
#include "international.hh"
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)
{
{
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);
}
}
}
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);
}
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)
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
{
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);
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&);
};
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);
&& (!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;
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 *
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_)