| 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 | } |