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"
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;
26 Syllable_group::clear()
33 group_translation_f_
= 0.0;
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_
;
50 Syllable_group::set_first_in_phrase(bool f
)
52 first_in_phrase_b_
= f
;
56 Syllable_group::set_notehead(Grob
* notehead
)
59 /* there should only be a single notehead, so silently ignore any extras */
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
;
76 longest_lyric_l_
= shortest_lyric_l_
= lyric
;
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.
88 // extender->set_grob_property("right-trim-amount", gh_double2scm(0.0));
92 extender
->set_grob_property("right-trim-amount",
93 gh_double2scm(-notehead_l_
->extent(notehead_l_
, X_AXIS
).length()/ss
));
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.
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).
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.
122 lyric
->set_parent(notehead_l_
, X_AXIS
);
123 lyric
->add_offset_callback (Side_position::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
134 If (length of longest lyric) < 2 * (length of shortest lyric),
135 - centre longest lyric on notehead
137 - move so shortest lyric just reaches notehead centre
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_
;
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.
161 Syllable_group::appropriate_alignment(const char *punc
)
163 if(first_in_phrase_b_
)
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
):"";
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
)) {
178 /* Special case: trailing space. Here examine the previous character and reverse the
179 sense of the test (i.e. trailing space makes a break without punctuation, or
180 suppresses a break with punctuation).
181 This behaviour can be suppressed by including a space in the
182 phrasingPunctuation property, in which case trailing space always means
183 the same as punctuation.
185 FIXME: The extra space throws alignment out a bit.
187 if(lastchar
== ' ') {
188 if(lyric_str
.length_i()>1) {
189 lastchar
= lyric_str
[lyric_str
.length_i()-2];
190 if(strchr(punc
, lastchar
))
205 /** We don't know about the melisma until after the initial alignment work is done, so go
206 back and fix the alignment when we DO know.
209 Syllable_group::adjust_melisma_align()
211 if(notehead_l_
&& lyric_list_
.size()) {
212 // override any previous offset adjustments
213 Real translation
= -group_translation_f_
;
215 switch (alignment_i_
) {
216 // case LEFT: // that's all
217 case CENTER
: // move right so smallest lyric is left-aligned on notehead
218 translation
+= (shortest_lyric_l_
->extent(shortest_lyric_l_
, X_AXIS
)).length()/2;
220 case RIGHT
: // move right so smallest lyric is left-aligned on notehead
221 translation
+= (shortest_lyric_l_
->extent(shortest_lyric_l_
, X_AXIS
)).length();
224 group_translation_f_
+= translation
;
225 for(int l
= 0; l
< lyric_list_
.size(); l
++) {
226 lyric_list_
[l
]->translate_axis (translation
, X_AXIS
);
233 Syllable_group::is_empty()
235 return lyric_list_
.size()==0;
239 Syllable_group::next_lyric()
241 first_in_phrase_b_
= (alignment_i_
== RIGHT
);
247 Syllable_group::mark_smob (SCM
)
253 Syllable_group::print_smob (SCM
, SCM port
, scm_print_state
* )
255 scm_puts ("#<Syllable_group>", port
);
259 IMPLEMENT_UNSMOB(Syllable_group
, voice_entry
);
260 IMPLEMENT_SIMPLE_SMOBS(Syllable_group
);
261 IMPLEMENT_DEFAULT_EQUAL_P(Syllable_group
);
264 Syllable_group::make_entry ()
266 Syllable_group
*vi
= new Syllable_group
;
267 return vi
->smobbed_self ();