]> git.donarmstrong.com Git - lilypond.git/blob - lily/lyric-engraver.cc
Fixes issue 786, "Extenders in lyrics stop prematurely if a single underscore is...
[lilypond.git] / lily / lyric-engraver.cc
1 /*
2   lyric-engraver.cc -- implement Lyric_engraver
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
7   Jan Nieuwenhuizen <janneke@gnu.org>
8 */
9
10 #include "context.hh"
11 #include "engraver.hh"
12 #include "item.hh"
13 #include "note-head.hh"
14 #include "stream-event.hh"
15 #include "international.hh"
16
17 #include "translator.icc"
18
19 /**
20    Generate texts for lyric syllables.  We only do one lyric at a time.
21    Multiple copies of this engraver should be used to do multiple voices.
22 */
23 class Lyric_engraver : public Engraver
24 {
25 protected:
26   void stop_translation_timestep ();
27   void process_music ();
28   DECLARE_TRANSLATOR_LISTENER (lyric);
29
30 public:
31   TRANSLATOR_DECLARATIONS (Lyric_engraver);
32
33 private:
34   Stream_event *event_;
35   Item *text_;
36   Item *last_text_;
37
38   Context *get_voice_context ();
39 };
40
41 Lyric_engraver::Lyric_engraver ()
42 {
43   text_ = 0;
44   last_text_ = 0;
45   event_ = 0;
46 }
47
48 IMPLEMENT_TRANSLATOR_LISTENER (Lyric_engraver, lyric);
49 void
50 Lyric_engraver::listen_lyric (Stream_event *ev)
51 {
52   ASSIGN_EVENT_ONCE (event_, ev);
53 }
54
55 void
56 Lyric_engraver::process_music ()
57 {
58   if (event_)
59     {
60       SCM text = event_->get_property ("text");
61
62       if (ly_is_equal (text, scm_from_locale_string (" ")))
63         {
64           if (last_text_)
65             last_text_->set_property ("self-alignment-X", scm_from_int (LEFT));
66         }
67       /*
68         "Empty" LyricText objects are needed to allow the Extender_engraver to
69         distinguish between the end of a lyrics block and manual melismata.
70       */
71       text_ = make_item ("LyricText", event_->self_scm ());
72     }
73 }
74
75 Context *
76 get_voice_to_lyrics (Context *lyrics)
77 {
78   SCM avc = lyrics->get_property ("associatedVoiceContext");
79   if (Context *c = unsmob_context (avc))
80     return c;
81
82   SCM voice_name = lyrics->get_property ("associatedVoice");
83   string nm = lyrics->id_string ();
84
85   if (scm_is_string (voice_name))
86     nm = ly_scm2string (voice_name);
87   else if (nm == "")
88     return 0;
89   else
90     {
91       ssize idx = nm.rfind ('-');
92       if (idx != NPOS)
93         nm = nm.substr (0, idx);
94     }
95
96   Context *parent = lyrics;
97   Context *voice = 0;
98   while (parent && !voice)
99     {
100       voice = find_context_below (parent, ly_symbol2scm ("Voice"), nm);
101       parent = parent->get_parent_context ();
102     }
103
104   if (voice)
105     return voice;
106
107   parent = lyrics;
108   voice = 0;
109   while (parent && !voice)
110     {
111       voice = find_context_below (parent, ly_symbol2scm ("Voice"), "");
112       parent = parent->get_parent_context ();
113     }
114
115   return voice;
116 }
117
118 Grob *
119 get_current_note_head (Context *voice)
120 {
121   Moment now = voice->now_mom ();
122   for (SCM s = voice->get_property ("busyGrobs");
123        scm_is_pair (s); s = scm_cdr (s))
124     {
125       Grob *g = unsmob_grob (scm_cdar (s));;
126       Moment *end_mom = unsmob_moment (scm_caar (s));
127       if (!end_mom || !g)
128         {
129           programming_error ("busyGrobs invalid");
130           continue;
131         }
132
133       if (end_mom->main_part_ > now.main_part_
134           && dynamic_cast<Item *> (g)
135           && Note_head::has_interface (g))
136         return g;
137     }
138
139   return 0;
140 }
141
142 void
143 Lyric_engraver::stop_translation_timestep ()
144 {
145   if (text_)
146     {
147       Context *voice = get_voice_to_lyrics (context ());
148
149       if (voice)
150         {
151           Grob *head = get_current_note_head (voice);
152
153           if (head)
154             {
155               text_->set_parent (head, X_AXIS);
156               if (melisma_busy (voice)
157                   && !to_boolean (get_property ("ignoreMelismata")))
158                 text_->set_property ("self-alignment-X",
159                                      get_property("lyricMelismaAlignment"));
160             }
161           else
162             {
163               text_->warning (_ ("Lyric syllable does not have note. Use \\lyricsto or associatedVoice."));
164               text_->set_property ("X-offset", scm_from_int (0));
165             }
166         }
167
168       last_text_ = text_;
169       text_ = 0;
170     }
171   event_ = 0;
172 }
173
174 ADD_TRANSLATOR (Lyric_engraver,
175                 /* doc */
176                 "Engrave text for lyrics.",
177
178                 /* create */
179                 "LyricText ",
180
181                 /* read */
182                 "ignoreMelismata "
183                 "lyricMelismaAlignment ",
184
185                 /* write */
186                 ""
187                 );