]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/syllable-group.cc
* VERSION (MY_PATCH_LEVEL): make 1.7.0
[lilypond.git] / lily / syllable-group.cc
index 66dc8830c4d92f68c6e7a7d1fb4f6ad1a6b21d40..e4bf7a8f52df09626ddc9cff55eee4e199941052 100644 (file)
 #include "paper-def.hh"
 
 
-/*=========================================================================================*/
-
-/** Syllable_group is a class to be smobbed and entered as data in the association list
-    member of the Lyric_phrasing_engraver class.
+/*
+  Syllable_group is a class to be smobbed and entered as data in the
+  association list member of the Lyric_phrasing_engraver class.
 */
 
-Syllable_group::Syllable_group()
+Syllable_group::Syllable_group ()
 {
   first_in_phrase_b_=true;
   melisma_b_ = false;
-  clear();
+  clear ();
 }
 
 void 
-Syllable_group::clear()
+Syllable_group::clear ()
 {
-  notehead_l_=0;
-  lyric_list_.clear();
-  longest_lyric_l_=0;
-  shortest_lyric_l_=0;
+  notehead_=0;
+  lyrics_.clear ();
+  longest_lyric_=0;
+  shortest_lyric_=0;
   melisma_b_ = false;
-  group_translation_f_ = 0.0;
+  group_translation_ = 0.0;
 }
   
 void
-Syllable_group::copySyllable_group *from)
+Syllable_group::copy (Syllable_group *from)
 {
-  notehead_l_ = from->notehead_l_;
-  lyric_list_ = from->lyric_list_;
-  longest_lyric_l_ = from->longest_lyric_l_;
-  shortest_lyric_l_ = from->shortest_lyric_l_;
+  notehead_ = from->notehead_;
+  lyrics_ = from->lyrics_;
+  longest_lyric_ = from->longest_lyric_;
+  shortest_lyric_ = from->shortest_lyric_;
   melisma_b_ = from->melisma_b_;
-  alignment_i_ = from->alignment_i_;
+  alignment_ = from->alignment_;
   first_in_phrase_b_ = from->first_in_phrase_b_;
-  group_translation_f_ = from->group_translation_f_;
+  group_translation_ = from->group_translation_;
 }
 
 void 
-Syllable_group::set_first_in_phrase(bool f) 
+Syllable_group::set_first_in_phrase (bool f) 
 { 
   first_in_phrase_b_ = f; 
 }
 
 void 
-Syllable_group::set_notehead(Score_element * notehead)
+Syllable_group::set_notehead (Grob * notehead)
 {
-  if(!notehead_l_) {
-    /* there should only be a single notehead, so silently ignore any extras */
-    notehead_l_=notehead;
-  }
+  if (!notehead_)
+    {
+      /* there should only be a single notehead, so silently ignore
+        any extras */
+      notehead_=notehead;
+    }
 }
 
 void 
-Syllable_group::add_lyric(Score_element * lyric)
+Syllable_group::add_lyric (Grob * lyric)
 {
-  lyric_list_.push(lyric);
+  lyrics_.push (lyric);
   /* record longest and shortest lyrics */
-  if( longest_lyric_l_ ) {
-    if(lyric->extent(lyric,X_AXIS).length() > (longest_lyric_l_->extent(longest_lyric_l_, X_AXIS)).length())
-      longest_lyric_l_ = lyric;
-    if(lyric->extent(lyric, X_AXIS).length() < (shortest_lyric_l_->extent(shortest_lyric_l_, X_AXIS)).length())
-      shortest_lyric_l_ = lyric;
-  }
+  if (longest_lyric_)
+    {
+      if (lyric->extent (lyric,X_AXIS).length () > (longest_lyric_->extent (longest_lyric_, X_AXIS)).length ())
+       longest_lyric_ = lyric;
+      if (lyric->extent (lyric, X_AXIS).length () < (shortest_lyric_->extent (shortest_lyric_, X_AXIS)).length ())
+       shortest_lyric_ = lyric;
+    }
   else
-    longest_lyric_l_ = shortest_lyric_l_ = lyric;
+    longest_lyric_ = shortest_lyric_ = lyric;
 }
 
 void 
-Syllable_group::add_extender(Score_element * extender)
+Syllable_group::add_extender (Grob * extender)
 {
-  if(notehead_l_ && melisma_b_) {
-    dynamic_cast<Spanner*>(extender)->set_bound (RIGHT, notehead_l_);
-    // should the extender finish at the right of the last note of the melisma, or the left?
-    // Comments in lyric-extender.hh say left, but right looks better to me. GP.
-
-    // Left:
-//     extender->set_elt_property("right-trim-amount", gh_double2scm(0.0));
-
-    // Right:
-    Real ss = extender->paper_l ()->get_var ("staffspace");
-    extender->set_elt_property("right-trim-amount", 
-                              gh_double2scm(-notehead_l_->extent(notehead_l_, X_AXIS).length()/ss));
-  }
+  if (notehead_ && melisma_b_)
+    {
+      dynamic_cast<Spanner*> (extender)->set_bound (RIGHT, notehead_);
+      // should the extender finish at the right of the last note of the melisma, or the left?
+      // Comments in lyric-extender.hh say left, but right looks better to me. GP.
+
+      // Left:
+      //     extender->set_grob_property ("right-trim-amount", gh_double2scm (0.0));
+
+      // Right:
+      Real ss = 1.0;
+      extender->set_grob_property ("right-trim-amount", 
+                                  gh_double2scm (-notehead_->extent (notehead_, X_AXIS).length ()/ss));
+    }
 }
 
 bool 
-Syllable_group::set_lyric_align(const char *punc, Score_element *default_notehead_l)
+Syllable_group::set_lyric_align (const char *punc, Grob *default_notehead)
 {
-  if(lyric_list_.size()==0) {
-    // No lyrics: nothing to do.
-    return true;
-  }
-
-  Score_element * lyric;
-  alignment_i_ = appropriate_alignment(punc);
-
-  // If there was no notehead in the matching voice context, use the first 
-  // notehead caught from any voice context (any port in a storm).
-  if(!notehead_l_) {
-    notehead_l_ = default_notehead_l;
-  }
+  if (lyrics_.size () <= 0)
+    {
+      // No lyrics: nothing to do.
+      return true;
+    }
+  
+  Grob * lyric;
+  alignment_ = appropriate_alignment (punc);
+  
+  /* If there was no notehead in the matching voice context, use the
+   first notehead caught from any voice context (any port in a storm).
+
+
+   Is this wise? Can't the lyric simply be set on a the paper-column,
+   and be done with it. That's just as correct, and won't give strange
+   results if the port-in-the-storms happesn to be involved in a
+   note-collision? --hwn.
+  */
+  if (!notehead_)
+    {
+      notehead_ = default_notehead;
+    }
 
-  group_translation_f_ = amount_to_translate();
+  group_translation_ = amount_to_translate ();
 
   // set the x alignment of each lyric
-  for(int l = 0; l < lyric_list_.size(); l++) {
-    lyric = lyric_list_[l];
-    lyric->set_elt_property("self-alignment-X", gh_int2scm(alignment_i_));
-    // centre on notehead ... if we have one. 
-    if(notehead_l_) {
-      lyric->set_parent(notehead_l_, X_AXIS);
-      lyric->add_offset_callback (Side_position::centered_on_parent_proc, X_AXIS);
-      // reference is on the right of the notehead; move it left half way, and add translation
-      lyric->translate_axis (group_translation_f_-(notehead_l_->extent(notehead_l_, X_AXIS)).center(), X_AXIS);
+  for (int l = 0; l < lyrics_.size (); l++)
+    {
+      lyric = lyrics_[l];
+      lyric->set_grob_property ("self-alignment-X", gh_int2scm (alignment_));
+      if (notehead_)
+       {
+         /*
+           Centering on parent is done by default (see
+           grob-description.scm); we only have to set the parent.
+         */
+         lyric->set_parent (notehead_, X_AXIS);
+         lyric->translate_axis (group_translation_, X_AXIS);
+       }
     }
-  }
-  return (notehead_l_);
+  return (notehead_);
 }
 
 /** determine the distance to translate lyrics to get correct alignment
     Rules: If alignment is centre, translate = 0
-           Otherwise,
-             If (length of longest lyric) < 2 * (length of shortest lyric),
-                - centre longest lyric on notehead
-             Otherwise
-                - move so shortest lyric just reaches notehead centre
+    Otherwise,
+    If (length of longest lyric) < (property {begin,end}-alignment) * (length of shortest lyric),
+    - centre longest lyric on notehead
+    Otherwise
+    - move so shortest lyric just reaches notehead centre
 */
 Real 
-Syllable_group::amount_to_translate()
+Syllable_group::amount_to_translate ()
 {
   Real translate = 0.0;
-  if(alignment_i_ != CENTER) {
-    // FIXME: do we really know the lyric extent here? Some font sizing comes later?
-    Real l1 = longest_lyric_l_->extent(longest_lyric_l_, X_AXIS).length() / 2;
-    Real l2 = shortest_lyric_l_->extent(shortest_lyric_l_, X_AXIS).length();
-
-    translate = l1 <? l2;
-    translate *= alignment_i_ ;
-  }
+  if (alignment_ != CENTER)
+    {
+      switch (alignment_)
+       {
+         // FIXME: do we really know the lyric extent here? Some font sizing comes later?
+       case LEFT: 
+         translate =  longest_lyric_->extent (longest_lyric_, X_AXIS).length () / gh_scm2double (longest_lyric_->get_grob_property("begin-alignment"));
+         break;
+       case RIGHT: 
+         translate =   longest_lyric_->extent (longest_lyric_, X_AXIS).length () / gh_scm2double (longest_lyric_->get_grob_property("end-alignment"));
+         break;
+       }
+      if (!gh_scm2bool(longest_lyric_->get_grob_property("ignore-length-mismatch")))
+       {
+         Real l = shortest_lyric_->extent (shortest_lyric_, X_AXIS).length ();
+         translate = l <? translate;
+       }
+    
+      translate *= alignment_ ;
+    }
   return translate;
 }
 
 
 /** determine what alignment we want.
-    Rules: if first_in_phrase_b_ is set, then alignment is LEFT.
-           otherwise if each syllable ends in punctuation, then alignment is RIGHT
-          otherwise alignment is centre.
+    Rules: if property alignment is set it specifies the alignment
+    if first_in_phrase_b_ is set, then alignment is LEFT.
+    otherwise if each syllable ends in punctuation, then alignment is RIGHT
+    otherwise alignment is centre.
 */
 int 
-Syllable_group::appropriate_alignment(const char *punc)
+Syllable_group::appropriate_alignment (const char *punc)
 {
-  if(first_in_phrase_b_)
+  SCM s=this->longest_lyric_->get_grob_property ("alignment");
+  if (s!=SCM_EOL)
+    {
+      return gh_scm2int (s);
+    }
+
+  if (first_in_phrase_b_)
     return LEFT;
 
-  Score_element * lyric;
+  Grob * lyric;
   bool end_phrase = true;
 
-  for(int l = 0; l < lyric_list_.size() && end_phrase; l++) {
-    lyric = lyric_list_[l];
-    SCM lyric_scm = lyric->get_elt_property("text");
-    String lyric_str = gh_string_p(lyric_scm)?ly_scm2string(lyric_scm):"";
-    char lastchar;
-    if(lyric_str.length_i()>0) {
-      lastchar = lyric_str[lyric_str.length_i()-1];
-      /* If it doesn't end in punctuation then it ain't an end of phrase */
-      if(! strchr(punc, lastchar)) {
-       /* Special case: trailing space. Here examine the previous character and reverse the
-          sense of the test (i.e. trailing space makes a break without punctuation, or 
-          suppresses a break with punctuation).
-          This behaviour can be suppressed by including a space in the 
-          phrasingPunctuation property, in which case trailing space always means 
-          the same as punctuation.
-
-          FIXME: The extra space throws alignment out a bit.
-       */
-       if(lastchar == ' ') {
-         if(lyric_str.length_i()>1) {
-           lastchar = lyric_str[lyric_str.length_i()-2];
-           if(strchr(punc, lastchar))
-             end_phrase=false;
-         }
+  for (int l = 0; l < lyrics_.size () && end_phrase; l++)
+    {
+      lyric = lyrics_[l];
+      SCM lyric_scm = lyric->get_grob_property ("text");
+      String lyric_string = gh_string_p (lyric_scm)?ly_scm2string (lyric_scm):"";
+      char lastchar;
+      if (lyric_string.length ()>0)
+       {
+         lastchar = lyric_string[lyric_string.length ()-1];
+         /* If it doesn't end in punctuation then it ain't an end of phrase */
+         if (! strchr (punc, lastchar))
+           {
+             /*
+               FIXME: Document this.
+         
+               Special case: trailing space. Here examine the
+               previous character and reverse the sense of the test
+               (i.e. trailing space makes a break without
+               punctuation, or suppresses a break with punctuation).
+               This behaviour can be suppressed by including a space
+               in the phrasingPunctuation property, in which case
+               trailing space always means the same as punctuation.
+
+               FIXME: The extra space throws alignment out a bit.
+             */
+             if (lastchar == ' ')
+               {
+                 if (lyric_string.length ()>1)
+                   {
+                     lastchar = lyric_string[lyric_string.length ()-2];
+                     if (strchr (punc, lastchar))
+                       end_phrase=false;
+                   }
+               }
+             else
+               end_phrase=false;
+           }
        }
-       else
-         end_phrase=false;
-      }
     }
-  }
-  if(end_phrase)
+  if (end_phrase)
     return RIGHT;
 
   return CENTER;
@@ -206,40 +249,43 @@ Syllable_group::appropriate_alignment(const char *punc)
     back and fix the alignment when we DO know.
 */
 void
-Syllable_group::adjust_melisma_align()
+Syllable_group::adjust_melisma_align ()
 {
-  if(notehead_l_ && lyric_list_.size()) {
-    // override any previous offset adjustments
-    Real translation = -group_translation_f_;
-    // melisma aligning:
-    switch (alignment_i_) {
-      //  case LEFT: // that's all
-    case CENTER: // move right so smallest lyric is left-aligned on notehead
-      translation += (shortest_lyric_l_->extent(shortest_lyric_l_, X_AXIS)).length()/2;
-      break;
-    case RIGHT: // move right so smallest lyric is left-aligned on notehead
-      translation += (shortest_lyric_l_->extent(shortest_lyric_l_, X_AXIS)).length();
-      break;
-    }
-    group_translation_f_ += translation;
-    for(int l = 0; l < lyric_list_.size(); l++) {
-      lyric_list_[l]->translate_axis (translation, X_AXIS);
+  if (notehead_ && lyrics_.size ())
+    {
+      // override any previous offset adjustments
+      Real translation = -group_translation_;
+      // melisma aligning:
+      switch (alignment_)
+       {
+         //  case LEFT: // that's all
+       case CENTER: // move right so smallest lyric is left-aligned on notehead
+         translation += (shortest_lyric_->extent (shortest_lyric_, X_AXIS)).length ()/2;
+         break;
+       case RIGHT: // move right so smallest lyric is left-aligned on notehead
+         translation += (shortest_lyric_->extent (shortest_lyric_, X_AXIS)).length ();
+         break;
+       }
+      group_translation_ += translation;
+      for (int l = 0; l < lyrics_.size (); l++)
+       {
+         lyrics_[l]->translate_axis (translation, X_AXIS);
+       }
     }
-  }
 }
 
 
 bool
-Syllable_group::is_empty()
+Syllable_group::is_empty ()
 {
-  return lyric_list_.size()==0;
+  return lyrics_.size ()==0;
 }
 
 void
-Syllable_group::next_lyric()
+Syllable_group::next_lyric ()
 {
-  first_in_phrase_b_ = (alignment_i_ == RIGHT);
-  clear();
+  first_in_phrase_b_ = (alignment_ == RIGHT);
+  clear ();
 }
 
 /* SMOB */
@@ -250,15 +296,15 @@ Syllable_group::mark_smob (SCM)
 }
 
 int
-Syllable_group::print_smob (SCM, SCM port, scm_print_state * )
+Syllable_group::print_smob (SCM, SCM port, scm_print_state *)
 {
   scm_puts ("#<Syllable_group>", port);
   return 1;
 }
 
-IMPLEMENT_UNSMOB(Syllable_group, voice_entry);
-IMPLEMENT_SIMPLE_SMOBS(Syllable_group);
-IMPLEMENT_DEFAULT_EQUAL_P(Syllable_group);
+
+IMPLEMENT_SIMPLE_SMOBS (Syllable_group);
+IMPLEMENT_DEFAULT_EQUAL_P (Syllable_group);
 
 SCM
 Syllable_group::make_entry ()
@@ -266,3 +312,12 @@ Syllable_group::make_entry ()
   Syllable_group *vi = new Syllable_group;
   return vi->smobbed_self ();
 }
+
+struct Lyric_syllable
+{
+  static bool has_interface (Grob*);
+};
+ADD_INTERFACE (Lyric_syllable,"lyric-syllable-interface",
+              "a single piece of lyrics",
+              "word-space alignment ignore-length-mismatch begin-alignment end-alignment");
+