]> git.donarmstrong.com Git - lilypond.git/blob - lily/gregorian-ligature-engraver.cc
*** empty log message ***
[lilypond.git] / lily / gregorian-ligature-engraver.cc
1 /*
2   gregorian-ligature-engraver.cc -- implement Gregorian_ligature_engraver
3   
4   source file of the GNU LilyPond music typesetter
5   
6   (C) 2003 Juergen Reuter <reuter@ipd.uka.de>
7  */
8
9 #include "gregorian-ligature-engraver.hh"
10 #include "gregorian-ligature.hh"
11 #include "item.hh"
12 #include "warn.hh"
13 #include "staff-symbol-referencer.hh"
14 #include "spanner.hh"
15 #include "paper-column.hh"
16
17 /*
18  * TODO: This class shares some code with Mensural_ligature_engraver.
19  * Maybe we should create a common super class "Rod_ligature_engraver"
20  * and derive all shared code from it.
21  */
22
23 Gregorian_ligature_engraver::Gregorian_ligature_engraver ()
24 {
25   porrectus_req_ = 0;
26 }
27
28 void
29 Gregorian_ligature_engraver::transform_heads (Spanner *, Array<Grob_info>)
30 {
31   programming_error ("Gregorian_ligature_engraver::transform_heads (): "
32                      "this is an abstract method that should not be called, "
33                      "but overridden by a subclass");
34 }
35
36 bool
37 Gregorian_ligature_engraver::try_music (Music *m)
38 {
39   if (m->is_mus_type ("porrectus-event"))
40     {
41       porrectus_req_ = m;
42       return true;
43     }
44   else
45     return Ligature_engraver::try_music (m);
46 }
47
48 /*
49  * TODO: move this function to class Item?
50  */
51 void
52 Gregorian_ligature_engraver::get_set_column (Item *item, Paper_column *column)
53 {
54   Item *parent = dynamic_cast<Item*> (item->get_parent (X_AXIS));
55   if (!parent)
56     {
57       programming_error ("failed tweaking paper column in ligature");
58       return;
59     }
60
61   String name = parent->name ();
62   if (!String::compare (name, "PaperColumn"))
63     {
64       // Change column not only for targeted item (NoteColumn), but
65       // also for all associated grobs (NoteSpacing, SeparationItem).
66       Grob *sl = Staff_symbol_referencer::get_staff_symbol (item);
67       for (SCM tail = parent->get_grob_property ("elements");
68            gh_pair_p (tail);
69            tail = ly_cdr (tail))
70         {
71           Item *sibling = unsmob_item (ly_car (tail));
72           if ((sibling) &&
73               (Staff_symbol_referencer::get_staff_symbol (sibling) == sl))
74             {
75               sibling->set_parent (column, X_AXIS);
76             }
77         }
78     }
79   else
80     {
81       get_set_column (parent, column);
82     }
83 }
84
85 void fix_prefix (char *name, int mask,
86                  int *current_set, int min_set, int max_set,
87                  Grob *primitive)
88 {
89   bool current = *current_set & mask;
90   bool min = min_set & mask;
91   bool max = max_set & mask;
92   if (max < min)
93     {
94       programming_error ("min_set > max_set");
95       return;
96     }
97   if (min && !current)
98     {
99       primitive->warning (_f ("\\%s ignored", name));
100       *current_set &= ~mask;
101     }
102   if (!max && current)
103     {
104       primitive->warning (_f ("implied \\%s added", name));
105       *current_set |= mask;
106     }
107 }
108
109 void fix_prefix_set (int *current_set, int min_set, int max_set, Grob *primitive)
110 {
111   fix_prefix ("virga", VIRGA, current_set, min_set, max_set, primitive);
112   fix_prefix ("stropha", STROPHA, current_set, min_set, max_set, primitive);
113   fix_prefix ("inclinatum", INCLINATUM, current_set, min_set, max_set, primitive);
114   fix_prefix ("auctum", AUCTUM, current_set, min_set, max_set, primitive);
115   fix_prefix ("descendens", DESCENDENS, current_set, min_set, max_set, primitive);
116   fix_prefix ("ascendens", ASCENDENS, current_set, min_set, max_set, primitive);
117   fix_prefix ("oriscus", ORISCUS, current_set, min_set, max_set, primitive);
118   fix_prefix ("quilisma", QUILISMA, current_set, min_set, max_set, primitive);
119   fix_prefix ("deminutus", DEMINUTUM, current_set, min_set, max_set, primitive);
120   fix_prefix ("semivocalis", SEMIVOCALIS, current_set, min_set, max_set, primitive);
121   fix_prefix ("cavum", CAVUM, current_set, min_set, max_set, primitive);
122   fix_prefix ("linea", LINEA, current_set, min_set, max_set, primitive);
123   fix_prefix ("pes_or_flexa", LINEA, current_set, min_set, max_set, primitive);
124 }
125
126 void check_and_fix_all_prefixes (Array<Grob_info> primitives)
127 {
128   /* Check for illegal head modifier combinations */
129   for (int i = 0; i < primitives.size(); i++) {
130     Grob *primitive = primitives[i].grob_;
131
132     /* compute head prefix set by inspecting primitive grob properties */
133     int prefix_set =
134       (VIRGA * to_boolean (primitive->get_grob_property ("virga"))) |
135       (STROPHA * to_boolean (primitive->get_grob_property ("stropha"))) |
136       (INCLINATUM * to_boolean (primitive->get_grob_property ("inclinatum"))) |
137       (AUCTUM * to_boolean (primitive->get_grob_property ("auctum"))) |
138       (DESCENDENS * to_boolean (primitive->get_grob_property ("descendens"))) |
139       (ASCENDENS * to_boolean (primitive->get_grob_property ("ascendens"))) |
140       (ORISCUS * to_boolean (primitive->get_grob_property ("oriscus"))) |
141       (QUILISMA * to_boolean (primitive->get_grob_property ("quilisma"))) |
142       (DEMINUTUM * to_boolean (primitive->get_grob_property ("deminutum"))) |
143       (SEMIVOCALIS * to_boolean (primitive->get_grob_property ("semivocalis"))) |
144       (CAVUM * to_boolean (primitive->get_grob_property ("cavum"))) |
145       (LINEA * to_boolean (primitive->get_grob_property ("linea"))) |
146       (PES_OR_FLEXA * to_boolean (primitive->get_grob_property ("pes-or-flexa")));
147
148     /* check: ascendens and descendens exclude each other; same with
149        auctum and diminutum */
150     if (prefix_set & DESCENDENS)
151       {
152         fix_prefix_set (&prefix_set,
153                         prefix_set & ~ASCENDENS,
154                         prefix_set & ~ASCENDENS,
155                         primitive);
156       }
157     if (prefix_set & AUCTUM)
158       {
159         fix_prefix_set (&prefix_set,
160                         prefix_set & ~DEMINUTUM,
161                         prefix_set & ~DEMINUTUM,
162                         primitive);
163       }
164
165     /* check: virga, quilisma and oriscus can not be combined with any
166        other prefix, but may be part of a pes or flexa */
167     if (prefix_set & VIRGA)
168       {
169         fix_prefix_set (&prefix_set,
170                         VIRGA,
171                         VIRGA | PES_OR_FLEXA,
172                         primitive);
173       }
174     if (prefix_set & QUILISMA)
175       {
176         fix_prefix_set (&prefix_set,
177                         QUILISMA,
178                         QUILISMA | PES_OR_FLEXA,
179                         primitive);
180       }
181     if (prefix_set & ORISCUS)
182       {
183         fix_prefix_set (&prefix_set,
184                         ORISCUS,
185                         ORISCUS | PES_OR_FLEXA,
186                         primitive);
187       }
188
189     /* check: auctum is the only valid optional prefix for stropha */
190     if (prefix_set & STROPHA)
191       {
192         fix_prefix_set (&prefix_set,
193                         STROPHA,
194                         STROPHA | AUCTUM,
195                         primitive);
196       }
197
198     /* check: semivocalis must occur in combination with and only with
199        pes or flexa */
200     if (prefix_set & SEMIVOCALIS)
201       {
202         fix_prefix_set (&prefix_set,
203                         SEMIVOCALIS | PES_OR_FLEXA,
204                         SEMIVOCALIS | PES_OR_FLEXA,
205                         primitive);
206       }
207
208     /* check: inclinatum may be prefixed with auctum or diminutum only */
209     if (prefix_set & INCLINATUM)
210       {
211         fix_prefix_set (&prefix_set,
212                         INCLINATUM,
213                         INCLINATUM | AUCTUM | DEMINUTUM,
214                         primitive);
215       }
216
217     /* check: cavum and linea (either or both) may be applied only
218        upon core punctum */
219     if (prefix_set & (CAVUM | LINEA))
220       {
221         fix_prefix_set (&prefix_set,
222                         0,
223                         CAVUM | LINEA,
224                         primitive);
225       }
226
227     /* all other combinations should be valid (unless I made a
228        mistake) */
229
230     primitive->set_grob_property ("prefix-set", gh_int2scm (prefix_set));
231   }
232 }
233
234 /*
235  * Marks those heads that participate in a pes or flexa.
236  */
237 void
238 provide_context_info (Array<Grob_info> primitives)
239 {
240   Grob *prev_primitive = 0;
241   int prev_context_info = 0;
242   int prev_pitch = 0;
243   for (int i = 0; i < primitives.size(); i++) {
244     Grob *primitive = primitives[i].grob_;
245     Music *music_cause = primitives[i].music_cause ();
246     int context_info = 0;
247     int pitch = unsmob_pitch (music_cause->get_mus_property ("pitch"))->steps ();
248     int prefix_set = gh_scm2int (primitive->get_grob_property ("prefix-set"));
249
250     if (prefix_set & PES_OR_FLEXA)
251       if (pitch > prev_pitch) // pes
252         {
253           prev_context_info |= PES_LOWER;
254           context_info |= PES_UPPER;
255         }
256       else if (pitch < prev_pitch) // flexa
257         {
258           prev_context_info |= FLEXA_LEFT;
259           context_info |= FLEXA_RIGHT;
260         }
261       else // (pitch == prev_pitch)
262         {
263           primitive->warning ("may not apply `\\~' on heads with "
264                               "identical pitch; ignoring `\\~'");
265         }
266     if (prev_primitive)
267       prev_primitive->set_grob_property ("context-info",
268                                          gh_int2scm (prev_context_info));
269     prev_primitive = primitive;
270     prev_context_info = context_info;
271     prev_pitch = pitch;
272   }
273   if (prev_primitive)
274     prev_primitive->set_grob_property ("context-info",
275                                        gh_int2scm (prev_context_info));
276 }
277
278 void
279 Gregorian_ligature_engraver::typeset_ligature (Spanner *ligature,
280                                                Array<Grob_info> primitives)
281 {
282   // apply style-independent checking and transformation
283   check_and_fix_all_prefixes (primitives);
284   provide_context_info (primitives);
285
286   // apply style-specific transformation (including line-up)
287   transform_heads (ligature, primitives);
288
289   // typeset
290   for (int i = 0; i < primitives.size (); i++)
291     {
292       typeset_grob (primitives[i].grob_);
293     }
294 }
295
296 void
297 Gregorian_ligature_engraver::start_translation_timestep ()
298 {
299   Ligature_engraver::start_translation_timestep ();
300   porrectus_req_ = 0;
301 }
302
303 ENTER_DESCRIPTION (Gregorian_ligature_engraver,
304 /* descr */       "This is an abstract class.  Subclasses such as Vaticana_ligature_engraver handle ligatures by glueing special ligature heads together.",
305 /* creats*/       "",
306 /* accepts */     "ligature-event abort-event",
307 /* acks  */      "ligature-head-interface note-head-interface rest-interface",
308 /* reads */       "",
309 /* write */       "");