]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/vaticana-ligature-engraver.cc
Run `make grand-replace'.
[lilypond.git] / lily / vaticana-ligature-engraver.cc
index 82bf53aaaceca0aeb11194f52abf6c946506d553..53bec433d7c196be7b7fb5a9728ac273cd6e8685 100644 (file)
@@ -3,19 +3,25 @@
 
   source file of the GNU LilyPond music typesetter
 
-  (c) 2003--2005 Juergen Reuter <reuter@ipd.uka.de>
+  (c) 2003--2008 Juergen Reuter <reuter@ipd.uka.de>
 */
 
 #include "gregorian-ligature-engraver.hh"
+
+#include "font-interface.hh"
 #include "gregorian-ligature.hh"
-#include "vaticana-ligature.hh"
+#include "international.hh"
+#include "output-def.hh"
+#include "paper-column.hh"
+#include "separation-item.hh"
 #include "spanner.hh"
 #include "staff-symbol-referencer.hh"
-#include "font-interface.hh"
+#include "stream-event.hh"
+#include "vaticana-ligature.hh"
 #include "warn.hh"
-#include "output-def.hh"
-#include "paper-column.hh"
-
+#include "dot-column.hh"
+#include "rhythmic-head.hh"
+#include "pitch.hh"
 #include "translator.icc"
 
 /*
  * style ligatures for Gregorian chant notation.
  */
 
+/*
+ * TODO: Maybe move handling of dots/mora to
+ * Gregorian_ligature_engraver?  It's probably common for all types of
+ * Gregorian chant notation that have dotted notes.
+ *
+ * FIXME: The horizontal alignment of the mora column is bad (too far
+ * to the left), if the last dotted note is not the last primitive in
+ * the ligature.  Fortunately, in practice this bug should have no
+ * negative impact, since dotted notes appear within a ligature
+ * usually always at the end of the ligature, such that the bug never
+ * should apply for valid ligatures.
+ *
+ * TODO: Graduale Triplex, tempus per annum, hebdomada septima,
+ * alleluia (page 280) shows a counter-example for collecting dots
+ * always in a single column behind the ligature.  Maybe only the last
+ * two dots in a ligature should be collected and all other dots put
+ * behind or on top of the head?
+ */
 class Vaticana_ligature_engraver : public Gregorian_ligature_engraver
 {
 
@@ -32,9 +56,13 @@ private:
                               int context_info, int delta_pitch);
   bool is_stacked_head (int prefix_set,
                        int context_info);
-  Real align_heads (Array<Grob_info> primitives,
+  Real align_heads (vector<Grob_info> primitives,
                    Real flexa_width,
                    Real thickness);
+  void check_for_prefix_loss (Item *primitive);
+  void check_for_ambiguous_dot_pitch (Grob_info primitive);
+  void add_mora_column (Paper_column *column);
+  vector<Grob_info> augmented_primitives_;
 
 public:
   TRANSLATOR_DECLARATIONS (Vaticana_ligature_engraver);
@@ -42,13 +70,30 @@ public:
 protected:
   virtual Spanner *create_ligature_spanner ();
   virtual void transform_heads (Spanner *ligature,
-                               Array<Grob_info> primitives);
+                               vector<Grob_info> primitives);
+  DECLARE_TRANSLATOR_LISTENER (pes_or_flexa);
+  DECLARE_TRANSLATOR_LISTENER (ligature);
 };
 
+IMPLEMENT_TRANSLATOR_LISTENER (Vaticana_ligature_engraver, pes_or_flexa);
+void
+Vaticana_ligature_engraver::listen_pes_or_flexa (Stream_event *ev)
+{
+  Gregorian_ligature_engraver::listen_pes_or_flexa (ev);
+}
+
+IMPLEMENT_TRANSLATOR_LISTENER (Vaticana_ligature_engraver, ligature);
+void
+Vaticana_ligature_engraver::listen_ligature (Stream_event *ev)
+{
+  Ligature_engraver::listen_ligature (ev);
+}
+
 Vaticana_ligature_engraver::Vaticana_ligature_engraver ()
 {
   brew_ligature_primitive_proc = 
     Vaticana_ligature::brew_ligature_primitive_proc;
+  augmented_primitives_.clear ();
 }
 
 Spanner *
@@ -61,41 +106,41 @@ bool
 Vaticana_ligature_engraver::is_stacked_head (int prefix_set,
                                             int context_info)
 {
-  bool is_stacked_b;
+  bool is_stacked;
 
   // upper head of pes is stacked upon lower head of pes ...
-  is_stacked_b = context_info & PES_UPPER;
+  is_stacked = context_info & PES_UPPER;
 
   // ... unless this note starts a flexa
   if (context_info & FLEXA_LEFT)
-    is_stacked_b = false;
+    is_stacked = false;
 
   // ... or another pes
   if (context_info & PES_LOWER)
-    is_stacked_b = false;
+    is_stacked = false;
 
   // ... or the previous note is a semivocalis or inclinatum
   if (context_info & AFTER_DEMINUTUM)
-    is_stacked_b = false;
+    is_stacked = false;
 
   // auctum head is never stacked upon preceding note
   if (prefix_set & AUCTUM)
-    is_stacked_b = false;
+    is_stacked = false;
 
   // virga is never stacked upon preceding note
   if (prefix_set & VIRGA)
-    is_stacked_b = false;
+    is_stacked = false;
 
   // oriscus is never stacked upon preceding note
   if (prefix_set & ORISCUS)
-    is_stacked_b = false;
+    is_stacked = false;
 
   if ((prefix_set & DEMINUTUM)
       && ! (prefix_set & INCLINATUM)
       && (context_info & FLEXA_RIGHT))
-    is_stacked_b = true; // semivocalis head of deminutus form
+    is_stacked = true; // semivocalis head of deminutus form
 
-  return is_stacked_b;
+  return is_stacked;
 }
 
 /*
@@ -143,7 +188,7 @@ Vaticana_ligature_engraver::need_extra_horizontal_space (int prev_prefix_set, in
 }
 
 Real
-Vaticana_ligature_engraver::align_heads (Array<Grob_info> primitives,
+Vaticana_ligature_engraver::align_heads (vector<Grob_info> primitives,
                                         Real flexa_width,
                                         Real thickness)
 {
@@ -161,7 +206,7 @@ Vaticana_ligature_engraver::align_heads (Array<Grob_info> primitives,
     = dynamic_cast<Item *> (primitives[0].grob ())->get_column ();
 
   Real join_thickness
-    = thickness * column->layout ()->get_dimension (ly_symbol2scm ("linethickness"));
+    = thickness * column->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
 
   /*
    * Amount of extra space two put between some particular
@@ -178,7 +223,7 @@ Vaticana_ligature_engraver::align_heads (Array<Grob_info> primitives,
 
   Item *prev_primitive = 0;
   int prev_prefix_set = 0;
-  for (int i = 0; i < primitives.size (); i++)
+  for (vsize i = 0; i < primitives.size (); i++)
     {
       Item *primitive = dynamic_cast<Item *> (primitives[i].grob ());
       int prefix_set
@@ -194,23 +239,23 @@ Vaticana_ligature_engraver::align_heads (Array<Grob_info> primitives,
       if (glyph_name_scm == SCM_EOL)
        {
          primitive->programming_error ("Vaticana_ligature:"
-                                       "undefined glyph-name -> "
-                                       "ignoring grob");
+                                       " undefined glyph-name ->"
+                                       " ignoring grob");
          continue;
        }
-      String glyph_name = ly_scm2string (glyph_name_scm);
+      string glyph_name = ly_scm2string (glyph_name_scm);
 
       int delta_pitch = 0;
       if (prev_primitive) /* urgh, need prev_primitive only here */
        {
-         SCM delta_pitch_scm = prev_primitive->get_property ("delta-pitch");
+         SCM delta_pitch_scm = prev_primitive->get_property ("delta-position");
          if (delta_pitch_scm != SCM_EOL)
            delta_pitch = scm_to_int (delta_pitch_scm);
          else
            {
              primitive->programming_error ("Vaticana_ligature:"
-                                           "delta-pitch undefined -> "
-                                           "ignoring grob");
+                                           " delta-position undefined ->"
+                                           " ignoring grob");
              continue;
            }
        }
@@ -234,10 +279,9 @@ Vaticana_ligature_engraver::align_heads (Array<Grob_info> primitives,
          head_width = 0.0;
          x_offset = join_thickness
            - Font_interface::get_default_font (primitive)->
-           find_by_name ("noteheads." + glyph_name).extent (X_AXIS).length ();
+           find_by_name ("noteheads.s" + glyph_name).extent (X_AXIS).length ();
        }
-      else if (!String::compare (glyph_name, "flexa")
-              || !String::compare (glyph_name, ""))
+      else if (glyph_name == "flexa" || glyph_name == "")
        {
          /*
           * This head represents either half of a flexa shape.
@@ -254,7 +298,7 @@ Vaticana_ligature_engraver::align_heads (Array<Grob_info> primitives,
           */
          head_width
            = Font_interface::get_default_font (primitive)->
-           find_by_name ("noteheads." + glyph_name).extent (X_AXIS).length ();
+           find_by_name ("noteheads.s" + glyph_name).extent (X_AXIS).length ();
          x_offset = 0.0;
        }
 
@@ -290,7 +334,7 @@ Vaticana_ligature_engraver::align_heads (Array<Grob_info> primitives,
              ligature_width -= join_thickness;
            }
        }
-      else if (!String::compare (glyph_name, ""))
+      else if (glyph_name == "")
        {
          /*
           * This is the 2nd (virtual) head of flexa shape.  Join it
@@ -307,8 +351,7 @@ Vaticana_ligature_engraver::align_heads (Array<Grob_info> primitives,
       /*
        * Horizontally line-up this head to form a ligature.
        */
-      get_set_column (primitive, column);
-      primitive->translate_axis (ligature_width, X_AXIS);
+      move_related_items_to_column (primitive, column, ligature_width);
       ligature_width += head_width;
 
       prev_primitive = primitive;
@@ -336,22 +379,89 @@ Vaticana_ligature_engraver::align_heads (Array<Grob_info> primitives,
  * primitive were engraved as a stand-alone head.
  */
 void
-check_for_prefix_loss (Item *primitive)
+Vaticana_ligature_engraver::check_for_prefix_loss (Item *primitive)
 {
   int prefix_set
     = scm_to_int (primitive->get_property ("prefix-set"));
   if (prefix_set & ~PES_OR_FLEXA)
     {
-      String prefs = Gregorian_ligature::prefixes_to_str (primitive);
+      string prefs = Gregorian_ligature::prefixes_to_str (primitive);
       primitive->warning (_f ("ignored prefix (es) `%s' of this head according "
                              "to restrictions of the selected ligature style",
-                             prefs.to_str0 ()));
+                             prefs.c_str ()));
+    }
+}
+
+void
+Vaticana_ligature_engraver::add_mora_column (Paper_column *column)
+{
+  if (augmented_primitives_.size () == 0) // no dot for column
+    return;
+  if (!column) // empty ligature???
+    {
+      augmented_primitives_[0].grob ()->
+       programming_error ("no paper column to add dot");
+      return;
+    }
+  Item *dotcol = make_item ("DotColumn", SCM_EOL);
+  dotcol->set_parent (column, X_AXIS);
+  for (vsize i = 0; i < augmented_primitives_.size (); i++)
+    {
+      Item *primitive =
+       dynamic_cast<Item *> (augmented_primitives_[i].grob ());
+      Item *dot = make_item ("Dots", primitive->self_scm ());
+      dot->set_property ("dot-count", scm_from_int (1));
+      dot->set_parent (primitive, Y_AXIS);
+      primitive->set_object ("dot", dot->self_scm ());
+      Dot_column::add_head (dotcol, primitive);
+
+      // FIXME: why isn't the dot picked up by Paper_column_engraver?
+      Separation_item::add_item (column, dot);
+    }
+}
+
+/*
+ * This function prints a warning, if the given primitive has the same
+ * pitch as at least one of the primitives already stored in the
+ * augmented_primitives_ array.
+ *
+ * The rationale of this check is, that, if there are two dotted
+ * primitives with the same pitch, then collecting all dots in a dot
+ * column behind the ligature leads to a notational ambiguity of to
+ * which head the corresponding dot refers.
+ *
+ * Such a case should be treated as a badly specified ligature.  The
+ * user should split the ligature to make the notation of dots
+ * unambiguous.
+ */
+void
+Vaticana_ligature_engraver::check_for_ambiguous_dot_pitch (Grob_info primitive)
+{
+  // TODO: Fix performance, which is currently O (n^2) (since this
+  // method is called O (n) times and takes O (n) steps in the for
+  // loop), but could be O (n) (by replacing the for loop by e.g. a
+  // bitmask based O (1) test); where n=<number of primitives in the
+  // ligature> (which is typically small (n<10), though).
+  Stream_event *new_cause = primitive.event_cause ();
+  int new_pitch = unsmob_pitch (new_cause->get_property ("pitch"))->steps ();
+  for (vsize i = 0; i < augmented_primitives_.size (); i++)
+    {
+      Stream_event *cause = augmented_primitives_[i].event_cause ();
+      int pitch = unsmob_pitch (cause->get_property ("pitch"))->steps ();
+      if (pitch == new_pitch)
+       {
+         primitive.grob ()->
+           warning ("Ambiguous use of dots in ligature: there are "
+                    "multiple dotted notes with the same pitch.  "
+                    "The ligature should be split.");
+         return; // supress multiple identical warnings
+       }
     }
 }
 
 void
 Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
-                                            Array<Grob_info> primitives)
+                                            vector<Grob_info> primitives)
 {
   Real flexa_width = robust_scm2double (ligature->get_property ("flexa-width"), 2);
 
@@ -361,20 +471,21 @@ Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
   int prev_prefix_set = 0;
   int prev_context_info = 0;
   int prev_delta_pitch = 0;
-  String prev_glyph_name = "";
-  for (int i = 0; i < primitives.size (); i++)
+  string prev_glyph_name = "";
+  augmented_primitives_.clear ();
+  for (vsize i = 0; i < primitives.size (); i++)
     {
       Item *primitive = dynamic_cast<Item *> (primitives[i].grob ());
 
       int delta_pitch;
-      SCM delta_pitch_scm = primitive->get_property ("delta-pitch");
+      SCM delta_pitch_scm = primitive->get_property ("delta-position");
       if (delta_pitch_scm != SCM_EOL)
        delta_pitch = scm_to_int (delta_pitch_scm);
       else
        {
          primitive->programming_error ("Vaticana_ligature:"
-                                       "delta-pitch undefined -> "
-                                       "ignoring grob");
+                                       " delta-position undefined ->"
+                                       " ignoring grob");
          continue;
        }
 
@@ -383,6 +494,27 @@ Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
        = scm_to_int (primitive->get_property ("prefix-set"));
       int context_info
        = scm_to_int (primitive->get_property ("context-info"));
+
+      if (Rhythmic_head::dot_count (primitive) > 0)
+       // remove dots from primitive and add remember primitive for
+       // creating a dot column
+       {
+         Rhythmic_head::get_dots (primitive)->set_property ("dot-count",
+                                                            scm_from_int (0));
+         // TODO: Maybe completely remove grob "Dots" (dots->suicide
+         // () ?) rather than setting property "dot-count" to 0.
+
+         check_for_ambiguous_dot_pitch (primitives[i]);
+         augmented_primitives_.push_back (primitives[i]);
+       }
+      else if (augmented_primitives_.size () > 0)
+       {
+         primitive->warning ("This ligature has a dotted head followed by "
+                             "a non-dotted head.  The ligature should be "
+                             "split after the last dotted head before "
+                             "this head.");
+       }
+
       if (is_stacked_head (prefix_set, context_info))
        {
          context_info |= STACKED_HEAD;
@@ -396,32 +528,32 @@ Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
        * this decision must be made here in the engraver rather than in
        * the backend).
        */
-      String glyph_name;
+      string glyph_name;
       if (prefix_set & VIRGA)
        {
-         glyph_name = "svaticana.punctum";
+         glyph_name = "vaticana.punctum";
          primitive->set_property ("add-stem", ly_bool2scm (true));
        }
       else if (prefix_set & QUILISMA)
-       glyph_name = "svaticana.quilisma";
+       glyph_name = "vaticana.quilisma";
       else if (prefix_set & ORISCUS)
-       glyph_name = "ssolesmes.oriscus";
+       glyph_name = "solesmes.oriscus";
       else if (prefix_set & STROPHA)
        if (prefix_set & AUCTUM)
-         glyph_name = "ssolesmes.stropha.aucta";
-       else glyph_name = "ssolesmes.stropha";
+         glyph_name = "solesmes.stropha.aucta";
+       else glyph_name = "solesmes.stropha";
       else if (prefix_set & INCLINATUM)
        if (prefix_set & AUCTUM)
-         glyph_name = "ssolesmes.incl.auctum";
+         glyph_name = "solesmes.incl.auctum";
        else if (prefix_set & DEMINUTUM)
-         glyph_name = "ssolesmes.incl.parvum";
+         glyph_name = "solesmes.incl.parvum";
        else
-         glyph_name = "svaticana.inclinatum";
+         glyph_name = "vaticana.inclinatum";
       else if (prefix_set & DEMINUTUM)
        if (i == 0)
          {
            // initio debilis
-           glyph_name = "svaticana.reverse.plica";
+           glyph_name = "vaticana.reverse.plica";
          }
        else if (prev_delta_pitch > 0)
          {
@@ -429,13 +561,13 @@ Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
            if (! (prev_context_info & FLEXA_RIGHT))
              /* correct head of previous primitive */
              if (prev_delta_pitch > 1)
-               prev_glyph_name = "svaticana.epiphonus";
+               prev_glyph_name = "vaticana.epiphonus";
              else
-               prev_glyph_name = "svaticana.vepiphonus";
+               prev_glyph_name = "vaticana.vepiphonus";
            if (prev_delta_pitch > 1)
-             glyph_name = "svaticana.plica";
+             glyph_name = "vaticana.plica";
            else
-             glyph_name = "svaticana.vplica";
+             glyph_name = "vaticana.vplica";
          }
        else if (prev_delta_pitch < 0)
          {
@@ -446,12 +578,12 @@ Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
                if (i > 1)
                  {
                    /* cephalicus head with fixed size cauda */
-                   prev_glyph_name = "svaticana.inner.cephalicus";
+                   prev_glyph_name = "vaticana.inner.cephalicus";
                  }
                else
                  {
                    /* cephalicus head without cauda */
-                   prev_glyph_name = "svaticana.cephalicus";
+                   prev_glyph_name = "vaticana.cephalicus";
                  }
 
                /*
@@ -468,43 +600,43 @@ Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
                                              ly_bool2scm (false));
              }
            if (prev_delta_pitch < - 1)
-             glyph_name = "svaticana.reverse.plica";
+             glyph_name = "vaticana.reverse.plica";
            else
-             glyph_name = "svaticana.reverse.vplica";
+             glyph_name = "vaticana.reverse.vplica";
          }
        else // (prev_delta_pitch == 0)
          {
            primitive->programming_error ("Vaticana_ligature:"
-                                         "deminutum head must have different "
-                                         "pitch -> ignoring grob");
+                                         " deminutum head must have different"
+                                         " pitch -> ignoring grob");
          }
       else if (prefix_set & (CAVUM | LINEA))
        if ((prefix_set & CAVUM) && (prefix_set & LINEA))
-         glyph_name = "svaticana.linea.punctum.cavum";
+         glyph_name = "vaticana.linea.punctum.cavum";
        else if (prefix_set & CAVUM)
-         glyph_name = "svaticana.punctum.cavum";
+         glyph_name = "vaticana.punctum.cavum";
        else
-         glyph_name = "svaticana.linea.punctum";
+         glyph_name = "vaticana.linea.punctum";
       else if (prefix_set & AUCTUM)
        if (prefix_set & ASCENDENS)
-         glyph_name = "ssolesmes.auct.asc";
+         glyph_name = "solesmes.auct.asc";
        else
-         glyph_name = "ssolesmes.auct.desc";
+         glyph_name = "solesmes.auct.desc";
       else if ((context_info & STACKED_HEAD)
               && (context_info & PES_UPPER))
        if (prev_delta_pitch > 1)
-         glyph_name = "svaticana.upes";
+         glyph_name = "vaticana.upes";
        else
-         glyph_name = "svaticana.vupes";
+         glyph_name = "vaticana.vupes";
       else
-       glyph_name = "svaticana.punctum";
+       glyph_name = "vaticana.punctum";
 
       /*
        * This head needs a cauda, if it starts a flexa, is not the upper
        * head of a pes, and if it is a punctum.
        */
       if ((context_info & FLEXA_LEFT) && ! (context_info & PES_UPPER))
-       if (!String::compare (glyph_name, "svaticana.punctum"))
+       if (glyph_name == "vaticana.punctum")
          primitive->set_property ("add-cauda", ly_bool2scm (true));
 
       /*
@@ -543,17 +675,17 @@ Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
        {
          if ((context_info & PES_UPPER) && (context_info & STACKED_HEAD))
            {
-             if (!String::compare (prev_glyph_name, "svaticana.punctum"))
+             if (prev_glyph_name == "vaticana.punctum")
                if (prev_delta_pitch > 1)
-                 prev_glyph_name = "svaticana.lpes";
+                 prev_glyph_name = "vaticana.lpes";
                else
-                 prev_glyph_name = "svaticana.vlpes";
+                 prev_glyph_name = "vaticana.vlpes";
            }
        }
 
       if (prev_primitive)
        prev_primitive->set_property ("glyph-name",
-                                     scm_makfrom0str (prev_glyph_name.to_str0 ()));
+                                     ly_string2scm (prev_glyph_name));
 
       /*
        * In the backend, flexa shapes and joins need to know about line
@@ -571,10 +703,13 @@ Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
     }
 
   prev_primitive->set_property ("glyph-name",
-                               scm_makfrom0str (prev_glyph_name.to_str0 ()));
+                               ly_string2scm (prev_glyph_name));
 
   align_heads (primitives, flexa_width, thickness);
 
+  // append all dots to paper column of ligature's last head
+  add_mora_column (prev_primitive->get_column ());
+
 #if 0 // experimental code to collapse spacing after ligature
   /* TODO: set to max (old/new spacing-increment), since other
      voices/staves also may want to set this property. */
@@ -591,8 +726,16 @@ Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
 ADD_ACKNOWLEDGER (Vaticana_ligature_engraver, rest);
 ADD_ACKNOWLEDGER (Vaticana_ligature_engraver, note_head);
 ADD_TRANSLATOR (Vaticana_ligature_engraver,
-               /* doc */ "Handles ligatures by glueing special ligature heads together.",
-               /* create */ "VaticanaLigature",
-               /* accept */ "ligature-event",
-               /* read */ "",
-               /* write */ "");
+               /* doc */
+               "Handle ligatures by glueing special ligature heads together.",
+
+               /* create */
+               "VaticanaLigature "
+               "DotColumn ",
+
+               /* read */
+               "",
+
+               /* write */
+               ""
+               );