lilypond-1.5.9
[lilypond.git] / lily / syllable-group.cc
blob119acd03df36e3dfe6de5a799d5a626d8f6105e9
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"
12 /*=========================================================================================*/
14 /** Syllable_group is a class to be smobbed and entered as data in the association list
15 member of the Lyric_phrasing_engraver class.
18 Syllable_group::Syllable_group ()
20 first_in_phrase_b_=true;
21 melisma_b_ = false;
22 clear ();
25 void
26 Syllable_group::clear ()
28 notehead_l_=0;
29 lyric_list_.clear ();
30 longest_lyric_l_=0;
31 shortest_lyric_l_=0;
32 melisma_b_ = false;
33 group_translation_f_ = 0.0;
36 void
37 Syllable_group::copy (Syllable_group *from)
39 notehead_l_ = from->notehead_l_;
40 lyric_list_ = from->lyric_list_;
41 longest_lyric_l_ = from->longest_lyric_l_;
42 shortest_lyric_l_ = from->shortest_lyric_l_;
43 melisma_b_ = from->melisma_b_;
44 alignment_i_ = from->alignment_i_;
45 first_in_phrase_b_ = from->first_in_phrase_b_;
46 group_translation_f_ = from->group_translation_f_;
49 void
50 Syllable_group::set_first_in_phrase (bool f)
52 first_in_phrase_b_ = f;
55 void
56 Syllable_group::set_notehead (Grob * notehead)
58 if (!notehead_l_) {
59 /* there should only be a single notehead, so silently ignore any extras */
60 notehead_l_=notehead;
64 void
65 Syllable_group::add_lyric (Grob * lyric)
67 lyric_list_.push (lyric);
68 /* record longest and shortest lyrics */
69 if (longest_lyric_l_) {
70 if (lyric->extent (lyric,X_AXIS).length () > (longest_lyric_l_->extent (longest_lyric_l_, X_AXIS)).length ())
71 longest_lyric_l_ = lyric;
72 if (lyric->extent (lyric, X_AXIS).length () < (shortest_lyric_l_->extent (shortest_lyric_l_, X_AXIS)).length ())
73 shortest_lyric_l_ = lyric;
75 else
76 longest_lyric_l_ = shortest_lyric_l_ = lyric;
79 void
80 Syllable_group::add_extender (Grob * extender)
82 if (notehead_l_ && melisma_b_) {
83 dynamic_cast<Spanner*> (extender)->set_bound (RIGHT, notehead_l_);
84 // should the extender finish at the right of the last note of the melisma, or the left?
85 // Comments in lyric-extender.hh say left, but right looks better to me. GP.
87 // Left:
88 // extender->set_grob_property ("right-trim-amount", gh_double2scm (0.0));
90 // Right:
91 Real ss = 1.0;
92 extender->set_grob_property ("right-trim-amount",
93 gh_double2scm (-notehead_l_->extent (notehead_l_, X_AXIS).length ()/ss));
97 bool
98 Syllable_group::set_lyric_align (const char *punc, Grob *default_notehead_l)
100 if (lyric_list_.size ()==0) {
101 // No lyrics: nothing to do.
102 return true;
105 Grob * lyric;
106 alignment_i_ = appropriate_alignment (punc);
108 // If there was no notehead in the matching voice context, use the first
109 // notehead caught from any voice context (any port in a storm).
110 if (!notehead_l_) {
111 notehead_l_ = default_notehead_l;
114 group_translation_f_ = amount_to_translate ();
116 // set the x alignment of each lyric
117 for (int l = 0; l < lyric_list_.size (); l++) {
118 lyric = lyric_list_[l];
119 lyric->set_grob_property ("self-alignment-X", gh_int2scm (alignment_i_));
120 // centre on notehead ... if we have one.
121 if (notehead_l_) {
122 lyric->set_parent (notehead_l_, X_AXIS);
123 lyric->add_offset_callback (Side_position_interface::centered_on_parent_proc, X_AXIS);
124 // reference is on the right of the notehead; move it left half way, and add translation
125 lyric->translate_axis (group_translation_f_- (notehead_l_->extent (notehead_l_, X_AXIS)).center (), X_AXIS);
128 return (notehead_l_);
131 /** determine the distance to translate lyrics to get correct alignment
132 Rules: If alignment is centre, translate = 0
133 Otherwise,
134 If (length of longest lyric) < 2 * (length of shortest lyric),
135 - centre longest lyric on notehead
136 Otherwise
137 - move so shortest lyric just reaches notehead centre
139 Real
140 Syllable_group::amount_to_translate ()
142 Real translate = 0.0;
143 if (alignment_i_ != CENTER) {
144 // FIXME: do we really know the lyric extent here? Some font sizing comes later?
145 Real l1 = longest_lyric_l_->extent (longest_lyric_l_, X_AXIS).length () / 2;
146 Real l2 = shortest_lyric_l_->extent (shortest_lyric_l_, X_AXIS).length ();
148 translate = l1 <? l2;
149 translate *= alignment_i_ ;
151 return translate;
155 /** determine what alignment we want.
156 Rules: if first_in_phrase_b_ is set, then alignment is LEFT.
157 otherwise if each syllable ends in punctuation, then alignment is RIGHT
158 otherwise alignment is centre.
160 int
161 Syllable_group::appropriate_alignment (const char *punc)
163 if (first_in_phrase_b_)
164 return LEFT;
166 Grob * lyric;
167 bool end_phrase = true;
169 for (int l = 0; l < lyric_list_.size () && end_phrase; l++) {
170 lyric = lyric_list_[l];
171 SCM lyric_scm = lyric->get_grob_property ("text");
172 String lyric_str = gh_string_p (lyric_scm)?ly_scm2string (lyric_scm):"";
173 char lastchar;
174 if (lyric_str.length_i ()>0) {
175 lastchar = lyric_str[lyric_str.length_i ()-1];
176 /* If it doesn't end in punctuation then it ain't an end of phrase */
177 if (! strchr (punc, lastchar)) {
179 FIXME: Document this.
181 Special case: trailing space. Here examine the previous character and reverse the
182 sense of the test (i.e. trailing space makes a break without punctuation, or
183 suppresses a break with punctuation).
184 This behaviour can be suppressed by including a space in the
185 phrasingPunctuation property, in which case trailing space always means
186 the same as punctuation.
188 FIXME: The extra space throws alignment out a bit.
190 if (lastchar == ' ') {
191 if (lyric_str.length_i ()>1) {
192 lastchar = lyric_str[lyric_str.length_i ()-2];
193 if (strchr (punc, lastchar))
194 end_phrase=false;
197 else
198 end_phrase=false;
202 if (end_phrase)
203 return RIGHT;
205 return CENTER;
208 /** We don't know about the melisma until after the initial alignment work is done, so go
209 back and fix the alignment when we DO know.
211 void
212 Syllable_group::adjust_melisma_align ()
214 if (notehead_l_ && lyric_list_.size ()) {
215 // override any previous offset adjustments
216 Real translation = -group_translation_f_;
217 // melisma aligning:
218 switch (alignment_i_) {
219 // case LEFT: // that's all
220 case CENTER: // move right so smallest lyric is left-aligned on notehead
221 translation += (shortest_lyric_l_->extent (shortest_lyric_l_, X_AXIS)).length ()/2;
222 break;
223 case RIGHT: // move right so smallest lyric is left-aligned on notehead
224 translation += (shortest_lyric_l_->extent (shortest_lyric_l_, X_AXIS)).length ();
225 break;
227 group_translation_f_ += translation;
228 for (int l = 0; l < lyric_list_.size (); l++) {
229 lyric_list_[l]->translate_axis (translation, X_AXIS);
235 bool
236 Syllable_group::is_empty ()
238 return lyric_list_.size ()==0;
241 void
242 Syllable_group::next_lyric ()
244 first_in_phrase_b_ = (alignment_i_ == RIGHT);
245 clear ();
248 /* SMOB */
250 Syllable_group::mark_smob (SCM)
252 return SCM_EOL;
256 Syllable_group::print_smob (SCM, SCM port, scm_print_state *)
258 scm_puts ("#<Syllable_group>", port);
259 return 1;
262 IMPLEMENT_UNSMOB (Syllable_group, voice_entry);
263 IMPLEMENT_SIMPLE_SMOBS (Syllable_group);
264 IMPLEMENT_DEFAULT_EQUAL_P (Syllable_group);
267 Syllable_group::make_entry ()
269 Syllable_group *vi = new Syllable_group;
270 return vi->smobbed_self ();