]> git.donarmstrong.com Git - lilypond.git/blob - lily/ligature-engraver.cc
Juergens patch.
[lilypond.git] / lily / ligature-engraver.cc
1 /*   
2   ligature-engraver.cc -- implement Ligature_engraver
3   
4   source file of the GNU LilyPond music typesetter
5   
6   (c) 2002 Juergen Reuter <reuter@ipd.uka.de>
7   
8  */
9 #include "ligature-engraver.hh"
10 #include "ligature-head.hh"
11 #include "spanner.hh"
12 #include "score-engraver.hh"
13 #include "note-head.hh"
14 #include "rest.hh"
15 #include "warn.hh"
16
17 /*
18  * TODO: lyrics/melisma/syllables: there should be at most one
19  * syllable of lyrics per ligature (i.e. for the lyrics context, a
20  * ligature should count as a single note, regardless of how many
21  * heads the ligature consists of).
22  *
23  * TODO: currently, you have to add/remove the proper
24  * Ligature_engraver (Ligature_bracket_engraver,
25  * Mensural_ligature_engraver) to the proper translator
26  * (e.g. VoiceContext) to choose between various representations.
27  * Since adding/removing an engraver to a translator is a global
28  * action in the paper block, you can not mix various representations
29  * _within_ the same score.  Hence, for selecting a representation,
30  * one would rather like to have a property that can be set e.g. for
31  * several staves individually.  However, it seems that this approach
32  * would require to have a single, complicated Ligature_engraver that
33  * consists of all the code...  This needs further thoughts.
34  */
35 Ligature_engraver::Ligature_engraver ()
36 {
37   ligature_ = 0;
38   finished_ligature_ = 0;
39   reqs_drul_[LEFT] = reqs_drul_[RIGHT] = 0;
40   prev_start_req_ = 0;
41   last_bound_ = 0;
42   brew_ligature_primitive_proc = SCM_EOL;
43 }
44
45 bool
46 Ligature_engraver::try_music (Music *m)
47 {
48   if (m->is_mus_type ("abort-event"))
49     {
50       reqs_drul_[START] = 0;
51       reqs_drul_[STOP] = 0;
52       if (ligature_)
53         ligature_->suicide ();
54       ligature_ = 0;
55     }
56   else if (m->is_mus_type ("ligature-event"))
57     {
58       Direction d = to_dir (m->get_mus_property ("span-direction"));
59       reqs_drul_[d] = m;
60       return true;
61     }
62   return false;
63 }
64
65 Spanner *
66 Ligature_engraver::create_ligature_spanner ()
67 {
68   programming_error ("Ligature_engraver::create_ligature_spanner (): "
69                      "this is an abstract method that should not be called, "
70                      "but overridden by a subclass");
71   return 0;
72 }
73
74 void
75 Ligature_engraver::process_music ()
76 {
77   if (reqs_drul_[STOP])
78     {
79       if (!ligature_)
80         reqs_drul_[STOP]->origin ()->warning (_ ("can't find start of ligature"));
81       else
82         {
83           if (!last_bound_)
84             {
85               reqs_drul_[STOP]->origin ()->warning (_ ("no right bound"));
86             }
87           else
88             {
89               ligature_->set_bound (RIGHT, last_bound_);
90             }
91         }
92       prev_start_req_ = 0;
93       finished_primitives_ = primitives_;
94       finished_ligature_ = ligature_;
95       primitives_.clear ();
96       ligature_ = 0;
97     }
98   last_bound_ = unsmob_grob (get_property ("currentMusicalColumn"));
99
100   if (ligature_)
101     {
102       // TODO: maybe forbid breaks only if not transcribing
103       top_engraver ()->forbid_breaks ();
104     }
105   if (reqs_drul_[START])
106     {
107       if (ligature_)
108         {
109           reqs_drul_[START]->origin ()->warning (_ ("already have a ligature"));
110           return;
111         }
112
113       prev_start_req_ = reqs_drul_[START];
114       ligature_ = create_ligature_spanner ();
115       brew_ligature_primitive_proc =
116         ligature_->get_grob_property ("ligature-primitive-callback");
117       if (brew_ligature_primitive_proc == SCM_EOL)
118         {
119           warning ("Ligature_engraver: ligature-primitive-callback undefined");
120         }
121
122       Grob *bound = unsmob_grob (get_property ("currentMusicalColumn"));
123       if (!bound)
124         {
125           reqs_drul_[START]->origin ()->warning (_ ("no left bound"));
126         }
127       else
128         {
129           ligature_->set_bound (LEFT, bound);
130         }
131
132       ligature_start_mom_ = now_mom ();
133       
134       announce_grob(ligature_, reqs_drul_[START]->self_scm());
135     }
136 }
137
138 void
139 Ligature_engraver::start_translation_timestep ()
140 {
141   reqs_drul_[START] = 0;
142   reqs_drul_[STOP] = 0;
143 }
144
145 void
146 Ligature_engraver::typeset_ligature (Spanner *, Array<Grob_info>)
147 {
148   programming_error ("Ligature_engraver::typeset_ligature (): "
149                      "this is an abstract method that should not be called, "
150                      "but overridden by a subclass");
151 }
152
153 void
154 Ligature_engraver::stop_translation_timestep ()
155 {
156   if (finished_ligature_)
157     {
158       typeset_ligature (finished_ligature_, finished_primitives_);
159       finished_primitives_.clear ();
160       finished_ligature_ = 0;
161     }
162 }
163
164 void
165 Ligature_engraver::finalize ()
166 {
167   if (finished_ligature_)
168     {
169       typeset_ligature (finished_ligature_, finished_primitives_);
170       finished_primitives_.clear ();
171       finished_ligature_ = 0;
172     }
173   if (ligature_)
174     {
175       prev_start_req_->origin ()->warning (_ ("unterminated ligature"));
176       ligature_->suicide ();
177     }
178 }
179
180 Spanner *
181 Ligature_engraver::current_ligature ()
182 {
183   return ligature_;
184 }
185
186 void
187 Ligature_engraver::acknowledge_grob (Grob_info info)
188 {
189   if (ligature_)
190     {
191       if (Note_head::has_interface (info.grob_))
192         {
193           primitives_.push (info);
194         }
195       if (Ligature_head::has_interface (info.grob_))
196         {
197           info.grob_->set_grob_property ("ligature-primitive-callback",
198                                          brew_ligature_primitive_proc);
199         }
200       else if (Rest::has_interface (info.grob_))
201         {
202           info.music_cause ()->origin ()->warning (_ ("ligature may not contain rest; ignoring rest"));
203           prev_start_req_->origin ()->warning (_ ("ligature was started here"));
204           // TODO: maybe better should stop ligature here rather than
205           // ignoring the rest?
206         }
207     }
208 }
209
210 ENTER_DESCRIPTION (Ligature_engraver,
211 /* descr */       "Abstract class; a concrete subclass handles Ligature_events by engraving Ligatures in a concrete style.",
212 /* creats */      "",
213 /* accepts */     "ligature-event abort-event",
214 /* acks  */      "ligature-head-interface rest-interface",
215 /* reads */       "",
216 /* write */       "");