@end ignore
+@item
+All of @code{\override}, @code{\revert}, @code{\set}, and
+@code{\unset} now work with the @code{\once} prefix for making
+one-time settings.
+@lilypond[quote,verbatim]
+\relative {
+ c'4 d
+ \override NoteHead.color = #red
+ e4 f |
+ \once \override NoteHead.color = #green
+ g4 a
+ \once \revert NoteHead.color
+ b c |
+ \revert NoteHead.color
+ f2 c |
+}
+@end lilypond
+
@item
When outputting MIDI, LilyPond will now store the @code{title}
defined in a score's @code{\header} block (or, if there is no
@funindex \once
@funindex once
-Both the @code{\override} and the @code{\set} commands may be prefixed
-by @code{\once}. This causes the following @code{\override} or
-@code{\set} command to be effective only during the current musical
+@code{\override}, @code{\revert}. @code{\set}, and @code{\unset}
+commands may be prefixed with @code{\once}. This causes such a
+command to be effective only during the current musical
moment before the property reverts back to its previous value (this can
be different from the default if another @code{\override} is still in
effect). Using the same example, we can change the color of a single
e4 f |
\once \override NoteHead.color = #green
g4 a
- \revert NoteHead.color
+ \once \revert NoteHead.color
b c |
+ \revert NoteHead.color
+ f2 c |
}
@end lilypond
}
@end lilypond
-However, predefined commands of the form @code{\@dots{}Neutral},
-@code{\@dots{}Off} and @code{\un@dots{}} use @code{\revert} internally
-rather than @code{\override} so prefixing these with @code{\once} has no
-effect.
-
-
@node The overrideProperty command
@unnumberedsubsubsec The @code{@bs{}overrideProperty} command
@cindex \once
-Preceding a @code{\set} command by @code{\once} makes the
-setting apply to only a single time-step:
+Preceding a @code{\set} or @code{\unset} command by @code{\once}
+makes the setting apply to only a single time-step:
@lilypond[quote,fragment,verbatim]
c''4
@funindex \once
@cindex overriding for only one moment
-@code{\once} can be used with @code{\override}
+@code{\once} can be used with @code{\override} or @code{\revert}
to affect only the current time step:
@lilypond[quote,verbatim]
--- /dev/null
+\version "2.19.28"
+
+\header {
+ texidoc = "@code{\\once \\revert} can be used for reverting a property
+once rather than permanently."
+}
+
+\layout {
+ ragged-right = ##t
+}
+
+\relative {
+ c'4-"b" d-"b"
+ \override NoteHead.color = #red
+ e4-"r" f-"r" |
+ \once \override NoteHead.color = #green
+ g4-"g" a-"r"
+ \once \revert NoteHead.color
+ b-"b" c-"r" |
+ \temporary \override NoteHead.color = #yellow
+ g-"y" e-"y"
+ \once \revert NoteHead.color
+ d-"r" c-"y" |
+ \revert NoteHead.color
+ d-"r" e-"r"
+ \once \revert NoteHead.color
+ f-"b" d-"r" |
+ \revert NoteHead.color
+ c1-"b"
+}
// cooked_from_ is the value of alist_ from which the expansion has
// been done
SCM cooked_from_;
- // nested_ is a count of nested overrides in alist_
+ // nested_ is a count of nested overrides in alist_ Or rather: of
+ // entries that must not appear in the cooked list and are
+ // identified by having a "key" that is not a symbol. Temporary
+ // overrides and reverts also meet that description and have a
+ // nominal key of #t/#f and a value of the original cons cell.
int nested_;
Grob_properties (SCM alist, SCM based_on) :
return SCM_EOL;
}
+// Used for \once \override, returns a token for matched_pop
+SCM
+Grob_property_info::temporary_override (SCM grob_property_path, SCM new_value)
+{
+ SCM cell = push (grob_property_path, new_value);
+ if (!scm_is_pair (cell))
+ return cell;
+ if (scm_is_symbol (scm_car (cell)))
+ props_->nested_++;
+ cell = scm_cons (SCM_BOOL_T, cell);
+ props_->alist_ = scm_cons (cell, scm_cdr (props_->alist_));
+ return cell;
+}
+
+// Used for \once \revert, returns a token for matched_pop
+SCM
+Grob_property_info::temporary_revert (SCM grob_property_path)
+{
+ if (!check ())
+ return SCM_EOL;
+
+ SCM current_alist = props_->alist_;
+ SCM daddy = props_->based_on_;
+ SCM tail = SCM_EOL;
+
+ if (!scm_is_pair (grob_property_path)
+ || !scm_is_symbol (scm_car (grob_property_path)))
+ {
+ programming_error ("Grob property path should be list of symbols.");
+ return SCM_EOL;
+ }
+
+ if (scm_is_pair (scm_cdr (grob_property_path)))
+ {
+ tail = assoc_tail (grob_property_path, current_alist, daddy);
+ if (scm_is_false (tail))
+ return SCM_EOL;
+ }
+ else
+ {
+ tail = assq_tail (scm_car (grob_property_path), current_alist, daddy);
+ if (scm_is_false (tail))
+ return SCM_EOL;
+ ++props_->nested_;
+ }
+
+ SCM cell = scm_cons (SCM_BOOL_F, scm_car (tail));
+ props_->alist_ = partial_list_copy (current_alist, tail,
+ scm_cons (cell, scm_cdr (tail)));
+ return cell;
+}
+
+
void
Grob_property_info::matched_pop (SCM cell)
{
{
if (scm_is_eq (scm_car (p), cell))
{
- if (scm_is_pair (scm_car (cell)))
+ SCM key = scm_car (cell);
+ if (scm_is_false (key))
+ {
+ // temporary revert, reactivate
+ cell = scm_cdr (cell);
+ if (scm_is_symbol (scm_car (cell)))
+ props_->nested_--;
+ props_->alist_ = partial_list_copy (current_alist, p,
+ scm_cons (cell, scm_cdr (p)));
+ return;
+ }
+ if (!scm_is_symbol (key))
props_->nested_--;
props_->alist_ = partial_list_copy (current_alist, p, scm_cdr (p));
return;
else if (scm_is_eq (type, ly_symbol2scm ("assign")))
tg->set_property (scm_car (entry), scm_cadr (entry));
else if (scm_is_eq (type, ly_symbol2scm ("apply")))
- scm_apply_1 (scm_car (entry), tg->self_scm (), scm_cdr (entry));
+ scm_apply_1 (scm_car (entry), tg->self_scm (), scm_cdr (entry));
else if (scm_is_eq (type, ly_symbol2scm ("unset")))
tg->unset_property (scm_car (entry));
}
return SCM_UNSPECIFIED;
}
+LY_DEFINE (ly_context_matched_pop_property, "ly:context-matched-pop-property",
+ 3, 0, 0, (SCM context, SCM grob, SCM cell),
+ "This undoes a particular @code{\\override},"
+ " @code{\\once \\override} or @code{\\once \\revert}"
+ " when given the specific alist pair to undo.")
+{
+ Context *tg = LY_ASSERT_SMOB (Context, context, 1);
+ LY_ASSERT_TYPE (ly_is_symbol, grob, 2);
+ Grob_property_info (tg, grob).matched_pop (cell);
+ return SCM_UNSPECIFIED;
+}
+
+
LY_DEFINE (ly_context_property, "ly:context-property",
2, 1, 0, (SCM context, SCM sym, SCM def),
"Return the value for property @var{sym} in @var{context}."
ok = type_check_assignment (sym, val, ly_symbol2scm ("translation-type?"));
if (ok)
- set_property (sym, val);
+ {
+ if (to_boolean (ev->get_property ("once")))
+ {
+ if (Global_context *g = get_global_context ())
+ {
+ SCM old_val = SCM_UNDEFINED;
+ if (here_defined (sym, &old_val))
+ g->add_finalization (scm_list_4 (ly_context_set_property_x_proc,
+ self_scm (),
+ sym,
+ old_val));
+ else
+ g->add_finalization (scm_list_3 (ly_context_unset_property_proc,
+ self_scm (),
+ sym));
+ }
+ }
+ set_property (sym, val);
+ }
}
}
Stream_event *ev = unsmob<Stream_event> (sev);
SCM sym = ev->get_property ("symbol");
- type_check_assignment (sym, SCM_EOL, ly_symbol2scm ("translation-type?"));
- unset_property (sym);
+ bool ok = type_check_assignment (sym, SCM_EOL, ly_symbol2scm ("translation-type?"));
+
+ if (ok)
+ {
+ if (to_boolean (ev->get_property ("once")))
+ {
+ if (Global_context *g = get_global_context ())
+ {
+ SCM old_val = SCM_UNDEFINED;
+ if (here_defined (sym, &old_val))
+ g->add_finalization (scm_list_4 (ly_context_set_property_x_proc,
+ self_scm (),
+ sym,
+ old_val));
+ else
+ g->add_finalization (scm_list_3 (ly_context_unset_property_proc,
+ self_scm (),
+ sym));
+ }
+ }
+ unset_property (sym);
+ }
}
/*
#include "context.hh"
#include "dispatcher.hh"
#include "engraver-group.hh"
+#include "global-context.hh"
#include "grob.hh"
#include "grob-properties.hh"
#include "paper-score.hh"
Engraver_group::override (SCM sev)
{
Stream_event *ev = unsmob<Stream_event> (sev);
+ SCM sym = ev->get_property ("symbol");
+ Grob_property_info gpi (context (), sym);
- Grob_property_info (context (), ev->get_property ("symbol"))
- .push (ev->get_property ("property-path"),
- ev->get_property ("value"));
+ if (to_boolean (ev->get_property ("once")))
+ {
+ SCM token = gpi.temporary_override (ev->get_property ("property-path"),
+ ev->get_property ("value"));
+ if (scm_is_pair (token))
+ if (Global_context *g = context ()->get_global_context ())
+ {
+ g->add_finalization (scm_list_4 (ly_context_matched_pop_property_proc,
+ context ()->self_scm (),
+ sym,
+ token));
+ }
+ }
+ else
+ gpi.push (ev->get_property ("property-path"),
+ ev->get_property ("value"));
}
void
Engraver_group::revert (SCM sev)
{
Stream_event *ev = unsmob<Stream_event> (sev);
+ SCM sym = ev->get_property ("symbol");
+ Grob_property_info gpi (context (), sym);
- Grob_property_info (context (), ev->get_property ("symbol"))
- .pop (ev->get_property ("property-path"));
+ if (to_boolean (ev->get_property ("once")))
+ {
+ SCM token = gpi.temporary_revert (ev->get_property ("property-path"));
+ if (scm_is_pair (token))
+ if (Global_context *g = context ()->get_global_context ())
+ {
+ g->add_finalization (scm_list_4 (ly_context_matched_pop_property_proc,
+ context ()->self_scm (),
+ sym,
+ token));
+ }
+ }
+ else
+ gpi.pop (ev->get_property ("property-path"));
}
void
SCM nested_property_alist (SCM alist, SCM prop_path, SCM value);
SCM nested_create_alist (SCM prop_path, SCM value);
SCM partial_list_copy (SCM alist, SCM tail, SCM newtail);
+SCM assq_tail (SCM key, SCM alist, SCM alist_end);
+SCM assoc_tail (SCM key, SCM alist, SCM alist_end);
SCM evict_from_alist (SCM, SCM, SCM);
SCM nalist_to_alist (SCM nalist, int nested);
+extern SCM ly_context_set_property_x_proc;
+extern SCM ly_context_unset_property_proc;
+extern SCM ly_context_matched_pop_property_proc;
#endif /* CONTEXT_HH */
bool create ();
SCM updated ();
SCM push (SCM path, SCM value);
+ SCM temporary_override (SCM path, SCM value);
+ SCM temporary_revert (SCM path);
void matched_pop (SCM);
void pop (SCM path);
void pushpop (SCM path, SCM value)
{
public:
DECLARE_SCHEME_CALLBACK (constructor, ());
- DECLARE_SCHEME_CALLBACK (once_finalization, (SCM, SCM, SCM));
DECLARE_CLASSNAME (Property_iterator);
protected:
- virtual void do_quit ();
virtual void process (Moment);
};
{
public:
DECLARE_SCHEME_CALLBACK (constructor, ());
- DECLARE_SCHEME_CALLBACK (once_finalization, (SCM, SCM));
DECLARE_CLASSNAME (Push_property_iterator);
protected:
virtual void process (Moment);
- virtual void do_quit ();
};
class Pop_property_iterator : public Simple_music_iterator
SCM copied = SCM_EOL;
SCM partials = SCM_EOL;
// partials is a alist of partial overrides
- for (;;)
+ while (nested)
{
SCM elt = scm_car (nalist);
nalist = scm_cdr (nalist);
SCM key = scm_car (elt);
+ if (!scm_is_symbol (key))
+ --nested;
+ if (scm_is_bool (key))
+ {
+ if (scm_is_false (key))
+ continue;
+ elt = scm_cdr (elt);
+ key = scm_car (elt);
+ }
if (scm_is_pair (key))
// nested override: record for key in partial
{
partials);
else
scm_set_cdr_x (pair, scm_cons (elt, scm_cdr (pair)));
- if (!--nested)
- break;
+ continue;
}
- else
- // plain override: apply any known corresponding partials
+
+ // plain override: apply any known corresponding partials
+ SCM pair = assq_pop_x (key, &partials);
+ if (scm_is_true (pair))
{
- SCM pair = assq_pop_x (key, &partials);
- if (scm_is_true (pair))
- {
- SCM value = scm_cdr (elt);
- for (SCM pp = scm_cdr (pair); scm_is_pair (pp); pp = scm_cdr (pp))
- value = nested_property_alist (value, scm_cdaar (pp), scm_cdar (pp));
- copied = scm_acons (key, value, copied);
- }
- else
- copied = scm_cons (elt, copied);
+ SCM value = scm_cdr (elt);
+ for (SCM pp = scm_cdr (pair); scm_is_pair (pp); pp = scm_cdr (pp))
+ value = nested_property_alist (value, scm_cdaar (pp), scm_cdar (pp));
+ copied = scm_acons (key, value, copied);
}
+ else
+ copied = scm_cons (elt, copied);
}
// Now need to work off the remaining partials. All of them are
// unique, so we can push them to `copied' after resolving without
{
Context *o = get_outlet ();
Music *m = get_music ();
- bool once = to_boolean (m->get_property ("once"));
- SCM symbol = m->get_property ("symbol");
- SCM previous_value = SCM_UNDEFINED;
- if (once)
- o->here_defined (symbol, &previous_value);
send_stream_event (o, "SetProperty", m->origin (),
- ly_symbol2scm ("symbol"), symbol,
- ly_symbol2scm ("value"), m->get_property ("value"));
-
- /* For \once \set install a finalization hook to reset the property to the
- * previous value after the timestep */
- if (once)
- {
- Global_context *tg = get_outlet ()->get_global_context ();
- tg->add_finalization (scm_list_4 (once_finalization_proc,
- o->self_scm (), m->self_scm (),
- previous_value));
- }
+ ly_symbol2scm ("symbol"), m->get_property ("symbol"),
+ ly_symbol2scm ("value"), m->get_property ("value"),
+ ly_symbol2scm ("once"), m->get_property ("once"));
Simple_music_iterator::process (mom);
}
{
Context *o = get_outlet ();
Music *m = get_music ();
- bool once = to_boolean (m->get_property ("once"));
- SCM symbol = m->get_property ("symbol");
- SCM previous_value = SCM_UNDEFINED;
- if (once)
- o->here_defined (symbol, &previous_value);
send_stream_event (o, "UnsetProperty", m->origin (),
- ly_symbol2scm ("symbol"), symbol);
-
- /* For \once \unset install a finalization hook to reset the property to the
- * previous value after the timestep */
- if (once && !SCM_UNBNDP (previous_value))
- {
- Global_context *tg = get_outlet ()->get_global_context ();
- tg->add_finalization (scm_list_4 (Property_iterator::once_finalization_proc,
- o->self_scm (), m->self_scm (),
- previous_value));
- }
-
- Simple_music_iterator::process (mom);
-}
-
-MAKE_SCHEME_CALLBACK (Property_iterator, once_finalization, 3);
-SCM
-Property_iterator::once_finalization (SCM ctx, SCM music, SCM previous_value)
-{
- Music *m = unsmob<Music> (music);
- Context *c = unsmob<Context> (ctx);
-
- // Do not use UnsetProperty, which sets the default, but rather
- // cache the value before the \once \set command and restore it now
- send_stream_event (c, "SetProperty", m->origin (),
ly_symbol2scm ("symbol"), m->get_property ("symbol"),
- ly_symbol2scm ("value"), previous_value);
-
- return SCM_UNSPECIFIED;
-}
+ ly_symbol2scm ("once"), m->get_property ("once"));
-void
-Property_iterator::do_quit ()
-{
+ Simple_music_iterator::process (mom);
}
bool
{
SCM grob_property_path = get_property_path (get_music ());
SCM val = get_music ()->get_property ("grob-value");
+ SCM once = get_music ()->get_property ("once");
if (to_boolean (get_music ()->get_property ("pop-first"))
- && !to_boolean (get_music ()->get_property ("once")))
+ && !to_boolean (once))
send_stream_event (get_outlet (), "Revert", get_music ()->origin (),
ly_symbol2scm ("symbol"), sym,
ly_symbol2scm ("property-path"), grob_property_path);
send_stream_event (get_outlet (), "Override", get_music ()->origin (),
ly_symbol2scm ("symbol"), sym,
ly_symbol2scm ("property-path"), grob_property_path,
+ ly_symbol2scm ("once"), once,
ly_symbol2scm ("value"), val);
}
Simple_music_iterator::process (m);
}
-MAKE_SCHEME_CALLBACK (Push_property_iterator, once_finalization, 2);
-SCM
-Push_property_iterator::once_finalization (SCM ctx, SCM music)
-{
- Music *mus = unsmob<Music> (music);
- Context *c = unsmob<Context> (ctx);
-
- SCM sym = mus->get_property ("symbol");
- if (check_grob (mus, sym))
- {
- SCM grob_property_path = get_property_path (mus);
-
- send_stream_event (c, "Revert", mus->origin (),
- ly_symbol2scm ("symbol"), sym,
- ly_symbol2scm ("property-path"), grob_property_path);
- }
- return SCM_UNSPECIFIED;
-}
-
void
-Push_property_iterator::do_quit ()
+Pop_property_iterator::process (Moment mom)
{
- if (to_boolean (get_music ()->get_property ("once")))
- {
- SCM trans = get_outlet ()->self_scm ();
- SCM music = get_music ()->self_scm ();
-
- Global_context *tg = get_outlet ()->get_global_context ();
- tg->add_finalization (scm_list_3 (once_finalization_proc,
- trans, music));
- }
-}
-
-void
-Pop_property_iterator::process (Moment m)
-{
- SCM sym = get_music ()->get_property ("symbol");
+ Music *m = get_music ();
+ SCM sym = m->get_property ("symbol");
- if (check_grob (get_music (), sym))
+ if (check_grob (m, sym))
{
- SCM grob_property_path = get_property_path (get_music ());
+ SCM grob_property_path = get_property_path (m);
- send_stream_event (get_outlet (), "Revert", get_music ()->origin (),
+ send_stream_event (get_outlet (), "Revert", m->origin (),
ly_symbol2scm ("symbol"), sym,
+ ly_symbol2scm ("once"), m->get_property ("once"),
ly_symbol2scm ("property-path"), grob_property_path);
}
- Simple_music_iterator::process (m);
+ Simple_music_iterator::process (mom);
}
IMPLEMENT_CTOR_CALLBACK (Pop_property_iterator);
(new-line->lily-string))))
(define-display-method PropertyUnset (expr)
- (format #f "\\unset ~a~a~a"
+ (format #f "~a\\unset ~a~a~a"
+ (if (ly:music-property expr 'once #f) "\\once " "")
(if (eqv? (*current-context*) 'Bottom)
""
(format #f "~a . " (*current-context*)))
(define-display-method RevertProperty (expr)
(let* ((symbol (ly:music-property expr 'symbol))
(properties (ly:music-property expr 'grob-property-path
- (list (ly:music-property expr 'grob-property)))))
- (format #f "\\revert ~{~a~^.~}~a"
+ (list (ly:music-property expr
+ 'grob-property))))
+ (once (ly:music-property expr 'once #f)))
+ (format #f "~a\\revert ~{~a~^.~}~a"
+ (if once "\\once " "")
(if (eqv? (*current-context*) 'Bottom)
(cons symbol properties)
(cons* (*current-context*) symbol properties))