X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fligature-engraver.cc;h=f3bac03c76955f62a913224224d3966aacc2016c;hb=3af0951f9a11677240efa6228683dd4fcea13eaf;hp=799ea1f0bce13f1645829d8ba365c89d2aae391e;hpb=c7b5938cc8295a21f793d550c468c3ca9cf0de14;p=lilypond.git diff --git a/lily/ligature-engraver.cc b/lily/ligature-engraver.cc index 799ea1f0bc..f3bac03c76 100644 --- a/lily/ligature-engraver.cc +++ b/lily/ligature-engraver.cc @@ -1,17 +1,68 @@ -/* - ligature-engraver.cc -- implement Ligature_engraver - - source file of the GNU LilyPond music typesetter - - (c) 2002 Juergen Reuter - - */ +/* + This file is part of LilyPond, the GNU music typesetter. + + Copyright (C) 2002--2015 Juergen Reuter + + LilyPond is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + LilyPond is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with LilyPond. If not, see . +*/ + #include "ligature-engraver.hh" -#include "ligature-head.hh" -#include "spanner.hh" -#include "score-engraver.hh" + +#include "context.hh" +#include "international.hh" +#include "note-head.hh" #include "rest.hh" -#include "warn.hh" +#include "spanner.hh" +#include "stream-event.hh" +#include "translator.icc" + +/* + * 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 *, vector). 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. The same approach is + * used for Kievan notation ligatures, or, rather melismas. + * Though these are not single connected objects, they behave much + * in the same way and have a fixed, small amount of space between + * noteheads. Except in Kievan "ligatures", accidentals have to be put + * to the left of the ligature, and not to the left of individual + * noteheads. In Kievan ligatures, the B-flat may be part of the + * ligature itself. 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 +75,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,162 +86,139 @@ 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 (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; - } - } - return false; -} - -Spanner * -Ligature_engraver::create_ligature_spanner () +void +Ligature_engraver::listen_ligature (Stream_event *ev) { - return new Spanner (SCM_EOL); + Direction d = to_dir (ev->get_property ("span-direction")); + ASSIGN_EVENT_ONCE (events_drul_[d], ev); } 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 (_ ("cannot 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); - } - } - prev_start_req_ = 0; + ligature_->set_bound (RIGHT, last_bound_); + + prev_start_event_ = 0; + finished_primitives_ = primitives_; finished_ligature_ = ligature_; + primitives_.clear (); ligature_ = 0; } - last_bound = unsmob_grob (get_property ("currentMusicalColumn")); + last_bound_ = unsmob (get_property ("currentMusicalColumn")); if (ligature_) { // TODO: maybe forbid breaks only if not transcribing - top_engraver ()->forbid_breaks (); + context ()->get_score_context ()->set_property ("forbidBreak", SCM_BOOL_T); } - if (reqs_drul_[START]) + + if (events_drul_[START]) { if (ligature_) - { - reqs_drul_[START]->origin ()->warning (_ ("already have a ligature")); - return; - } + { + 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"); - if (brew_ligature_primitive_proc == SCM_EOL) - { - warning ("Ligature_engraver: ligature-primitive-callback undefined"); - } - - Grob *bound = unsmob_grob (get_property ("currentMusicalColumn")); + + Grob *bound = unsmob (get_property ("currentMusicalColumn")); if (!bound) - { - reqs_drul_[START]->origin ()->warning (_ ("no left bound")); - } + events_drul_[START]->origin ()->warning (_ ("no left bound")); else - { - ligature_->set_bound (LEFT, bound); - } + ligature_->set_bound (LEFT, bound); 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 ()); } } void -Ligature_engraver::start_translation_timestep () +Ligature_engraver::stop_translation_timestep () { - reqs_drul_[START] = 0; - reqs_drul_[STOP] = 0; + if (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; + } + + events_drul_[START] = 0; + events_drul_[STOP] = 0; } void -Ligature_engraver::try_stop_ligature () +Ligature_engraver::finalize () { if (finished_ligature_) { - typeset_grob (finished_ligature_); + typeset_ligature (finished_ligature_, finished_primitives_); + finished_primitives_.clear (); finished_ligature_ = 0; } + if (ligature_) + { + prev_start_event_->origin ()->warning (_ ("unterminated ligature")); + ligature_->suicide (); + } } -void -Ligature_engraver::stop_translation_timestep () +Spanner * +Ligature_engraver::current_ligature () { - try_stop_ligature (); + return ligature_; } void -Ligature_engraver::finalize () +Ligature_engraver::acknowledge_ligature_head (Grob_info info) { - try_stop_ligature (); if (ligature_) { - prev_start_req_->origin ()->warning (_ ("unterminated ligature")); - ligature_->suicide (); + primitives_.push_back (info); + if (info.grob () && !scm_is_null (brew_ligature_primitive_proc)) + info.grob ()->set_property ("stencil", brew_ligature_primitive_proc); } } void -Ligature_engraver::acknowledge_grob (Grob_info info) +Ligature_engraver::acknowledge_rest (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? - } + info.event_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? } } -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 */ "ligature-primitive-callback"); +// no ADD_ACKNOWLEDGER / ADD_ACKNOWLEDGER / ADD_TRANSLATOR macro calls +// since this class is abstract