X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fligature-engraver.cc;h=7f47ce516572e1479b7137b582bf360d25b09702;hb=83ed05cef3bfcbd69f4148e8ee0669ca0664e214;hp=899c3585cd977fb877bf0761817b64e9ee42f9e3;hpb=baf32731650d510882eeb52d02f4ca0c816c7c29;p=lilypond.git diff --git a/lily/ligature-engraver.cc b/lily/ligature-engraver.cc index 899c3585cd..7f47ce5165 100644 --- a/lily/ligature-engraver.cc +++ b/lily/ligature-engraver.cc @@ -1,17 +1,51 @@ -/* +/* ligature-engraver.cc -- implement Ligature_engraver - + source file of the GNU LilyPond music typesetter - - (c) 2002 Juergen Reuter - - */ + + (c) 2002--2005 Juergen Reuter +*/ + #include "ligature-engraver.hh" -#include "ligature-head.hh" + #include "spanner.hh" #include "score-engraver.hh" +#include "note-head.hh" #include "rest.hh" #include "warn.hh" +#include "context.hh" + +/* + * This abstract class provides the general framework for ligatures of + * any kind. It cares for handling start/stop ligatures events and + * collecting all noteheads inbetween, but delegates creation of a + * ligature spanner for each start/stop pair and typesetting of the + * ligature spanner to a concrete subclass. + * + * A concrete ligature engraver must subclass this class and provide + * functions create_ligature_spanner () and typeset_ligature + * (Spanner *, Array). Subclasses of this class basically + * fall into two categories. + * + * The first category consists of engravers that engrave ligatures in + * a way that really deserves the name ligature. That is, they + * produce a single connected graphical object of fixed width, + * consisting of noteheads and other primitives. Space may be + * inserted only after each ligature, if necessary, but in no case + * between the primitives of the ligature. Accidentals have to be put + * to the left of the ligature, and not to the left of individual + * noteheads. Class Coherent_ligature_engraver is the common + * superclass for all of these engravers. + * + * The second category is for engravers that are relaxed in the sense + * that they do not require to produce a single connected graphical + * object. For example, in contemporary editions, ligatures are often + * marked, but otherwise use contemporary notation and spacing. In + * this category, there is currently only a single class, + * Ligature_bracket_engraver, which marks each ligature with a + * horizontal sqare bracket, but otherwise leaves the appearance + * untouched. + */ /* * TODO: lyrics/melisma/syllables: there should be at most one @@ -24,7 +58,7 @@ * Mensural_ligature_engraver) to the proper translator * (e.g. VoiceContext) to choose between various representations. * Since adding/removing an engraver to a translator is a global - * action in the paper block, you can not mix various representations + * action in the layout block, you cannot mix various representations * _within_ the same score. Hence, for selecting a representation, * one would rather like to have a property that can be set e.g. for * several staves individually. However, it seems that this approach @@ -35,33 +69,20 @@ Ligature_engraver::Ligature_engraver () { ligature_ = 0; finished_ligature_ = 0; - reqs_drul_[LEFT] = reqs_drul_[RIGHT] = 0; - prev_start_req_ = 0; - last_bound = 0; + events_drul_[LEFT] = events_drul_[RIGHT] = 0; + prev_start_event_ = 0; + last_bound_ = 0; brew_ligature_primitive_proc = SCM_EOL; } bool Ligature_engraver::try_music (Music *m) { - if (Span_req *req_ = dynamic_cast (m)) + if (m->is_mus_type ("ligature-event")) { - if (scm_equal_p (req_->get_mus_property ("span-type"), - scm_makfrom0str ("abort")) == SCM_BOOL_T) - { - reqs_drul_[START] = 0; - reqs_drul_[STOP] = 0; - if (ligature_) - ligature_->suicide (); - ligature_ = 0; - } - else if (scm_equal_p (req_->get_mus_property ("span-type"), - scm_makfrom0str ("ligature")) == SCM_BOOL_T) - { - Direction d = req_->get_span_dir (); - reqs_drul_[d] = req_; - return true; - } + Direction d = to_dir (m->get_property ("span-direction")); + events_drul_[d] = m; + return true; } return false; } @@ -69,59 +90,114 @@ Ligature_engraver::try_music (Music *m) Spanner * Ligature_engraver::create_ligature_spanner () { - return new Spanner (SCM_EOL); + programming_error ("Ligature_engraver::create_ligature_spanner (): " + "this is an abstract method that should not be called, " + "but overridden by a subclass"); + return 0; +} + +/* + * This method should do something that comes close to the following + * .ly snippet: + * + * \property Voice.NoteHead \override #'print-function = + * < value of #'ligature-primitive-callback of Voice.NoteHead > + * + * TODO: What we are doing here on the c++ level, should actually be + * performed on the SCM level. However, I do not know how to teach + * lilypond to apply an \override and \revert on #'print-function, + * whenever lily encounters a \[ and \] in an .ly file, respectively. + * Also encounter, that lily should not crash if a user erronously + * nests \[ and \]. + */ +void +Ligature_engraver::override_stencil_callback () +{ + SCM target_callback = ly_symbol2scm ("print-function"); + SCM source_callback = ly_symbol2scm ("ligature-primitive-callback"); + SCM noteHeadProperties = updated_grob_properties (context (), ly_symbol2scm ("NoteHead")); + SCM value = scm_cdr (scm_sloppy_assq (source_callback, noteHeadProperties)); + execute_pushpop_property (context (), ly_symbol2scm ("NoteHead"), + target_callback, value); +} + +/* + * This method should do something that comes close to the following + * .ly snippet: + * + * \property Voice.NoteHead \revert #'print-function + * + * TODO: What we are doing here on the c++ level, should actually be + * performed on the SCM level. However, I do not know how to teach + * lilypond to apply an \override and \revert on #'print-function, + * whenever lily encounters a \[ and \] in an .ly file, respectively. + * Also encounter, that lily should not crash if a user erronously + * nests \[ and \]. + */ +void +Ligature_engraver::revert_stencil_callback () +{ + SCM symbol = ly_symbol2scm ("NoteHead"); + SCM key = ly_symbol2scm ("print-function"); + execute_pushpop_property (context (), symbol, key, SCM_UNDEFINED); } void Ligature_engraver::process_music () { - if (reqs_drul_[STOP]) + if (events_drul_[STOP]) { if (!ligature_) - reqs_drul_[STOP]->origin ()->warning (_ ("can't find start of ligature")); + { + events_drul_[STOP]->origin ()->warning (_ ("can't find start of ligature")); + return; + } + + if (!last_bound_) + { + events_drul_[STOP]->origin ()->warning (_ ("no right bound")); + } else { - if (!last_bound) - { - reqs_drul_[STOP]->origin ()->warning (_ ("no right bound")); - } - else - { - ligature_->set_bound (RIGHT, last_bound); - } + ligature_->set_bound (RIGHT, last_bound_); } - prev_start_req_ = 0; + + prev_start_event_ = 0; + finished_primitives_ = primitives_; finished_ligature_ = ligature_; + primitives_.clear (); ligature_ = 0; + revert_stencil_callback (); } - last_bound = unsmob_grob (get_property ("currentMusicalColumn")); + last_bound_ = unsmob_grob (get_property ("currentMusicalColumn")); if (ligature_) { // TODO: maybe forbid breaks only if not transcribing - top_engraver ()->forbid_breaks (); + get_score_engraver ()->forbid_breaks (); } - if (reqs_drul_[START]) + + if (events_drul_[START]) { if (ligature_) { - reqs_drul_[START]->origin ()->warning (_ ("already have a ligature")); + events_drul_[START]->origin ()->warning (_ ("already have a ligature")); return; } - prev_start_req_ = reqs_drul_[START]; + prev_start_event_ = events_drul_[START]; ligature_ = create_ligature_spanner (); - brew_ligature_primitive_proc = - ligature_->get_grob_property ("ligature-primitive-callback"); + brew_ligature_primitive_proc + = ligature_->get_property ("ligature-primitive-callback"); if (brew_ligature_primitive_proc == SCM_EOL) { - warning ("Ligature_engraver: ligature-primitive-callback undefined"); + programming_error ("Ligature_engraver: ligature-primitive-callback undefined"); } Grob *bound = unsmob_grob (get_property ("currentMusicalColumn")); if (!bound) { - reqs_drul_[START]->origin ()->warning (_ ("no left bound")); + events_drul_[START]->origin ()->warning (_ ("no left bound")); } else { @@ -129,68 +205,94 @@ Ligature_engraver::process_music () } ligature_start_mom_ = now_mom (); - - announce_grob(ligature_, reqs_drul_[START]->self_scm()); + + // TODO: dump cause into make_item/spanner. + // announce_grob (ligature_, events_drul_[START]->self_scm ()); + override_stencil_callback (); } } void -Ligature_engraver::start_translation_timestep () +Ligature_engraver::typeset_ligature (Spanner *, Array) { - reqs_drul_[START] = 0; - reqs_drul_[STOP] = 0; + programming_error ("Ligature_engraver::typeset_ligature (): " + "this is an abstract method that should not be called, " + "but overridden by a subclass"); } void -Ligature_engraver::try_stop_ligature () +Ligature_engraver::stop_translation_timestep () { if (finished_ligature_) { - typeset_grob (finished_ligature_); + if (!finished_primitives_.size ()) + { + finished_ligature_->programming_error ("Ligature_engraver::stop_translation_timestep (): " + "junking empty ligature"); + } + else + { + typeset_ligature (finished_ligature_, finished_primitives_); + finished_primitives_.clear (); + } finished_ligature_ = 0; } -} -void -Ligature_engraver::stop_translation_timestep () -{ - try_stop_ligature (); + events_drul_[START] = 0; + events_drul_[STOP] = 0; } void Ligature_engraver::finalize () { - try_stop_ligature (); + if (finished_ligature_) + { + typeset_ligature (finished_ligature_, finished_primitives_); + finished_primitives_.clear (); + finished_ligature_ = 0; + } if (ligature_) { - prev_start_req_->origin ()->warning (_ ("unterminated ligature")); + prev_start_event_->origin ()->warning (_ ("unterminated ligature")); ligature_->suicide (); } } +Spanner * +Ligature_engraver::current_ligature () +{ + return ligature_; +} + void -Ligature_engraver::acknowledge_grob (Grob_info info) +Ligature_engraver::acknowledge_note_head (Grob_info info) { if (ligature_) { - if (Ligature_head::has_interface (info.grob_)) - { - info.grob_->set_grob_property ("ligature-primitive-callback", - brew_ligature_primitive_proc); - } - else if (Rest::has_interface (info.grob_)) - { - info.music_cause ()->origin ()->warning (_ ("ligature may not contain rest; ignoring rest")); - prev_start_req_->origin ()->warning (_ ("ligature was started here")); - // TODO: maybe better should stop ligature here rather than - // ignoring the rest? - } + primitives_.push (info); + info.grob ()->set_property ("print-function", + brew_ligature_primitive_proc); } } -ENTER_DESCRIPTION(Ligature_engraver, -/* descr */ "Abstract class; a concrete subclass handles Ligature_requests by engraving Ligatures in a concrete style.", -/* creats*/ "Ligature", -/* acks */ "ligature-head-interface rest-interface", -/* reads */ "", -/* write */ ""); +void +Ligature_engraver::acknowledge_rest (Grob_info info) +{ + info.music_cause ()->origin ()->warning (_ ("ignoring rest: ligature may not contain rest")); + prev_start_event_->origin ()->warning (_ ("ligature was started here")); + // TODO: maybe better should stop ligature here rather than + // ignoring the rest? +} + + +#include "translator.icc" + +ADD_ACKNOWLEDGER(Ligature_engraver, rest); +ADD_ACKNOWLEDGER(Ligature_engraver, note_head); +ADD_TRANSLATOR (Ligature_engraver, + /* descr */ "Abstract class; a concrete subclass handles Ligature_events by engraving Ligatures in a concrete style.", + /* creats */ "", + /* accepts */ "ligature-event", + /* acks */ "", + /* reads */ "", + /* write */ "");