chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / range.c
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 }