]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/ligature-engraver.cc
($(outdir)/%.pdf): add DVIPS_FLAGS. This will
[lilypond.git] / lily / ligature-engraver.cc
index 3344e363a6b44f87bc05ea82d1787c31a0be3efc..a026118b98667e4b2dc3406725988d42bd772aa6 100644 (file)
@@ -3,15 +3,48 @@
   
   source file of the GNU LilyPond music typesetter
   
-  (c) 2002 Juergen Reuter <reuter@ipd.uka.de>
+  (c) 2002--2004 Juergen Reuter <reuter@ipd.uka.de>
   
  */
 #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 "translator-group.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<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.  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,22 +70,14 @@ 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"))
-    {
-      reqs_drul_[START] = 0;
-      reqs_drul_[STOP] = 0;
-      if (ligature_)
-       ligature_->suicide ();
-      ligature_ = 0;
-    }
-  else if (m->is_mus_type ("ligature-event"))
+  if (m->is_mus_type ("ligature-event"))
     {
       Direction d = to_dir (m->get_mus_property ("span-direction"));
       reqs_drul_[d] = m;
@@ -64,38 +89,93 @@ Ligature_engraver::try_music (Music *m)
 Spanner *
 Ligature_engraver::create_ligature_spanner ()
 {
+  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_trans_, ly_symbol2scm ("NoteHead"));
+  SCM value = ly_cdr (scm_sloppy_assq (source_callback, noteHeadProperties));
+  execute_pushpop_property (daddy_trans_, 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_trans_, symbol, key, SCM_UNDEFINED);
+}
+
 void
 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 ();
     }
+
   if (reqs_drul_[START])
     {
       if (ligature_)
@@ -126,36 +206,49 @@ Ligature_engraver::process_music ()
       ligature_start_mom_ = now_mom ();
       
       announce_grob(ligature_, reqs_drul_[START]->self_scm());
+      override_stencil_callback ();
     }
 }
 
 void
-Ligature_engraver::start_translation_timestep ()
+Ligature_engraver::typeset_ligature (Spanner *, Array<Grob_info>)
 {
-  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 +256,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",
+         primitives_.push (info);
+         info.grob_->set_grob_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"));
@@ -186,7 +286,7 @@ 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 */      "",
-/* accepts */     "ligature-event abort-event",
-/* acks  */      "ligature-head-interface rest-interface",
+/* accepts */     "ligature-event",
+/* acks  */      "note-head-interface rest-interface",
 /* reads */       "",
 /* write */       "");