*** empty log message ***
[lilypond.git] / lily / syllable-group.cc
blobe4bf7a8f52df09626ddc9cff55eee4e199941052
1 #include <string.h>
3 #include "lyric-phrasing-engraver.hh"
4 #include "note-head.hh"
5 #include "translator-group.hh"
6 #include "side-position-interface.hh"
7 #include "ly-smobs.icc"
8 #include "spanner.hh"
9 #include "paper-def.hh"
13 Syllable_group is a class to be smobbed and entered as data in the
14 association list member of the Lyric_phrasing_engraver class.
17 Syllable_group::Syllable_group ()
19 first_in_phrase_b_=true;
20 melisma_b_ = false;
21 clear ();
24 void
25 Syllable_group::clear ()
27 notehead_=0;
28 lyrics_.clear ();
29 longest_lyric_=0;
30 shortest_lyric_=0;
31 melisma_b_ = false;
32 group_translation_ = 0.0;
35 void
36 Syllable_group::copy (Syllable_group *from)
38 notehead_ = from->notehead_;
39 lyrics_ = from->lyrics_;
40 longest_lyric_ = from->longest_lyric_;
41 shortest_lyric_ = from->shortest_lyric_;
42 melisma_b_ = from->melisma_b_;
43 alignment_ = from->alignment_;
44 first_in_phrase_b_ = from->first_in_phrase_b_;
45 group_translation_ = from->group_translation_;
48 void
49 Syllable_group::set_first_in_phrase (bool f)
51 first_in_phrase_b_ = f;
54 void
55 Syllable_group::set_notehead (Grob * notehead)
57 if (!notehead_)
59 /* there should only be a single notehead, so silently ignore
60 any extras */
61 notehead_=notehead;
65 void
66 Syllable_group::add_lyric (Grob * lyric)
68 lyrics_.push (lyric);
69 /* record longest and shortest lyrics */
70 if (longest_lyric_)
72 if (lyric->extent (lyric,X_AXIS).length () > (longest_lyric_->extent (longest_lyric_, X_AXIS)).length ())
73 longest_lyric_ = lyric;
74 if (lyric->extent (lyric, X_AXIS).length () < (shortest_lyric_->extent (shortest_lyric_, X_AXIS)).length ())
75 shortest_lyric_ = lyric;
77 else
78 longest_lyric_ = shortest_lyric_ = lyric;
81 void
82 Syllable_group::add_extender (Grob * extender)
84 if (notehead_ && melisma_b_)
86 dynamic_cast<Spanner*> (extender)->set_bound (RIGHT, notehead_);
87 // should the extender finish at the right of the last note of the melisma, or the left?
88 // Comments in lyric-extender.hh say left, but right looks better to me. GP.
90 // Left:
91 // extender->set_grob_property ("right-trim-amount", gh_double2scm (0.0));
93 // Right:
94 Real ss = 1.0;
95 extender->set_grob_property ("right-trim-amount",
96 gh_double2scm (-notehead_->extent (notehead_, X_AXIS).length ()/ss));
100 bool
101 Syllable_group::set_lyric_align (const char *punc, Grob *default_notehead)
103 if (lyrics_.size () <= 0)
105 // No lyrics: nothing to do.
106 return true;
109 Grob * lyric;
110 alignment_ = appropriate_alignment (punc);
112 /* If there was no notehead in the matching voice context, use the
113 first notehead caught from any voice context (any port in a storm).
116 Is this wise? Can't the lyric simply be set on a the paper-column,
117 and be done with it. That's just as correct, and won't give strange
118 results if the port-in-the-storms happesn to be involved in a
119 note-collision? --hwn.
121 if (!notehead_)
123 notehead_ = default_notehead;
126 group_translation_ = amount_to_translate ();
128 // set the x alignment of each lyric
129 for (int l = 0; l < lyrics_.size (); l++)
131 lyric = lyrics_[l];
132 lyric->set_grob_property ("self-alignment-X", gh_int2scm (alignment_));
133 if (notehead_)
136 Centering on parent is done by default (see
137 grob-description.scm); we only have to set the parent.
139 lyric->set_parent (notehead_, X_AXIS);
140 lyric->translate_axis (group_translation_, X_AXIS);
143 return (notehead_);
146 /** determine the distance to translate lyrics to get correct alignment
147 Rules: If alignment is centre, translate = 0
148 Otherwise,
149 If (length of longest lyric) < (property {begin,end}-alignment) * (length of shortest lyric),
150 - centre longest lyric on notehead
151 Otherwise
152 - move so shortest lyric just reaches notehead centre
154 Real
155 Syllable_group::amount_to_translate ()
157 Real translate = 0.0;
158 if (alignment_ != CENTER)
160 switch (alignment_)
162 // FIXME: do we really know the lyric extent here? Some font sizing comes later?
163 case LEFT:
164 translate = longest_lyric_->extent (longest_lyric_, X_AXIS).length () / gh_scm2double (longest_lyric_->get_grob_property("begin-alignment"));
165 break;
166 case RIGHT:
167 translate = longest_lyric_->extent (longest_lyric_, X_AXIS).length () / gh_scm2double (longest_lyric_->get_grob_property("end-alignment"));
168 break;
170 if (!gh_scm2bool(longest_lyric_->get_grob_property("ignore-length-mismatch")))
172 Real l = shortest_lyric_->extent (shortest_lyric_, X_AXIS).length ();
173 translate = l <? translate;
176 translate *= alignment_ ;
178 return translate;
182 /** determine what alignment we want.
183 Rules: if property alignment is set it specifies the alignment
184 if first_in_phrase_b_ is set, then alignment is LEFT.
185 otherwise if each syllable ends in punctuation, then alignment is RIGHT
186 otherwise alignment is centre.
188 int
189 Syllable_group::appropriate_alignment (const char *punc)
191 SCM s=this->longest_lyric_->get_grob_property ("alignment");
192 if (s!=SCM_EOL)
194 return gh_scm2int (s);
197 if (first_in_phrase_b_)
198 return LEFT;
200 Grob * lyric;
201 bool end_phrase = true;
203 for (int l = 0; l < lyrics_.size () && end_phrase; l++)
205 lyric = lyrics_[l];
206 SCM lyric_scm = lyric->get_grob_property ("text");
207 String lyric_string = gh_string_p (lyric_scm)?ly_scm2string (lyric_scm):"";
208 char lastchar;
209 if (lyric_string.length ()>0)
211 lastchar = lyric_string[lyric_string.length ()-1];
212 /* If it doesn't end in punctuation then it ain't an end of phrase */
213 if (! strchr (punc, lastchar))
216 FIXME: Document this.
218 Special case: trailing space. Here examine the
219 previous character and reverse the sense of the test
220 (i.e. trailing space makes a break without
221 punctuation, or suppresses a break with punctuation).
222 This behaviour can be suppressed by including a space
223 in the phrasingPunctuation property, in which case
224 trailing space always means the same as punctuation.
226 FIXME: The extra space throws alignment out a bit.
228 if (lastchar == ' ')
230 if (lyric_string.length ()>1)
232 lastchar = lyric_string[lyric_string.length ()-2];
233 if (strchr (punc, lastchar))
234 end_phrase=false;
237 else
238 end_phrase=false;
242 if (end_phrase)
243 return RIGHT;
245 return CENTER;
248 /** We don't know about the melisma until after the initial alignment work is done, so go
249 back and fix the alignment when we DO know.
251 void
252 Syllable_group::adjust_melisma_align ()
254 if (notehead_ && lyrics_.size ())
256 // override any previous offset adjustments
257 Real translation = -group_translation_;
258 // melisma aligning:
259 switch (alignment_)
261 // case LEFT: // that's all
262 case CENTER: // move right so smallest lyric is left-aligned on notehead
263 translation += (shortest_lyric_->extent (shortest_lyric_, X_AXIS)).length ()/2;
264 break;
265 case RIGHT: // move right so smallest lyric is left-aligned on notehead
266 translation += (shortest_lyric_->extent (shortest_lyric_, X_AXIS)).length ();
267 break;
269 group_translation_ += translation;
270 for (int l = 0; l < lyrics_.size (); l++)
272 lyrics_[l]->translate_axis (translation, X_AXIS);
278 bool
279 Syllable_group::is_empty ()
281 return lyrics_.size ()==0;
284 void
285 Syllable_group::next_lyric ()
287 first_in_phrase_b_ = (alignment_ == RIGHT);
288 clear ();
291 /* SMOB */
293 Syllable_group::mark_smob (SCM)
295 return SCM_EOL;
299 Syllable_group::print_smob (SCM, SCM port, scm_print_state *)
301 scm_puts ("#<Syllable_group>", port);
302 return 1;
306 IMPLEMENT_SIMPLE_SMOBS (Syllable_group);
307 IMPLEMENT_DEFAULT_EQUAL_P (Syllable_group);
310 Syllable_group::make_entry ()
312 Syllable_group *vi = new Syllable_group;
313 return vi->smobbed_self ();
316 struct Lyric_syllable
318 static bool has_interface (Grob*);
320 ADD_INTERFACE (Lyric_syllable,"lyric-syllable-interface",
321 "a single piece of lyrics",
322 "word-space alignment ignore-length-mismatch begin-alignment end-alignment");