chiark / gitweb /
Merge branch 'arkkra' into shiny
[mup] / mup / mup / range.c
diff --git a/mup/mup/range.c b/mup/mup/range.c
new file mode 100644 (file)
index 0000000..c7cc22c
--- /dev/null
@@ -0,0 +1,432 @@
+
+/* Copyright (c) 1995, 1997, 1998, 1999, 2002, 2004 by Arkkra Enterprises */
+/* All rights reserved */
+
+/* Functions for saving away ranges of staff numbers or vno's when user
+ * is defining groups, lyrics, or stuff for one or more staffs and/or
+ * voices/verses. */
+
+
+#include "defines.h"
+#include "structs.h"
+#include "globals.h"
+
+/* special marker for "all." use something that can't possibly be a staff num */
+#define ALL    (-1)
+
+
+static void free_rangelist P((struct RANGELIST *list_p));
+
+
+
+/* When a line of input is being gathered for groups, lyrics, or stuff,
+ * this function should be called first. It makes
+ * sure the current range list is set to empty and makes a note of the place,
+ * (PL_ABOVE, PL_BELOW, or PL_BETWEEN) for later reference
+ */
+
+void
+begin_range(place)
+
+int place;     /* PL_ABOVE, etc */
+
+{
+       Staffrange_p = (struct RANGELIST *) 0;
+       Vnorange_p = (struct RANGELIST *) 0;
+       Place = (short) place;
+}
+\f
+
+/* This function is called when the parser has found a range of staffs that
+ * is to get the current set of groups, lyrics or stuff.
+ * In the degenerate case, the range
+ * may be a single staff. In the case of PL_BETWEEN, the ending staff number
+ * must be one more than the beginning. 
+ * If the endstaffno is ALL, this is a special case of "all" as in
+ * "above all" or "below all."
+ */
+
+void
+save_staff_range(beginstaffno, endstaffno)
+
+int beginstaffno;      /* first staff in range */
+int endstaffno;                /* last staff in range */
+
+{
+       struct RANGELIST *new_p;        /* to save info about this range */
+       short is_all = NO;
+       
+
+       /* handle special case of "all" */
+       if (endstaffno == ALL) {
+               is_all = YES;
+               endstaffno = beginstaffno;
+       }
+
+       /* do error checks */
+       if (rangecheck(beginstaffno, 1, MAXSTAFFS, "staff number") == NO) {
+               return;
+       }
+       if (rangecheck(endstaffno, 1, MAXSTAFFS, "staff number") == NO) {
+               return;
+       }
+
+       if (endstaffno < beginstaffno) {
+               yyerror("end of staff range smaller than beginning");
+               return;
+       }
+
+       if (Place == PL_BETWEEN) {
+               if (endstaffno != beginstaffno + 1) {
+                       yyerror("if place is 'between', second staff must be 1 greater than first");
+                       return;
+               }
+
+               if (beginstaffno == Score.staffs) {
+                       yyerror("can't use 'between' on bottom staff");
+                       return;
+               }
+       }
+
+       /* allocate a new struct and link onto head of list */
+       CALLOC(RANGELIST, new_p, 1);
+       new_p->next = Staffrange_p;
+       Staffrange_p = new_p;
+
+       /* fill in other fields */
+       new_p->begin = (short) beginstaffno;
+       new_p->end = (short) (Place == PL_BETWEEN ? beginstaffno : endstaffno);
+       new_p->all = is_all;
+       new_p->place = Place;
+}
+\f
+
+/* given a range of vno's, save the range for later use */
+/* Any error checking of the numbers should be done before calling this
+ * function. */
+
+void
+save_vno_range(begin, end)
+
+int begin;     /* first vno */
+int end;       /* last vno */
+
+{
+       struct RANGELIST *new_p;        /* to store vno info */
+
+
+       /* allocate a new struct and link onto head of list */
+       CALLOC(RANGELIST, new_p, 1);
+       new_p->next = Vnorange_p;
+       Vnorange_p = new_p;
+
+       /* fill in other fields */
+       new_p->begin = (short) begin;
+       new_p->end = (short) end;
+}
+\f
+
+/* free list of staff ranges */
+
+void
+free_staffrange()
+
+{
+       free_rangelist(Staffrange_p);
+       Staffrange_p = (struct RANGELIST *) 0;
+}
+
+
+
+/* free list of vno ranges */
+
+void
+free_vnorange()
+
+{
+       free_rangelist(Vnorange_p);
+       Vnorange_p = (struct RANGELIST *) 0;
+}
+
+
+
+
+/* free both the staff and vno lists */
+
+void
+free_rlists()
+
+{
+       if (Svrangelist_p != (struct SVRANGELIST *) 0) {
+               free_sv_list(Svrangelist_p);
+               Svrangelist_p = (struct SVRANGELIST *) 0;
+       }
+       else {
+               free_staffrange();
+               free_vnorange();
+       }
+}
+\f
+
+/* free the Svrangelist and the RANGELISTs hanging off of it */
+
+void
+free_sv_list(svrangelist_p)
+
+struct SVRANGELIST *svrangelist_p;
+
+{
+       if (svrangelist_p == (struct SVRANGELIST *) 0) {
+               return;
+       }
+
+       free_rangelist(svrangelist_p->stafflist_p);
+       free_rangelist(svrangelist_p->vnolist_p);
+
+       /* recurse */
+       free_sv_list(svrangelist_p->next);
+       FREE(svrangelist_p);
+}
+\f
+
+/* recursively free a list of RANGELIST structs */
+
+static void
+free_rangelist(list_p)
+
+struct RANGELIST *list_p;      /* the list to free */
+
+{
+       if (list_p == (struct RANGELIST *) 0) {
+               return;
+       }
+
+       free_rangelist(list_p->next);
+       FREE(list_p);
+}
+\f
+
+/* If doing between, staff ranges must be of the form N&M. If not doing
+ * between, must be either a single number or N-M. Make sure this is so.
+ */
+
+void
+chk_range_type(has_ampersand)
+
+int has_ampersand;     /* YES if range was of the form N&M */
+
+{
+       if (has_ampersand == YES && Place != PL_BETWEEN) {
+               yyerror("& only valid with 'between'");
+               return;
+       }
+
+       if (has_ampersand == NO && Place == PL_BETWEEN) {
+               yyerror("must use & to specify ranges with 'between'");
+       }
+}
+\f
+
+/* Create a STAFF struct in the main list for every staff.
+ * Point List_of_staffs_p to the first of them.
+ * Fill in Staffmap_p for each of them to allow quick mapping from staffno
+ * to STAFF struct.
+ */
+
+void
+create_staffs()
+
+{
+       struct MAINLL *mll_insert_p;    /* where to insert in main list */
+       struct MAINLL *new_p;           /* newly allocated struct */
+       struct MAINLL *mll_p;           /* for verifiying proper order */
+       struct MAINLL *next_mll_p;      /* next main list item to be checked */
+       register int s;                 /* index through staffs */
+
+
+       debug(4, "create_staffs");
+
+       if (List_of_staffs_p != (struct MAINLL *) 0) {
+               /* this function has already been called for current measure, so
+                * nothing more to do. This is normal, because this function
+                * is called whenever another function needs to make sure
+                * the STAFFs have been created.
+                */
+               return;
+       }
+
+       /* STAFFS are supposed to come before LINES, CURVES, and PRHEADS.
+        * However, the user is not constrained to put things in in that
+        * order, so there may be some already on the list. If so, back
+        * up to before where they should begin and insert the STAFFS there. */
+       for (mll_insert_p = Mainlltc_p; mll_insert_p != (struct MAINLL *) 0;
+                                       mll_insert_p = mll_insert_p->prev) {
+
+               if (mll_insert_p->str != S_LINE
+                                       && mll_insert_p->str != S_CURVE
+                                       && mll_insert_p->str != S_PRHEAD) {
+                       break;
+               }
+       }
+
+       /* keep track of place in main list, for later use */
+       mll_p = mll_insert_p;
+
+       /* allocate and add a struct for each staff in the range */
+       for ( s = 1; s <= Score.staffs; s++) {
+
+               new_p = newMAINLLstruct(S_STAFF, yylineno);
+               new_p->u.staff_p->staffno = (short) s;
+               insertMAINLL(new_p, mll_insert_p);
+
+               if (List_of_staffs_p == (struct MAINLL *) 0) {
+                       List_of_staffs_p = new_p;
+               }
+
+               Staffmap_p[s] = new_p;
+               mll_insert_p = new_p;
+       }
+
+       /* while we're making sure the main list in in the prescribed order,
+        * back up all the way to the previous bar (or beginning of list).
+        * If there are any LINES, CURVES, or PRHEADS in between there, move
+        * them to the end. This could happen if, for example, the user
+        * put in a print statement followed by a change of clef */
+       while (mll_p != (struct MAINLL *) 0) {
+               if (mll_p->str == S_BAR) {
+                       /* this is far enough to back up */
+                       break;
+               }
+
+               if (mll_p->str == S_LINE || mll_p->str == S_CURVE ||
+                                       mll_p->str == S_PRHEAD) {
+                       next_mll_p = mll_p->prev;
+                       unlinkMAINLL(mll_p);
+                       insertMAINLL(mll_p, mll_insert_p);
+                       mll_insert_p = mll_p;
+                       mll_p = next_mll_p;
+               }
+               else {
+                       mll_p = mll_p->prev;
+               }
+       }
+}
+\f
+
+/* if user specifies staff as "all", need to find the top visible
+ * staff (if above) or bottom visible (if below). If not above or below,
+ * error. */
+
+void
+all()
+
+{
+       int s;          /* staff number */
+
+
+       /* if user didn't specify a place, have to get default value */
+       if (Place == PL_UNKNOWN) {
+               Place = dflt_place();
+       }
+
+       switch(Place) {
+       case PL_ABOVE:
+               for (s = 1; s <= Score.staffs; s++) {
+                       if ( (svpath(s, VISIBLE))->visible == YES) {
+                               save_staff_range(s, ALL);
+                               return;
+                       }
+               }
+               break;
+
+       case PL_BELOW:
+               for (s = Score.staffs; s > 0; s--) {
+                       if ( (svpath(s, VISIBLE))->visible == YES) {
+                               save_staff_range(s, ALL);
+                               return;
+                       }
+               }
+               break;
+
+       default:
+               yyerror("'all' invalid");
+               return;
+       }
+
+       yyerror("no staffs visible");
+}
+\f
+
+/* start a new staff-voice list */
+
+void
+begin_sv_list()
+
+{
+       Svrangelist_p = (struct SVRANGELIST *) 0;
+}
+\f
+
+/* add the current staff and vno list to the staff-vno list */
+
+void
+add_to_sv_list()
+
+{
+       struct SVRANGELIST *new_p;
+       struct SVRANGELIST **insert_p_p;
+
+       MALLOC(SVRANGELIST, new_p, 1);
+       new_p->stafflist_p = Staffrange_p;
+       new_p->vnolist_p = Vnorange_p;
+       new_p->next = (struct SVRANGELIST *) 0;
+
+       /* link onto end of list */
+       for (insert_p_p = & Svrangelist_p;
+                               *insert_p_p != (struct SVRANGELIST *) 0;
+                               insert_p_p = & ((*insert_p_p)->next) ) {
+               ;
+       }
+       *insert_p_p = new_p;
+}
+\f
+
+/* return YES if given staff is a tab staff, NO if not */
+
+int
+is_tab_staff(staffno)
+
+int staffno;
+
+{
+       if (staffno < 1 && staffno > MAXSTAFFS) {
+               /* not a staff, so not a tab staff. */
+               return(NO);
+       }
+       return (Staff[staffno - 1].strinfo == (struct STRINGINFO *) 0 ? NO : YES);
+}
+\f
+
+/* return the staff number of the first staff on the current list of staffs.
+ * In this context, the "first staff" means the first staff the user defined,
+ * so if they said something like 5,6,9-12,2 this would return 5 */
+
+int
+leadstaff()
+
+{
+       struct RANGELIST *r_p;
+
+       if (Staffrange_p == (struct RANGELIST *) 0) {
+               pfatal("leadstaff called when no staffs on list");
+       }
+
+       /* since new ranges are linked onto the head of the list, we need
+        * to find the last range on the list. That will be the first one
+        * the user specified. */
+       for (r_p = Staffrange_p; r_p->next != (struct RANGELIST *) 0;
+                                               r_p = r_p->next) {
+               ;
+       }
+       return(r_p->begin);
+}