]> git.donarmstrong.com Git - lilypond.git/blob - lily/extender-engraver.cc
a46489be399ccf6f710a2e68b4e8652478894996
[lilypond.git] / lily / extender-engraver.cc
1 /*
2   extender-engraver.cc -- implement Extender_engraver
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1999--2009 Glen Prideaux <glenprideaux@iname.com>,
7   Han-Wen Nienhuys <hanwen@xs4all.nl>,
8   Jan Nieuwenhuizen <janneke@gnu.org>
9 */
10 #include "context.hh"
11 #include "engraver.hh"
12 #include "international.hh"
13 #include "item.hh"
14 #include "lyric-extender.hh"
15 #include "note-head.hh"
16 #include "pointer-group-interface.hh"
17 #include "stream-event.hh"
18 #include "warn.hh"
19 #include "spanner.hh"
20 #include "paper-column.hh"
21
22 #include "translator.icc"
23
24 void completize_extender (Spanner *sp);
25
26 class Extender_engraver : public Engraver
27 {
28   Stream_event *ev_;
29   Spanner *extender_;
30   Spanner *pending_extender_;
31   bool current_lyric_is_skip_;
32
33 public:
34   TRANSLATOR_DECLARATIONS (Extender_engraver);
35
36 protected:
37   DECLARE_TRANSLATOR_LISTENER (extender);
38   DECLARE_ACKNOWLEDGER (lyric_syllable);
39
40   virtual void finalize ();
41
42   void stop_translation_timestep ();
43   void process_music ();
44 };
45
46 Extender_engraver::Extender_engraver ()
47 {
48   current_lyric_is_skip_ = false;
49   extender_ = 0;
50   pending_extender_ = 0;
51   ev_ = 0;
52 }
53
54 IMPLEMENT_TRANSLATOR_LISTENER (Extender_engraver, extender);
55 void
56 Extender_engraver::listen_extender (Stream_event *ev)
57 {
58   ASSIGN_EVENT_ONCE (ev_, ev);
59 }
60
61 void
62 Extender_engraver::process_music ()
63 {
64   if (ev_)
65     extender_ = make_spanner ("LyricExtender", ev_->self_scm ());
66 }
67
68 void
69 Extender_engraver::acknowledge_lyric_syllable (Grob_info i)
70 {
71   Item *item = i.item ();
72   if (extender_)
73     extender_->set_bound (LEFT, item);
74
75   SCM text = item->get_property ("text");
76   current_lyric_is_skip_ = ly_is_equal (text, scm_from_locale_string (" "));
77
78   if (pending_extender_ && !current_lyric_is_skip_)
79     {
80       pending_extender_->set_object ("next", item->self_scm ());
81       completize_extender (pending_extender_);
82       pending_extender_ = 0;
83     }
84 }
85
86 void
87 Extender_engraver::stop_translation_timestep ()
88 {
89   if (extender_ || pending_extender_)
90     {
91       Context *voice = get_voice_to_lyrics (context ());
92       Grob *h = voice ? get_current_note_head (voice) : 0;
93
94       if (h)
95         {
96           if (extender_)
97             {
98               Pointer_group_interface::add_grob (extender_,
99                                                  ly_symbol2scm ("heads"), h);
100             }
101
102         if (pending_extender_)
103           {
104             Pointer_group_interface::add_grob (pending_extender_,
105                                               ly_symbol2scm ("heads"), h);
106             /*
107               The following check addresses the case where the lyrics end before
108               the associated voice. The current_lyric_is_skip_ check is
109               necessary to handle manual melismata, which should not result in
110               extenders being completized. We also need to make sure that we're not
111               in the middle of a note (happens when this function is called because
112               of an event in a voice other than our associated one).
113             */
114             if (!melisma_busy (voice) && !current_lyric_is_skip_)
115               {
116                 Moment now = voice->now_mom ();
117                 Paper_column *column = (dynamic_cast<Item *> (h))->get_column ();
118                 Moment *start_mom = column ? unsmob_moment (column->get_property ("when")) : 0;
119                 if (!column || (start_mom->main_part_ == now.main_part_))
120                   {
121                     completize_extender (pending_extender_);
122                     pending_extender_ = 0;
123                   }
124               }
125             }
126         }
127       else
128         {
129           if (pending_extender_
130               && !get_property ("extendersOverRests"))
131             {
132               completize_extender (pending_extender_);
133               pending_extender_ = 0;
134             }
135           
136         }
137       if (extender_)
138         {
139           pending_extender_ = extender_;
140           extender_ = 0;
141         }
142     }
143
144   ev_ = 0;
145 }
146
147 void
148 completize_extender (Spanner *sp)
149 {
150   if (!sp->get_bound (RIGHT))
151     {
152       extract_item_set (sp, "heads", heads);
153       if (heads.size ())
154         sp->set_bound (RIGHT, heads.back ());
155     }
156 }
157
158 void
159 Extender_engraver::finalize ()
160 {
161   if (extender_)
162     {
163       completize_extender (extender_);
164
165       if (!extender_->get_bound (RIGHT))
166         extender_->warning (_ ("unterminated extender"));
167       extender_ = 0;
168     }
169
170   if (pending_extender_)
171     {
172       completize_extender (pending_extender_);
173
174       if (!pending_extender_->get_bound (RIGHT))
175         pending_extender_->warning (_ ("unterminated extender"));
176       pending_extender_ = 0;
177     }
178 }
179
180 ADD_ACKNOWLEDGER (Extender_engraver, lyric_syllable);
181 ADD_TRANSLATOR (Extender_engraver,
182                 /* doc */
183                 "Create lyric extenders.",
184
185                 /* create */
186                 "LyricExtender ",
187
188                 /* read */
189                 "extendersOverRests ",
190
191                 /* write */
192                 ""
193                 );