]> git.donarmstrong.com Git - lilypond.git/blob - lily/syllable-group.cc
65aa936a374bb86281b6906c258e904d51d70c85
[lilypond.git] / lily / syllable-group.cc
1 #include <string.h>
2
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"
10
11
12 /*=========================================================================================*/
13
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.
16 */
17
18 Syllable_group::Syllable_group ()
19 {
20   first_in_phrase_b_=true;
21   melisma_b_ = false;
22   clear ();
23 }
24
25 void 
26 Syllable_group::clear ()
27 {
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;
34 }
35   
36 void
37 Syllable_group::copy (Syllable_group *from)
38 {
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_;
47 }
48
49 void 
50 Syllable_group::set_first_in_phrase (bool f) 
51
52   first_in_phrase_b_ = f; 
53 }
54
55 void 
56 Syllable_group::set_notehead (Grob * notehead)
57 {
58   if (!notehead_l_) {
59     /* there should only be a single notehead, so silently ignore any extras */
60     notehead_l_=notehead;
61   }
62 }
63
64 void 
65 Syllable_group::add_lyric (Grob * lyric)
66 {
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;
74   }
75   else
76     longest_lyric_l_ = shortest_lyric_l_ = lyric;
77 }
78
79 void 
80 Syllable_group::add_extender (Grob * extender)
81 {
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.
86
87     // Left:
88 //     extender->set_grob_property ("right-trim-amount", gh_double2scm (0.0));
89
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));
94   }
95 }
96
97 bool 
98 Syllable_group::set_lyric_align (const char *punc, Grob *default_notehead_l)
99 {
100   if (lyric_list_.size ()==0) {
101     // No lyrics: nothing to do.
102     return true;
103   }
104
105   Grob * lyric;
106   alignment_i_ = appropriate_alignment (punc);
107   
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;
112   }
113
114   group_translation_f_ = amount_to_translate ();
115
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);
126     }
127   }
128   return (notehead_l_);
129 }
130
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) < (property {begin,end}-alignment) * (length of shortest lyric),
135                  - centre longest lyric on notehead
136               Otherwise
137                  - move so shortest lyric just reaches notehead centre
138 */
139 Real 
140 Syllable_group::amount_to_translate ()
141 {
142   Real translate = 0.0;
143   if (alignment_i_ != CENTER) {
144     switch (alignment_i_) {
145       // FIXME: do we really know the lyric extent here? Some font sizing comes later?
146     case LEFT: 
147       translate =  longest_lyric_l_->extent (longest_lyric_l_, X_AXIS).length () / gh_scm2double (longest_lyric_l_->get_grob_property("begin-alignment"));
148       break;
149     case RIGHT: 
150       translate =   longest_lyric_l_->extent (longest_lyric_l_, X_AXIS).length () / gh_scm2double (longest_lyric_l_->get_grob_property("end-alignment"));
151       break;
152     }
153     if (!gh_scm2bool(longest_lyric_l_->get_grob_property("ignore-length-mismatch"))) {
154       Real l = shortest_lyric_l_->extent (shortest_lyric_l_, X_AXIS).length ();
155       translate = l <? translate;
156     }
157     
158     translate *= alignment_i_ ;
159   }
160   return translate;
161 }
162
163
164 /** determine what alignment we want.
165     Rules: if property alignment is set it specifies the alignment
166            if first_in_phrase_b_ is set, then alignment is LEFT.
167            otherwise if each syllable ends in punctuation, then alignment is RIGHT
168            otherwise alignment is centre.
169 */
170 int 
171 Syllable_group::appropriate_alignment (const char *punc)
172 {
173
174   SCM s=this->longest_lyric_l_->get_grob_property ("alignment");
175     if (s!=SCM_EOL) {
176       return gh_scm2int (s);
177     }
178
179   if (first_in_phrase_b_)
180     return LEFT;
181
182   Grob * lyric;
183   bool end_phrase = true;
184
185   for (int l = 0; l < lyric_list_.size () && end_phrase; l++) {
186     lyric = lyric_list_[l];
187     SCM lyric_scm = lyric->get_grob_property ("text");
188     String lyric_str = gh_string_p (lyric_scm)?ly_scm2string (lyric_scm):"";
189     char lastchar;
190     if (lyric_str.length_i ()>0) {
191       lastchar = lyric_str[lyric_str.length_i ()-1];
192       /* If it doesn't end in punctuation then it ain't an end of phrase */
193       if (! strchr (punc, lastchar)) {
194         /*
195           FIXME: Document this.
196           
197           Special case: trailing space. Here examine the previous character and reverse the
198            sense of the test (i.e. trailing space makes a break without punctuation, or 
199            suppresses a break with punctuation).
200            This behaviour can be suppressed by including a space in the 
201            phrasingPunctuation property, in which case trailing space always means 
202            the same as punctuation.
203
204            FIXME: The extra space throws alignment out a bit.
205         */
206         if (lastchar == ' ') {
207           if (lyric_str.length_i ()>1) {
208             lastchar = lyric_str[lyric_str.length_i ()-2];
209             if (strchr (punc, lastchar))
210               end_phrase=false;
211           }
212         }
213         else
214           end_phrase=false;
215       }
216     }
217   }
218   if (end_phrase)
219     return RIGHT;
220
221   return CENTER;
222 }
223
224 /** We don't know about the melisma until after the initial alignment work is done, so go
225     back and fix the alignment when we DO know.
226 */
227 void
228 Syllable_group::adjust_melisma_align ()
229 {
230   if (notehead_l_ && lyric_list_.size ()) {
231     // override any previous offset adjustments
232     Real translation = -group_translation_f_;
233     // melisma aligning:
234     switch (alignment_i_) {
235       //  case LEFT: // that's all
236     case CENTER: // move right so smallest lyric is left-aligned on notehead
237       translation += (shortest_lyric_l_->extent (shortest_lyric_l_, X_AXIS)).length ()/2;
238       break;
239     case RIGHT: // move right so smallest lyric is left-aligned on notehead
240       translation += (shortest_lyric_l_->extent (shortest_lyric_l_, X_AXIS)).length ();
241       break;
242     }
243     group_translation_f_ += translation;
244     for (int l = 0; l < lyric_list_.size (); l++) {
245       lyric_list_[l]->translate_axis (translation, X_AXIS);
246     }
247   }
248 }
249
250
251 bool
252 Syllable_group::is_empty ()
253 {
254   return lyric_list_.size ()==0;
255 }
256
257 void
258 Syllable_group::next_lyric ()
259 {
260   first_in_phrase_b_ = (alignment_i_ == RIGHT);
261   clear ();
262 }
263
264 /* SMOB */
265 SCM
266 Syllable_group::mark_smob (SCM)
267 {
268   return SCM_EOL;
269 }
270
271 int
272 Syllable_group::print_smob (SCM, SCM port, scm_print_state *)
273 {
274   scm_puts ("#<Syllable_group>", port);
275   return 1;
276 }
277
278
279 IMPLEMENT_SIMPLE_SMOBS (Syllable_group);
280 IMPLEMENT_DEFAULT_EQUAL_P (Syllable_group);
281
282 SCM
283 Syllable_group::make_entry ()
284 {
285   Syllable_group *vi = new Syllable_group;
286   return vi->smobbed_self ();
287 }