Commit | Line | Data |
---|---|---|
69695f33 MW |
1 | |
2 | /* Copyright (c) 1995, 1997, 1998, 1999, 2002, 2004 by Arkkra Enterprises */ | |
3 | /* All rights reserved */ | |
4 | ||
5 | /* Functions for saving away ranges of staff numbers or vno's when user | |
6 | * is defining groups, lyrics, or stuff for one or more staffs and/or | |
7 | * voices/verses. */ | |
8 | ||
9 | ||
10 | #include "defines.h" | |
11 | #include "structs.h" | |
12 | #include "globals.h" | |
13 | ||
14 | /* special marker for "all." use something that can't possibly be a staff num */ | |
15 | #define ALL (-1) | |
16 | ||
17 | ||
18 | static void free_rangelist P((struct RANGELIST *list_p)); | |
19 | ||
20 | ||
21 | ||
22 | /* When a line of input is being gathered for groups, lyrics, or stuff, | |
23 | * this function should be called first. It makes | |
24 | * sure the current range list is set to empty and makes a note of the place, | |
25 | * (PL_ABOVE, PL_BELOW, or PL_BETWEEN) for later reference | |
26 | */ | |
27 | ||
28 | void | |
29 | begin_range(place) | |
30 | ||
31 | int place; /* PL_ABOVE, etc */ | |
32 | ||
33 | { | |
34 | Staffrange_p = (struct RANGELIST *) 0; | |
35 | Vnorange_p = (struct RANGELIST *) 0; | |
36 | Place = (short) place; | |
37 | } | |
38 | \f | |
39 | ||
40 | /* This function is called when the parser has found a range of staffs that | |
41 | * is to get the current set of groups, lyrics or stuff. | |
42 | * In the degenerate case, the range | |
43 | * may be a single staff. In the case of PL_BETWEEN, the ending staff number | |
44 | * must be one more than the beginning. | |
45 | * If the endstaffno is ALL, this is a special case of "all" as in | |
46 | * "above all" or "below all." | |
47 | */ | |
48 | ||
49 | void | |
50 | save_staff_range(beginstaffno, endstaffno) | |
51 | ||
52 | int beginstaffno; /* first staff in range */ | |
53 | int endstaffno; /* last staff in range */ | |
54 | ||
55 | { | |
56 | struct RANGELIST *new_p; /* to save info about this range */ | |
57 | short is_all = NO; | |
58 | ||
59 | ||
60 | /* handle special case of "all" */ | |
61 | if (endstaffno == ALL) { | |
62 | is_all = YES; | |
63 | endstaffno = beginstaffno; | |
64 | } | |
65 | ||
66 | /* do error checks */ | |
67 | if (rangecheck(beginstaffno, 1, MAXSTAFFS, "staff number") == NO) { | |
68 | return; | |
69 | } | |
70 | if (rangecheck(endstaffno, 1, MAXSTAFFS, "staff number") == NO) { | |
71 | return; | |
72 | } | |
73 | ||
74 | if (endstaffno < beginstaffno) { | |
75 | yyerror("end of staff range smaller than beginning"); | |
76 | return; | |
77 | } | |
78 | ||
79 | if (Place == PL_BETWEEN) { | |
80 | if (endstaffno != beginstaffno + 1) { | |
81 | yyerror("if place is 'between', second staff must be 1 greater than first"); | |
82 | return; | |
83 | } | |
84 | ||
85 | if (beginstaffno == Score.staffs) { | |
86 | yyerror("can't use 'between' on bottom staff"); | |
87 | return; | |
88 | } | |
89 | } | |
90 | ||
91 | /* allocate a new struct and link onto head of list */ | |
92 | CALLOC(RANGELIST, new_p, 1); | |
93 | new_p->next = Staffrange_p; | |
94 | Staffrange_p = new_p; | |
95 | ||
96 | /* fill in other fields */ | |
97 | new_p->begin = (short) beginstaffno; | |
98 | new_p->end = (short) (Place == PL_BETWEEN ? beginstaffno : endstaffno); | |
99 | new_p->all = is_all; | |
100 | new_p->place = Place; | |
101 | } | |
102 | \f | |
103 | ||
104 | /* given a range of vno's, save the range for later use */ | |
105 | /* Any error checking of the numbers should be done before calling this | |
106 | * function. */ | |
107 | ||
108 | void | |
109 | save_vno_range(begin, end) | |
110 | ||
111 | int begin; /* first vno */ | |
112 | int end; /* last vno */ | |
113 | ||
114 | { | |
115 | struct RANGELIST *new_p; /* to store vno info */ | |
116 | ||
117 | ||
118 | /* allocate a new struct and link onto head of list */ | |
119 | CALLOC(RANGELIST, new_p, 1); | |
120 | new_p->next = Vnorange_p; | |
121 | Vnorange_p = new_p; | |
122 | ||
123 | /* fill in other fields */ | |
124 | new_p->begin = (short) begin; | |
125 | new_p->end = (short) end; | |
126 | } | |
127 | \f | |
128 | ||
129 | /* free list of staff ranges */ | |
130 | ||
131 | void | |
132 | free_staffrange() | |
133 | ||
134 | { | |
135 | free_rangelist(Staffrange_p); | |
136 | Staffrange_p = (struct RANGELIST *) 0; | |
137 | } | |
138 | ||
139 | ||
140 | ||
141 | /* free list of vno ranges */ | |
142 | ||
143 | void | |
144 | free_vnorange() | |
145 | ||
146 | { | |
147 | free_rangelist(Vnorange_p); | |
148 | Vnorange_p = (struct RANGELIST *) 0; | |
149 | } | |
150 | ||
151 | ||
152 | ||
153 | ||
154 | /* free both the staff and vno lists */ | |
155 | ||
156 | void | |
157 | free_rlists() | |
158 | ||
159 | { | |
160 | if (Svrangelist_p != (struct SVRANGELIST *) 0) { | |
161 | free_sv_list(Svrangelist_p); | |
162 | Svrangelist_p = (struct SVRANGELIST *) 0; | |
163 | } | |
164 | else { | |
165 | free_staffrange(); | |
166 | free_vnorange(); | |
167 | } | |
168 | } | |
169 | \f | |
170 | ||
171 | /* free the Svrangelist and the RANGELISTs hanging off of it */ | |
172 | ||
173 | void | |
174 | free_sv_list(svrangelist_p) | |
175 | ||
176 | struct SVRANGELIST *svrangelist_p; | |
177 | ||
178 | { | |
179 | if (svrangelist_p == (struct SVRANGELIST *) 0) { | |
180 | return; | |
181 | } | |
182 | ||
183 | free_rangelist(svrangelist_p->stafflist_p); | |
184 | free_rangelist(svrangelist_p->vnolist_p); | |
185 | ||
186 | /* recurse */ | |
187 | free_sv_list(svrangelist_p->next); | |
188 | FREE(svrangelist_p); | |
189 | } | |
190 | \f | |
191 | ||
192 | /* recursively free a list of RANGELIST structs */ | |
193 | ||
194 | static void | |
195 | free_rangelist(list_p) | |
196 | ||
197 | struct RANGELIST *list_p; /* the list to free */ | |
198 | ||
199 | { | |
200 | if (list_p == (struct RANGELIST *) 0) { | |
201 | return; | |
202 | } | |
203 | ||
204 | free_rangelist(list_p->next); | |
205 | FREE(list_p); | |
206 | } | |
207 | \f | |
208 | ||
209 | /* If doing between, staff ranges must be of the form N&M. If not doing | |
210 | * between, must be either a single number or N-M. Make sure this is so. | |
211 | */ | |
212 | ||
213 | void | |
214 | chk_range_type(has_ampersand) | |
215 | ||
216 | int has_ampersand; /* YES if range was of the form N&M */ | |
217 | ||
218 | { | |
219 | if (has_ampersand == YES && Place != PL_BETWEEN) { | |
220 | yyerror("& only valid with 'between'"); | |
221 | return; | |
222 | } | |
223 | ||
224 | if (has_ampersand == NO && Place == PL_BETWEEN) { | |
225 | yyerror("must use & to specify ranges with 'between'"); | |
226 | } | |
227 | } | |
228 | \f | |
229 | ||
230 | /* Create a STAFF struct in the main list for every staff. | |
231 | * Point List_of_staffs_p to the first of them. | |
232 | * Fill in Staffmap_p for each of them to allow quick mapping from staffno | |
233 | * to STAFF struct. | |
234 | */ | |
235 | ||
236 | void | |
237 | create_staffs() | |
238 | ||
239 | { | |
240 | struct MAINLL *mll_insert_p; /* where to insert in main list */ | |
241 | struct MAINLL *new_p; /* newly allocated struct */ | |
242 | struct MAINLL *mll_p; /* for verifiying proper order */ | |
243 | struct MAINLL *next_mll_p; /* next main list item to be checked */ | |
244 | register int s; /* index through staffs */ | |
245 | ||
246 | ||
247 | debug(4, "create_staffs"); | |
248 | ||
249 | if (List_of_staffs_p != (struct MAINLL *) 0) { | |
250 | /* this function has already been called for current measure, so | |
251 | * nothing more to do. This is normal, because this function | |
252 | * is called whenever another function needs to make sure | |
253 | * the STAFFs have been created. | |
254 | */ | |
255 | return; | |
256 | } | |
257 | ||
258 | /* STAFFS are supposed to come before LINES, CURVES, and PRHEADS. | |
259 | * However, the user is not constrained to put things in in that | |
260 | * order, so there may be some already on the list. If so, back | |
261 | * up to before where they should begin and insert the STAFFS there. */ | |
262 | for (mll_insert_p = Mainlltc_p; mll_insert_p != (struct MAINLL *) 0; | |
263 | mll_insert_p = mll_insert_p->prev) { | |
264 | ||
265 | if (mll_insert_p->str != S_LINE | |
266 | && mll_insert_p->str != S_CURVE | |
267 | && mll_insert_p->str != S_PRHEAD) { | |
268 | break; | |
269 | } | |
270 | } | |
271 | ||
272 | /* keep track of place in main list, for later use */ | |
273 | mll_p = mll_insert_p; | |
274 | ||
275 | /* allocate and add a struct for each staff in the range */ | |
276 | for ( s = 1; s <= Score.staffs; s++) { | |
277 | ||
278 | new_p = newMAINLLstruct(S_STAFF, yylineno); | |
279 | new_p->u.staff_p->staffno = (short) s; | |
280 | insertMAINLL(new_p, mll_insert_p); | |
281 | ||
282 | if (List_of_staffs_p == (struct MAINLL *) 0) { | |
283 | List_of_staffs_p = new_p; | |
284 | } | |
285 | ||
286 | Staffmap_p[s] = new_p; | |
287 | mll_insert_p = new_p; | |
288 | } | |
289 | ||
290 | /* while we're making sure the main list in in the prescribed order, | |
291 | * back up all the way to the previous bar (or beginning of list). | |
292 | * If there are any LINES, CURVES, or PRHEADS in between there, move | |
293 | * them to the end. This could happen if, for example, the user | |
294 | * put in a print statement followed by a change of clef */ | |
295 | while (mll_p != (struct MAINLL *) 0) { | |
296 | if (mll_p->str == S_BAR) { | |
297 | /* this is far enough to back up */ | |
298 | break; | |
299 | } | |
300 | ||
301 | if (mll_p->str == S_LINE || mll_p->str == S_CURVE || | |
302 | mll_p->str == S_PRHEAD) { | |
303 | next_mll_p = mll_p->prev; | |
304 | unlinkMAINLL(mll_p); | |
305 | insertMAINLL(mll_p, mll_insert_p); | |
306 | mll_insert_p = mll_p; | |
307 | mll_p = next_mll_p; | |
308 | } | |
309 | else { | |
310 | mll_p = mll_p->prev; | |
311 | } | |
312 | } | |
313 | } | |
314 | \f | |
315 | ||
316 | /* if user specifies staff as "all", need to find the top visible | |
317 | * staff (if above) or bottom visible (if below). If not above or below, | |
318 | * error. */ | |
319 | ||
320 | void | |
321 | all() | |
322 | ||
323 | { | |
324 | int s; /* staff number */ | |
325 | ||
326 | ||
327 | /* if user didn't specify a place, have to get default value */ | |
328 | if (Place == PL_UNKNOWN) { | |
329 | Place = dflt_place(); | |
330 | } | |
331 | ||
332 | switch(Place) { | |
333 | case PL_ABOVE: | |
334 | for (s = 1; s <= Score.staffs; s++) { | |
335 | if ( (svpath(s, VISIBLE))->visible == YES) { | |
336 | save_staff_range(s, ALL); | |
337 | return; | |
338 | } | |
339 | } | |
340 | break; | |
341 | ||
342 | case PL_BELOW: | |
343 | for (s = Score.staffs; s > 0; s--) { | |
344 | if ( (svpath(s, VISIBLE))->visible == YES) { | |
345 | save_staff_range(s, ALL); | |
346 | return; | |
347 | } | |
348 | } | |
349 | break; | |
350 | ||
351 | default: | |
352 | yyerror("'all' invalid"); | |
353 | return; | |
354 | } | |
355 | ||
356 | yyerror("no staffs visible"); | |
357 | } | |
358 | \f | |
359 | ||
360 | /* start a new staff-voice list */ | |
361 | ||
362 | void | |
363 | begin_sv_list() | |
364 | ||
365 | { | |
366 | Svrangelist_p = (struct SVRANGELIST *) 0; | |
367 | } | |
368 | \f | |
369 | ||
370 | /* add the current staff and vno list to the staff-vno list */ | |
371 | ||
372 | void | |
373 | add_to_sv_list() | |
374 | ||
375 | { | |
376 | struct SVRANGELIST *new_p; | |
377 | struct SVRANGELIST **insert_p_p; | |
378 | ||
379 | MALLOC(SVRANGELIST, new_p, 1); | |
380 | new_p->stafflist_p = Staffrange_p; | |
381 | new_p->vnolist_p = Vnorange_p; | |
382 | new_p->next = (struct SVRANGELIST *) 0; | |
383 | ||
384 | /* link onto end of list */ | |
385 | for (insert_p_p = & Svrangelist_p; | |
386 | *insert_p_p != (struct SVRANGELIST *) 0; | |
387 | insert_p_p = & ((*insert_p_p)->next) ) { | |
388 | ; | |
389 | } | |
390 | *insert_p_p = new_p; | |
391 | } | |
392 | \f | |
393 | ||
394 | /* return YES if given staff is a tab staff, NO if not */ | |
395 | ||
396 | int | |
397 | is_tab_staff(staffno) | |
398 | ||
399 | int staffno; | |
400 | ||
401 | { | |
402 | if (staffno < 1 && staffno > MAXSTAFFS) { | |
403 | /* not a staff, so not a tab staff. */ | |
404 | return(NO); | |
405 | } | |
406 | return (Staff[staffno - 1].strinfo == (struct STRINGINFO *) 0 ? NO : YES); | |
407 | } | |
408 | \f | |
409 | ||
410 | /* return the staff number of the first staff on the current list of staffs. | |
411 | * In this context, the "first staff" means the first staff the user defined, | |
412 | * so if they said something like 5,6,9-12,2 this would return 5 */ | |
413 | ||
414 | int | |
415 | leadstaff() | |
416 | ||
417 | { | |
418 | struct RANGELIST *r_p; | |
419 | ||
420 | if (Staffrange_p == (struct RANGELIST *) 0) { | |
421 | pfatal("leadstaff called when no staffs on list"); | |
422 | } | |
423 | ||
424 | /* since new ranges are linked onto the head of the list, we need | |
425 | * to find the last range on the list. That will be the first one | |
426 | * the user specified. */ | |
427 | for (r_p = Staffrange_p; r_p->next != (struct RANGELIST *) 0; | |
428 | r_p = r_p->next) { | |
429 | ; | |
430 | } | |
431 | return(r_p->begin); | |
432 | } |