]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/ligature-engraver.cc
Run grand-replace (issue 3765)
[lilypond.git] / lily / ligature-engraver.cc
index 17414179cb3db1064de6c6e0b3cc102117c3f6d0..8bd4d66137b4907c832bd0981ce739f507d81ed3 100644 (file)
@@ -1,18 +1,68 @@
-/*   
-  ligature-engraver.cc -- implement Ligature_engraver
-  
-  source file of the GNU LilyPond music typesetter
-  
-  (c) 2002--2003 Juergen Reuter <reuter@ipd.uka.de>
-  
- */
+/*
+  This file is part of LilyPond, the GNU music typesetter.
+
+  Copyright (C) 2002--2014 Juergen Reuter <reuter@ipd.uka.de>
+
+  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 <http://www.gnu.org/licenses/>.
+*/
+
 #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<Grob_info>).  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
@@ -25,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
@@ -36,60 +86,36 @@ Ligature_engraver::Ligature_engraver ()
 {
   ligature_ = 0;
   finished_ligature_ = 0;
-  reqs_drul_[LEFT] = reqs_drul_[RIGHT] = 0;
-  prev_start_req_ = 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 (m->is_mus_type ("abort-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"));
-      reqs_drul_[d] = m;
-      return true;
-    }
-  return false;
-}
-
-Spanner *
-Ligature_engraver::create_ligature_spanner ()
+void
+Ligature_engraver::listen_ligature (Stream_event *ev)
 {
-  programming_error ("Ligature_engraver::create_ligature_spanner (): "
-                    "this is an abstract method that should not be called, "
-                    "but overridden by a subclass");
-  return 0;
+  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 ();
@@ -100,54 +126,31 @@ Ligature_engraver::process_music ()
   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"));
       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());
-    }
-}
 
-void
-Ligature_engraver::start_translation_timestep ()
-{
-  reqs_drul_[START] = 0;
-  reqs_drul_[STOP] = 0;
-}
-
-void
-Ligature_engraver::typeset_ligature (Spanner *, Array<Grob_info>)
-{
-  programming_error ("Ligature_engraver::typeset_ligature (): "
-                    "this is an abstract method that should not be called, "
-                    "but overridden by a subclass");
+      // TODO: dump cause into make_item/spanner.
+      // announce_grob (ligature_, events_drul_[START]->self_scm ());
+    }
 }
 
 void
@@ -155,10 +158,21 @@ Ligature_engraver::stop_translation_timestep ()
 {
   if (finished_ligature_)
     {
-      typeset_ligature (finished_ligature_, finished_primitives_);
-      finished_primitives_.clear ();
+      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
@@ -172,7 +186,7 @@ Ligature_engraver::finalize ()
     }
   if (ligature_)
     {
-      prev_start_req_->origin ()->warning (_ ("unterminated ligature"));
+      prev_start_event_->origin ()->warning (_ ("unterminated ligature"));
       ligature_->suicide ();
     }
 }
@@ -184,33 +198,27 @@ Ligature_engraver::current_ligature ()
 }
 
 void
-Ligature_engraver::acknowledge_grob (Grob_info info)
+Ligature_engraver::acknowledge_ligature_head (Grob_info info)
+{
+  if (ligature_)
+    {
+      primitives_.push_back (info);
+      if (info.grob () && brew_ligature_primitive_proc != SCM_EOL)
+        info.grob ()->set_property ("stencil", brew_ligature_primitive_proc);
+    }
+}
+
+void
+Ligature_engraver::acknowledge_rest (Grob_info info)
 {
   if (ligature_)
     {
-      if (Note_head::has_interface (info.grob_))
-       {
-         primitives_.push (info);
-       }
-      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_events by engraving Ligatures in a concrete style.",
-/* creats */      "",
-/* accepts */     "ligature-event abort-event",
-/* acks  */      "ligature-head-interface rest-interface",
-/* reads */       "",
-/* write */       "");
+// no ADD_ACKNOWLEDGER / ADD_ACKNOWLEDGER / ADD_TRANSLATOR macro calls
+// since this class is abstract