X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fligature-engraver.cc;h=f1b120d146cb44ca5126265b703bcfed145ca8fc;hb=04ae3a59a98ccdfa95db5664c88223a5805dc3db;hp=bc96697c58acb1ddc1b2c624a90fea48b423700a;hpb=487ee28c2a58e7bd5cb9e85c7676057c0b32f385;p=lilypond.git diff --git a/lily/ligature-engraver.cc b/lily/ligature-engraver.cc index bc96697c58..f1b120d146 100644 --- a/lily/ligature-engraver.cc +++ b/lily/ligature-engraver.cc @@ -3,15 +3,49 @@ source file of the GNU LilyPond music typesetter - (c) 2002 Juergen Reuter + (c) 2002--2004 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 requests 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 @@ -37,24 +71,16 @@ Ligature_engraver::Ligature_engraver () finished_ligature_ = 0; reqs_drul_[LEFT] = reqs_drul_[RIGHT] = 0; prev_start_req_ = 0; - last_bound = 0; + last_bound_ = 0; brew_ligature_primitive_proc = SCM_EOL; } bool Ligature_engraver::try_music (Music *m) { - if (m->is_mus_type ("abort-event")) + if (m->is_mus_type ("ligature-event")) { - reqs_drul_[START] = 0; - reqs_drul_[STOP] = 0; - if (ligature_) - ligature_->suicide (); - ligature_ = 0; - } - else if (m->is_mus_type ("ligature-event")) - { - Direction d = to_dir (m->get_mus_property ("span-direction")); + Direction d = to_dir (m->get_property ("span-direction")); reqs_drul_[d] = m; return true; } @@ -64,7 +90,56 @@ 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 (daddy_context_, ly_symbol2scm ("NoteHead")); + SCM value = ly_cdr (scm_sloppy_assq (source_callback, noteHeadProperties)); + execute_pushpop_property (daddy_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 (daddy_context_, symbol, key, SCM_UNDEFINED); } void @@ -73,29 +148,35 @@ Ligature_engraver::process_music () if (reqs_drul_[STOP]) { if (!ligature_) - reqs_drul_[STOP]->origin ()->warning (_ ("can't find start of ligature")); + { + reqs_drul_[STOP]->origin ()->warning (_ ("can't find start of ligature")); + return; + } + + if (!last_bound_) + { + reqs_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; + 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 (ligature_) @@ -107,7 +188,7 @@ Ligature_engraver::process_music () prev_start_req_ = reqs_drul_[START]; ligature_ = create_ligature_spanner (); brew_ligature_primitive_proc = - ligature_->get_grob_property ("ligature-primitive-callback"); + ligature_->get_property ("ligature-primitive-callback"); if (brew_ligature_primitive_proc == SCM_EOL) { warning ("Ligature_engraver: ligature-primitive-callback undefined"); @@ -125,37 +206,50 @@ Ligature_engraver::process_music () ligature_start_mom_ = now_mom (); - announce_grob(ligature_, reqs_drul_[START]->self_scm()); + announce_grob (ligature_, reqs_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 (); + reqs_drul_[START] = 0; + reqs_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")); @@ -163,17 +257,24 @@ Ligature_engraver::finalize () } } +Spanner * +Ligature_engraver::current_ligature () +{ + return ligature_; +} + void Ligature_engraver::acknowledge_grob (Grob_info info) { if (ligature_) { - if (Ligature_head::has_interface (info.grob_)) + if (Note_head::has_interface (info.grob_)) { - info.grob_->set_grob_property ("ligature-primitive-callback", - brew_ligature_primitive_proc); + primitives_.push (info); + info.grob_->set_property ("print-function", + brew_ligature_primitive_proc); } - else if (Rest::has_interface (info.grob_)) + 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")); @@ -185,8 +286,8 @@ Ligature_engraver::acknowledge_grob (Grob_info info) ENTER_DESCRIPTION (Ligature_engraver, /* descr */ "Abstract class; a concrete subclass handles Ligature_events by engraving Ligatures in a concrete style.", -/* creats*/ "Ligature", -/* accepts */ "ligature-event abort-event", -/* acks */ "ligature-head-interface rest-interface", +/* creats */ "", +/* accepts */ "ligature-event", +/* acks */ "note-head-interface rest-interface", /* reads */ "", /* write */ "");