Merge branch 'arkkra' into shiny
[mup] / mup / mup / brac.c
diff --git a/mup/mup/brac.c b/mup/mup/brac.c
new file mode 100644 (file)
index 0000000..44ca1f1
--- /dev/null
@@ -0,0 +1,1017 @@
+
+/* Copyright (c) 1995, 1997, 1999, 2000, 2001, 2002, 2003, 2005 by Arkkra Enterprises */
+/* All rights reserved */
+
+/* functions to deal with brace/bracket lists, to make sure they don't
+ * overlap, and then to place the labels to minimize the space they use. */
+
+#include "defines.h"
+#include "structs.h"
+#include "globals.h"
+
+/* padding between labels in inches. **** eventually should adjust padding
+ * based on size??? ***/
+#define LABELPAD       0.125
+
+/* information to be able to determine overlaps in the brace/bracket lists */
+static struct BRAC_INFO {
+       struct STAFFSET *staffset_p;    /* bracelist or bracklist item */
+       int bractype;                   /* BRACELIST or BRACKLIST */
+       struct BRAC_INFO *nested_p;     /* pointer to another brace/bracket
+                                        * item, which has its top on the
+                                        * same staff, and presumably
+                                        * is nested inside this one */
+       struct BRAC_INFO *nested_by_p;  /* if this one is nested, pointer
+                                        * to what it is nested by, else NULL */
+       short nestlevel;                /* how many levels deep */
+       short topvisstaff;              /* top visible staff in range */
+       short botvisstaff;              /* bottom visible staff in range */
+} *Brac_info_p [MAXSTAFFS + 1];
+
+
+/* information about a label, either for a staff or group. */
+struct LABELINFO {
+       char    *label;         /* text of the label */
+       float   width;          /* strwidth(label) */
+       float   west;           /* relative distance of left edge of label
+                                * from the line between the labels and the
+                                * braces/brackets. This will be negative */
+       int     is_staff_label; /* YES for staff label, NO for group */
+       struct LABELINFO *next;/* linked list of labels at same y location */
+};
+
+/* information about all the labels that end up being printed left of a
+ * specific staff or between that staff and the one below it. */
+struct LABELLIST {
+       short staffno;                  /* which staff */
+       struct LABELINFO *label_p;      /* list of labels to be printed to
+                                        * the left of this staff */
+       struct LABELINFO *btwnlabel_p;  /* list of labels to be printed
+                                        * between this staff and the one
+                                        * below this staff */
+       short pad;                      /* how many levels of labels 
+                                        * have been put on this staff, either
+                                        * on the staff itself or on one or
+                                        * more other staffs that are
+                                        * grouped with this one */
+};
+static struct LABELLIST Labellist[MAXSTAFFS + 1];
+
+static short Numvis;           /* how many staffs currently visible */
+static short Maxlevels;                /* maximum number of nesting levels */
+static float Nested_brace_adjust = 0.0;        /* brace outside a bracket needs
+                                * some extra space to look good. */
+
+
+/* static functions */
+static void free_brac_info P((struct BRAC_INFO *brac_info_p));
+static void set_brac_info P((struct STAFFSET *staffset_p, int bractype));
+static int check_brac_overlap P((struct BRAC_INFO *brac_info_p));
+static void setnestlevel P((struct BRAC_INFO *brac_p,
+               struct BRAC_INFO *nested_by_p));
+static void place_labels P((struct MAINLL *mll_p,
+               struct MAINLL *prev_feed_mll_p));
+static void init_labellist P((void));
+static void free_label P((struct LABELINFO *label_p));
+static struct LABELINFO *newlabelinfo P((char *label, int is_staff_label));
+static void grouplabel P((struct BRAC_INFO *brac_p, int do_nested,
+               struct MAINLL *mll_p, struct MAINLL *prev_feed_mll_p));
+static double west_adjust P((struct MAINLL *mll_p,
+               struct MAINLL *prev_feed_mll_p));
+static struct MAINLL *find_prev_feed_mll_p P((struct MAINLL *mll_p));
+static char * label4staff P((struct MAINLL *mll_p, int s,
+               struct MAINLL *prev_feed_mll_p));
+static char * label4group P((struct MAINLL *mll_p, struct BRAC_INFO *brac_p,
+               struct MAINLL *prev_feed_mll_p));
+static double dflt_label_width P((struct MAINLL *mll_p,
+               struct MAINLL *prev_feed_mll_p));
+
+\f
+
+/* check for overlap between brace and bracket lists. Return YES if okay, NO
+ * if there is something illegal */
+
+int
+brac_check (bracelist_p, nbrace, bracklist_p, nbrack)
+
+struct STAFFSET *bracelist_p;
+int nbrace;                    /* how many items in bracelist_p */
+struct STAFFSET *bracklist_p;
+int nbrack;                    /* how many items in bracklist_p */
+
+{
+       register int s;         /* staff index into Brac_info_p */
+       register int n;         /* index into staffset */
+       int retval = 0;         /* return from check_brac_overlap() */
+       static int first_time = YES;    /* flag for if first time this function
+                                        * has been called */
+
+
+       debug(4, "brac_check");
+
+       /* initialize table */
+       for (s = 1; s <= Score.staffs; s++) {
+               if (first_time == NO) {
+                       /* only try to free if we know item has been properly
+                        * initialized, in case this is ever run on some system
+                        * that doesn't initialize pointer arrays to null ptrs */
+                       free_brac_info(Brac_info_p[s]);
+               }
+               Brac_info_p[s] = (struct BRAC_INFO *) 0;
+       }
+       first_time = NO;
+       Maxlevels = 0;
+
+       /* Go through each list, attaching each to table slot of its top staff.
+        */
+       for (n = 0; n < nbrace; n++) {
+               set_brac_info( &(bracelist_p[n]), BRACELIST);
+       }
+       for (n = 0; n < nbrack; n++) {
+               set_brac_info( &(bracklist_p[n]), BRACKLIST);
+       }
+
+       /* now check each staff for possible overlap */
+       for (s = 1; s <= Score.staffs; s++) {
+               if (Brac_info_p[s] == (struct BRAC_INFO *) 0) {
+                       /* no braces or brackets, so can't be any overlap */
+                       continue;
+               }
+
+               retval += check_brac_overlap (Brac_info_p[s]);
+       }
+
+       return(retval == 0 ? YES : NO);
+}
+\f
+
+/* recursively free a linked list of BRAC_INFO structs */
+
+static void
+free_brac_info(brac_info_p)
+
+struct BRAC_INFO *brac_info_p; /* the list to free */
+
+{
+       if (brac_info_p == (struct BRAC_INFO *) 0) {
+               return;
+       }
+
+       free_brac_info(brac_info_p->nested_p);
+       FREE(brac_info_p);
+}
+\f
+
+/* save information about a brace/bracket STAFFSET and link onto list for its
+ * top staff */
+
+static void
+set_brac_info (staffset_p, bractype)
+
+struct STAFFSET *staffset_p;   /* staffs to group together */
+int bractype;                  /* BRACELIST or BRACKLIST */
+
+{
+       struct BRAC_INFO *new_p;        /* info to be saved */
+       int s;                          /* staff num of top staff of staffset */
+
+
+       /* record information */
+       MALLOC(BRAC_INFO, new_p, 1);
+       new_p->staffset_p = staffset_p;
+       new_p->bractype = bractype;
+       new_p->nested_by_p = (struct BRAC_INFO *) 0;
+       new_p->nestlevel = 0;
+
+       /* link into list off of table */
+       s = staffset_p->topstaff;
+       new_p->nested_p = Brac_info_p[s];
+       Brac_info_p[s] = new_p;
+}
+\f
+
+/* check the brace/bracket information for one staff for overlap. Return
+ * number of errors found */
+
+static int
+check_brac_overlap (brac_info_p)
+
+struct BRAC_INFO *brac_info_p;
+
+{
+       register int s;
+
+
+       if (brac_info_p == (struct BRAC_INFO *) 0) {
+               /* end recursion */
+               return(0);
+       }
+
+       /* if no nesting, don't need to do those checks */
+       if (brac_info_p->nested_p != (struct BRAC_INFO *) 0) {
+
+               /* braces can't have anything nested inside them */
+               if (brac_info_p->bractype == BRACELIST) {
+                       yyerror("nesting inside a brace not allowed");
+                       return(1);
+               }
+
+               /* brace on top of bracket needs extra space */
+               if (brac_info_p->nested_p->bractype == BRACELIST) {
+                       Nested_brace_adjust = STEPSIZE;
+               }
+
+               /* check that nested range is a proper subset */
+               if (brac_info_p->nested_p->staffset_p->botstaff
+                                       >= brac_info_p->staffset_p->botstaff) {
+                       yyerror("nested brackets must be subsets of other brackets");
+                       return(1);
+               }
+
+               setnestlevel(brac_info_p->nested_p, brac_info_p);
+       }
+
+       /* see if this one overlaps with groups
+        * defined previously */
+       for (s = brac_info_p->staffset_p->topstaff + 1;
+                               s <= brac_info_p->staffset_p->botstaff; s++) {
+
+               if (Brac_info_p[s] == (struct BRAC_INFO *) 0) {
+                       continue;
+               }
+
+               /* if brace is being nested by something else,
+                * overlap is illegal */
+               if (brac_info_p->bractype == BRACELIST) {
+                       yyerror("brace overlap not allowed");
+                       return(1);
+               }
+
+               /* if bottom of this staffset is greater than bottom of the one
+                * we are checking, there is illegal overlap */
+               if (Brac_info_p[s]->staffset_p->botstaff 
+                                       > brac_info_p->staffset_p->botstaff) {
+                       yyerror("overlapping brackets are not nested");
+                       return(1);
+               }
+
+               /* remember who nests this one */
+               setnestlevel(Brac_info_p[s], brac_info_p);
+       }
+
+       /* recurse */
+       return (check_brac_overlap (brac_info_p->nested_p));
+}
+\f
+
+/* when one bracket is nested inside another, record that fact */
+
+static void
+setnestlevel(brac_p, nested_by_p)
+
+struct BRAC_INFO *brac_p;      /* set nesting here */
+struct BRAC_INFO *nested_by_p; /* brac_p is nested by this one */
+
+{
+       brac_p->nested_by_p = nested_by_p;
+       brac_p->nestlevel = nested_by_p->nestlevel + 1;
+
+       /* keep track of deepest nesting level */
+       if (brac_p->nestlevel > Maxlevels) {
+               Maxlevels = brac_p->nestlevel;
+       }
+}
+\f
+
+/*
+ * for each label
+ *     find which staff the label should go on based on visible
+ *
+ * Determine placement of staff labels, then nested, then outer.
+ */
+
+static void
+place_labels(mll_p, prev_feed_mll_p)
+
+struct MAINLL *mll_p;  /* current place in main list, used to determine
+                        * whether to use label or label2. */
+struct MAINLL *prev_feed_mll_p;        /* actual or proposed location of prev FEED */
+
+{
+       int s;                  /* index through staffs */
+       int count;              /* how many labels */
+       char *label;            /* the label being processed */
+       struct LABELINFO *lab_p;/* info about label */
+
+
+       init_labellist();
+       lab_p = (struct LABELINFO *) 0;
+
+       /* put the staff labels on the label list. While we're at it, count
+        * up the number of staffs that are currently visible */
+       for (count = Numvis = 0, s = 1; s <= Score.staffs; s++) {
+               if (svpath(s, VISIBLE)->visible == NO) {
+                       continue;
+               }
+
+               /* use label or label2 as appropriate */
+               if ((label = label4staff(mll_p, s, prev_feed_mll_p)) != 0) {
+                       lab_p = newlabelinfo(label, YES);
+               }
+
+               /* if there was a label, save info about it */
+               if (lab_p != (struct LABELINFO *) 0) {
+
+                       /* staff labels always go as far east as possible */
+                       /* Adjust by staffscale, but get from SSV, since
+                        * Stepsize won't be up to date. */
+                       lab_p->west = (-(lab_p->width) - STEPSIZE)
+                                       * svpath(s, STAFFSCALE)->staffscale;
+
+                       /* link onto list */
+                       lab_p->next = Labellist[Numvis].label_p;
+                       Labellist[Numvis].label_p = lab_p;
+
+                       /* count up number of staff labels */
+                       count++;
+
+                       /* re-init for next trip through loop */
+                       lab_p = (struct LABELINFO *) 0;
+               }
+
+               Labellist[Numvis].staffno = (short) s;
+
+               /* we now know there is one more staff visible */
+               Numvis++;
+       }
+
+       /* if there were any labels, mark all staffs as needing padding
+        * before placing another label. If there were no staff labels,
+        * group labels will go as far east as possible, otherwise the
+        * group labels will be leftward a bit. */
+       if (count > 0) {
+               for (s = 0; s < Numvis; s++) {
+                       (Labellist[s].pad)++;
+               }
+       }
+
+       /* do all nested group labels */
+       for (s = 1; s <= Score.staffs; s++) {
+               grouplabel(Brac_info_p[s], YES, mll_p, prev_feed_mll_p);
+       }
+
+       /* do all non-nested group labels */
+       for (s = 1; s <= Score.staffs; s++) {
+               grouplabel(Brac_info_p[s], NO, mll_p, prev_feed_mll_p);
+       }
+}
+\f
+
+/* initialize label list. Free any information currently in the list and
+ * mark everything as empty */
+
+static void
+init_labellist()
+
+{
+       register int s;         /* index through label list */
+
+
+       for (s = 0; s <= Numvis; s++) {
+               free_label(Labellist[s].label_p);
+               free_label(Labellist[s].btwnlabel_p);
+               Labellist[s].label_p = Labellist[s].btwnlabel_p
+                                               = (struct LABELINFO *) 0;
+               Labellist[s].pad = 0;
+       }
+       Numvis = 0;
+}
+\f
+
+/* recursively free linked list of LABELINFO structs */
+
+static void
+free_label(label_p)
+
+struct LABELINFO *label_p;     /* free this list */
+
+{
+       if (label_p == (struct LABELINFO *) 0) {
+               return;
+       }
+
+       free_label(label_p->next);
+       FREE(label_p);
+}
+\f
+
+/* allocate a new LABELINFO struct and fill in the label and width. Initialize
+ * west to zero */
+
+static struct LABELINFO *
+newlabelinfo(label, is_staff_label)
+
+char *label;           /* text of the label */
+int is_staff_label;    /* YES or NO */
+
+{
+       struct LABELINFO *new_p;        /* newly allocate place to save info */
+
+
+       MALLOC(LABELINFO, new_p, 1);
+       new_p->label = label;
+       new_p->west = 0.0;
+       new_p->width = strwidth(label);
+       new_p->is_staff_label = is_staff_label;
+       new_p->next = (struct LABELINFO *) 0;
+       return(new_p);
+}
+\f
+
+/* do placement of group labels */
+
+static void
+grouplabel(brac_p, do_nested, mll_p, prev_feed_mll_p)
+
+struct BRAC_INFO *brac_p;      /* info about group of staffs to do */
+int do_nested;                 /* if YES, process nested staff group. If NO,
+                                * process non-nested */
+struct MAINLL *mll_p;          /* used to decide if to use label or label2 */
+struct MAINLL *prev_feed_mll_p;        /* actual or proposed previous FEED */
+
+{
+       struct STAFFSET *staffset_p;    /* staffs/label in group */
+       char *label;                    /* label for group */
+       int index;                      /* into Labellist */
+       int topindex, botindex;         /* index into Labellist of where
+                                        * group range top & bottom visible
+                                        * staffs are */
+       int labindex;                   /* index into Labellist of staff where
+                                        * label should go */
+       struct LABELINFO *lab_p;        /* information about group label */
+       struct LABELINFO **lab_p_p;     /* where to insert label info */
+
+
+       if (brac_p == (struct BRAC_INFO *) 0) {
+               /* end recursion */
+               return;
+       }
+
+       if (do_nested == YES) {
+               /* recurse */
+               grouplabel(brac_p->nested_p, do_nested, mll_p, prev_feed_mll_p);
+               if (brac_p->nested_by_p == (struct BRAC_INFO *) 0) {
+                       return;
+               }
+       }
+       else if (brac_p->nested_by_p != (struct BRAC_INFO *) 0) {
+               return;
+       }
+
+       /* we'll probably need the staffset info a lot, so get pointer to it */
+       staffset_p = brac_p->staffset_p;
+
+       /* Find index in Labellist of top
+        * and bottom visible staffs of the range */
+       for (topindex = botindex = -1, index = 0;  index < Numvis; index++) {
+               if (topindex == -1 && staffset_p->topstaff
+                                               <= Labellist[index].staffno) {
+                       topindex = index;
+               }
+               if (staffset_p->botstaff >= Labellist[index].staffno) {
+                       botindex = index;
+               }
+       }
+
+       /* see if there were some visible staffs in this group */
+       if (topindex != -1 && botindex != -1 && botindex >= topindex) {
+
+               brac_p->topvisstaff = Labellist[topindex].staffno;
+               brac_p->botvisstaff = Labellist[botindex].staffno;
+
+               /* figure out which label to use, if any */
+               if ((label = label4group(mll_p, brac_p, prev_feed_mll_p))
+                                                       == (char *) 0) {
+                       return;
+               }
+
+               /* find index in list of visible staffs where label should
+                * go. If even number of visible staffs in range, label
+                * goes between two staffs */
+               labindex = (topindex + botindex) / 2;
+               if ((botindex - topindex) & 1) {
+                       lab_p_p = &(Labellist[labindex].btwnlabel_p);
+               }
+               else {
+                       lab_p_p = &(Labellist[labindex].label_p);
+               }
+
+               lab_p = newlabelinfo(label, NO);
+
+               /* put as far east as possible */
+               lab_p->west = - (lab_p->width);
+
+               lab_p->west -= Labellist[labindex].pad * LABELPAD;
+
+               /* link onto list */
+               lab_p->next = *lab_p_p;
+               *lab_p_p = lab_p;
+
+               /* add padding to all visible staffs in the group range */
+               for (    ; topindex <= botindex; topindex++) {
+                       Labellist[topindex].pad++;
+               }
+       }
+       else {
+               /* all staffs in group are invisible */
+               brac_p->topvisstaff = 0;
+       }
+}
+\f
+
+/* determine total width of labels. This is how much to add to
+ * relative west to get absolute location from left margin */
+
+static double
+west_adjust(mll_p, prev_feed_mll_p)
+
+struct MAINLL *mll_p;          /* actual or proposed FEED location,
+                                * used to decide if to use label or label2 */
+struct MAINLL *prev_feed_mll_p;        /* actual or proposed location of preceeding
+                                * FEED, used for label/label2 decision */
+
+{
+       register int s;         /* index */
+       double minwest = 0.0;   /* farthest west distance */
+
+
+       /* find westernmost label */
+       for (s = 0; s < Numvis; s++) {
+               if (Labellist[s].label_p != (struct LABELINFO *) 0) {
+                       if (Labellist[s].label_p->west < minwest) {
+                               minwest = Labellist[s].label_p->west;
+                       }
+               }
+               if (Labellist[s].btwnlabel_p != (struct LABELINFO *) 0) {
+                       if (Labellist[s].btwnlabel_p->west < minwest) {
+                               minwest = Labellist[s].btwnlabel_p->west;
+                       }
+               }
+       }
+
+       /* check for need to use default label on first score.
+        * If default label is needed, it creates an indent. */
+       if (minwest == 0.0) {
+               return(dflt_label_width(mll_p, prev_feed_mll_p));
+       }
+
+       return( - minwest);
+}
+\f
+
+/* return width of braces/brackets and their labels */
+
+double
+width_left_of_score(mll_p)
+
+struct MAINLL *mll_p;  /* FEED, used to decide if to use label or label2 */
+
+{
+       return(pwidth_left_of_score(mll_p, find_prev_feed_mll_p(mll_p)));
+}
+
+double
+pwidth_left_of_score(mll_p, prev_feed_mll_p)
+
+struct MAINLL *mll_p;          /* actual or proposed location of current FEED,
+                                * used to decide if to use label or label2 */
+struct MAINLL *prev_feed_mll_p;        /* actual or proposed location of prev FEED */
+
+{
+       double westadj;
+       int n;                  /* index through brac*lists */
+       int s;                  /* staff index */
+       int hasbracs;           /* YES if there are visible brackets/braces */
+
+
+       if (brac_check(Score.bracelist, Score.nbrace, Score.bracklist,
+                                       Score.nbrack) == NO) {
+               /* we should have exited before */
+               pfatal("illegal brace/bracket ranges");
+       }
+       /* call functions to determine the placement of all labels and
+        * save that information in the Labellist, then determine how
+        * wide the labels plus braces and brackets are */
+       place_labels(mll_p, prev_feed_mll_p);
+       westadj = west_adjust(mll_p, prev_feed_mll_p);
+
+       /* total is space for the labels (the westadj),
+        * the braces/brackets themselves (based on  Maxlevels),
+        * and 2 stepsizes of padding to left of score before brace/brack,
+        * plus special adjustment for brace on top of bracket, if any */
+       /* See if there are any visible brackets/braces.
+        * If so, we'll need to allow space for them, otherwise not. */
+       hasbracs = NO;
+       for (n = 0; n < Score.nbrace && hasbracs == NO; n++) {
+               for (s = Score.bracelist[n].topstaff;
+                                       s <= Score.bracelist[n].botstaff; s++){
+                       if (svpath(s, VISIBLE)->visible == YES) {
+                               hasbracs = YES;
+                               break;
+                       }
+               }
+       }
+       for (n = 0; n < Score.nbrack && hasbracs == NO; n++) {
+               for (s = Score.bracklist[n].topstaff;
+                                       s <= Score.bracklist[n].botstaff; s++){
+                       if (svpath(s, VISIBLE)->visible == YES) {
+                               hasbracs = YES;
+                               break;
+                       }
+               }
+       }
+
+       if (hasbracs == YES) {
+               return(westadj + ((Maxlevels + 2) * 2.0 * STDPAD)
+                               + (2.0 * STEPSIZE) + Nested_brace_adjust);
+       }
+       else {
+               return(westadj);
+       }
+}
+\f
+
+/* print braces/brackets and their labels, Return YES if there were braces or
+ * brackets, NO if not. */
+
+int
+pr_brac(is_restart, x_offset, mll_p)
+
+int is_restart;                /* YES if being called due to restart */
+double x_offset;       /* where to print, if is_restart == YES */
+struct MAINLL *mll_p;  /* for FEED for possible margin override, and to
+                        * decide if to use label or label2 */
+
+{
+       register int li;                /* index into Labellist */
+       register int s;                 /* staff index */
+       struct LABELINFO *lab_p;        /* info about a label */
+       struct LABELINFO *l_p;          /* for finding y adjust for overlaps */
+       double y_adjust;                /* for overlapping labels */
+       struct BRAC_INFO *brac_p;       /* info about brace or bracket */
+       double adj;                     /* how much to adjust relative west */
+       double x, y, y1;
+       int eff_stafflines;             /* how many stafflines there effectively
+                                        * are, counting the extra space around
+                                        * staffs with a very small number
+                                        * of stafflines */
+       double tab_adjust;              /* to adjust for TABRATIO */
+       double eff_stepsize;            /* STEPSIZE adjusted for staffscale */
+       char *label;
+       int printed_brac = NO;          /* if printed any braces/brackets */
+       struct MAINLL *prev_feed_mll_p; /* previous FEED */
+
+
+       debug(512, "pr_brac");
+
+       /* figure out where to place everything */
+       (void) brac_check(Score.bracelist, Score.nbrace, Score.bracklist,
+                                                               Score.nbrack);
+       prev_feed_mll_p = find_prev_feed_mll_p(mll_p);
+       place_labels(mll_p, prev_feed_mll_p);
+       if (is_restart == NO) {
+               adj = west_adjust(mll_p, prev_feed_mll_p)
+                                               + eff_leftmargin(mll_p);
+
+               /* print labels on visible staffs */
+               for (li = 0; li < Numvis; li++) {
+
+                       /* print labels to go by this staff */
+                       for (lab_p = Labellist[li].label_p;
+                                       lab_p != (struct LABELINFO *) 0;
+                                       lab_p = lab_p->next) {
+                               if (lab_p->is_staff_label == YES) {
+                                       /* Have to adjust by staffscale.
+                                        * We can't change the label
+                                        * in the SSV itself because that
+                                        * would cause problems, so make a copy
+                                        * and adjust that, then free it
+                                        * when we are done with it.
+                                        * Have to get size out of SSV,
+                                        * because Staffscale won't be
+                                        * up to date. */
+                                       MALLOCA(char, label, strlen(lab_p->label) + 1);
+                                       memcpy(label, lab_p->label,
+                                               strlen(lab_p->label) + 1);
+                                       resize_string(label,
+                                               svpath(Labellist[li].staffno,
+                                               STAFFSCALE)->staffscale,
+                                               (char *) 0, -1);
+                               }
+                               else {
+                                       label = lab_p->label;
+                               }
+
+                               x = lab_p->west + adj;
+                               /* Move above any other inner labels.
+                                * The very inner-most stays centered on the
+                                * staff, so ones above that have to be adjusted
+                                * by its ascent. This label itself has to
+                                * have enough room for half its height,
+                                * since it was originally centered,
+                                * for any between there, we need to skip
+                                * past their entire height.
+                                */
+                               for (y_adjust = 0.0, l_p = lab_p->next;
+                                               l_p != 0; l_p = l_p->next) {
+                                       if (l_p->next == 0) {
+                                               y_adjust += strascent(
+                                                       l_p->label) + STDPAD;
+                                       }
+                                       else {
+                                               y_adjust += strheight(
+                                                       l_p->label) + STDPAD;
+                                       }
+                               }
+                               if (lab_p->next != 0) {
+                                       y_adjust += strheight(lab_p->label)
+                                                       / 2.0 + STDPAD;
+                               }
+                               y = Staffs_y[ Labellist[li].staffno ]
+                                               + (strheight(label) / 2.0)
+                                               - strascent(label)
+                                               + y_adjust;
+                               pr_string(x, y, label, J_CENTER, (char *) 0, -1);
+                               if (lab_p->is_staff_label == YES) {
+                                       FREE(label);
+                               }
+                       }
+
+                       /* do labels that fall between staffs */
+                       for (lab_p = Labellist[li].btwnlabel_p;
+                                       lab_p != (struct LABELINFO *) 0;
+                                       lab_p = lab_p->next) {
+                               label = lab_p->label;
+                               x = lab_p->west + adj;
+                               /* y is the midpoint between the staffs,
+                                * adjusted by the height/ascent of the label,
+                                * and for any other labels. */
+                               for (y_adjust = 0.0, l_p = lab_p->next;
+                                               l_p != 0; l_p = l_p->next) {
+                                       if (l_p->next == 0) {
+                                               y_adjust += strascent(
+                                                       l_p->label) + STDPAD;
+                                       }
+                                       else {
+                                               y_adjust += strheight(
+                                                       l_p->label) + STDPAD;
+                                       }
+                               }
+                               if (lab_p->next != 0) {
+                                       y_adjust += strheight(lab_p->label)
+                                                       / 2.0 + STDPAD;
+                               }
+                               y = (Staffs_y[ Labellist[li].staffno ] +
+                                       Staffs_y[ Labellist[li+1].staffno ])/2.0
+                                       + (strheight(label) / 2.0)
+                                       - strascent(label) + y_adjust;
+                               pr_string(x, y, label, J_CENTER, (char *) 0, -1);
+                       }
+               }
+       }
+       else {
+               adj = - (Maxlevels * 2.0 * STDPAD);
+       }
+
+       /* print the braces and brackets themselves */
+       for (s = 1; s <= Score.staffs; s++) {
+               for (brac_p = Brac_info_p[s]; brac_p != (struct BRAC_INFO *) 0;
+                                               brac_p = brac_p->nested_p) {
+                       x = x_offset + adj + (Maxlevels - brac_p->nestlevel + 1)
+                                       * (2.0 * STDPAD) + (2.0 * STEPSIZE)
+                                       + Nested_brace_adjust;
+                       if (brac_p->bractype == BRACELIST) {
+                               if (brac_p->nested_by_p == 0) {
+                                       x += (0.5 * STEPSIZE);
+                               }
+                               else {
+                                       x -= (0.5 * STEPSIZE);
+                               }
+                       }
+                       if (brac_p->topvisstaff > 0) {
+                               /* figure out y (the top). Start at the y
+                                * of the top staff, then adjust as needed. */
+                               y = Staffs_y [brac_p->topvisstaff];
+
+                               /* figure out how tall the staff is effectively.
+                                * Staffs with only a few stafflines are
+                                * effectively taller than the number of
+                                * stafflines. */
+                               if ((eff_stafflines = svpath(
+                                               brac_p->topvisstaff, STAFFLINES)                                                ->stafflines) < 3) {
+                                       eff_stafflines = 3;
+                               }
+                               /* stepsizes are taller on tab staffs */
+                               tab_adjust = (is_tab_staff(brac_p->topvisstaff)
+                                                       ? TABRATIO : 1.0);
+
+                               /* adjust for height of staff */
+                               eff_stepsize = svpath(brac_p->topvisstaff,
+                                               STAFFSCALE)->staffscale
+                                               * STEPSIZE;
+                               y += (eff_stafflines - 1) * eff_stepsize
+                                               * tab_adjust;
+
+                               /* nested brackets should be a little shorter
+                                * vertically to fit inside their parent.
+                                * But beyond about 4 levels, if there is
+                                * only a single staff, things look
+                                * pretty bad, so limit to 4. */
+                               y -= (eff_stepsize * (brac_p->nestlevel < 5
+                                       ? brac_p->nestlevel : 4));
+
+                               /* brackets are 1 stepsize taller than braces */
+                               if (brac_p->bractype == BRACKLIST) {
+                                       y += eff_stepsize;
+                               }
+
+                               /* now calculate y1 (the bottom) by similar
+                                * means */
+                               y1 = Staffs_y [brac_p->botvisstaff];
+               
+                               /* figure out how tall the staff is effectively.
+                                * Staffs with only a few stafflines are
+                                * effectively taller than the number of
+                                * stafflines. */
+                               if ((eff_stafflines = svpath(
+                                               brac_p->botvisstaff, STAFFLINES)                                                ->stafflines) < 3) {
+                                       eff_stafflines = 3;
+                               }
+                               /* stepsizes are taller on tab staffs */
+                               tab_adjust = (is_tab_staff(brac_p->botvisstaff)
+                                                       ? TABRATIO : 1.0);
+
+                               /* adjust for height of staff */
+                               eff_stepsize = svpath(brac_p->botvisstaff,
+                                               STAFFSCALE)->staffscale
+                                               * STEPSIZE;
+                               y1 -= (eff_stafflines - 1) * eff_stepsize
+                                               * tab_adjust;
+
+                               /* nested brackets should be a little shorter
+                                * vertically to fit inside their parent.
+                                * But beyond about 4 levels, if there is
+                                * only a single staff, things look
+                                * pretty bad, so limit to 4. */
+                               y1 += (eff_stepsize * (brac_p->nestlevel < 5
+                                       ? brac_p->nestlevel : 4));
+
+                               /* brackets are 1 stepsize taller than braces */
+                               if (brac_p->bractype == BRACKLIST) {
+                                       y1 -= eff_stepsize;
+                               }
+
+                               /* now do the actual printing */
+                               do_pr_brac(x, y, y1, brac_p->bractype);
+                               printed_brac = YES;
+                       }
+               }
+       }
+
+       return(printed_brac);
+}
+\f
+
+/* Given one MAINLL pointing to a FEED, find the previous one.
+ * Many functions in this file need the previous feed. At abshorz time,
+ * there may not be an actual FEED yet, it might just be proposed,
+ * so functions at that time need to provide that proposed FEED place.
+ * Once all the FEEDs are determined, we can use this function to
+ * find the previous one.
+ */
+
+static struct MAINLL *
+find_prev_feed_mll_p(mll_p)
+
+struct MAINLL *mll_p;
+
+{
+       for (mll_p = mll_p->prev; mll_p != 0; mll_p = mll_p->prev) {
+               if (IS_CLEFSIG_FEED(mll_p)) {
+                       break;
+               }
+       }
+       return(mll_p);
+}
+\f
+
+/* Determine which label to use for a given staff.
+ * Goes backwards from mll_p, finding if label has been changed more recently
+ * than the previous feed. If so, use that label, else use label2.
+ */
+
+static char *
+label4staff(mll_p, s, prev_feed_mll_p)
+
+struct MAINLL *mll_p;  /* should point to an actual or proposed FEED location */
+int s;
+struct MAINLL *prev_feed_mll_p;        /* should point to an actual or proposed FEED location */
+
+{
+       for (mll_p = mll_p->prev; mll_p != 0; mll_p = mll_p->prev) {
+               if (mll_p == prev_feed_mll_p) {
+                       break;
+               }
+               if (mll_p->str == S_SSV) {
+                       struct SSV *ssv_p = mll_p->u.ssv_p;
+
+                       /* If user changed label for this staff in staff
+                        * context more recently that the previous feed,
+                        * then that's the label we need. */
+                       if (ssv_p->context == C_STAFF && ssv_p->staffno == s
+                                               && ssv_p->used[LABEL] == YES) {
+                               return(ssv_p->label);
+                       }
+
+                       /* If user changed the score-wide label
+                        * more recently than the previous feed,
+                        * but there isn't any label set in staff context for
+                        * this staff to override the score level label,
+                        * then the score level label is the one we need. */
+                       if (ssv_p->context == C_SCORE &&
+                                               ssv_p->used[LABEL] == YES &&
+                                               Staff[s-1].used[LABEL] == NO) {
+                               return(ssv_p->label);
+                       }
+               }
+       }
+       if (mll_p != 0) {
+               /* Hit another feed before any relevent label changes,
+                * so we need to use label2 */
+               return(svpath(s, LABEL2)->label2);
+       }
+       /* Ran off the top of the song. Use label */
+       return(svpath(s, LABEL)->label);
+}
+\f
+
+/* Given information about a set of grouped staffs,
+ * return the appropriate label to use: label or label2.
+ */
+
+static char *
+label4group(mll_p, brac_p, prev_feed_mll_p)
+
+struct MAINLL *mll_p;
+struct BRAC_INFO *brac_p;
+struct MAINLL *prev_feed_mll_p;
+
+{
+       for (mll_p = mll_p->prev; mll_p != 0; mll_p = mll_p->prev) {
+               if (mll_p == prev_feed_mll_p) {
+                       /* Hasn't changed since previous feed, so label2 */
+                       return(brac_p->staffset_p->label2);
+               }
+               if (mll_p->str == S_SSV && mll_p->u.ssv_p->context == C_SCORE &&
+                               mll_p->u.ssv_p->used[brac_p->bractype] == YES) {
+                       /* found SSV where brace/bracket was changed */
+                       break;
+               }
+       }
+       /* Either changed since previous feed or is the first feed in song,
+        * so use label. */
+       return(brac_p->staffset_p->label);
+}
+\f
+
+/* Return width of default label if the default label is needed (for
+ * indent of first score. Returns 0.0 if default label should not be used.
+ */
+
+static double
+dflt_label_width(mll_p, prev_feed_mll_p)
+
+struct MAINLL *mll_p;          /* points to FEED or proposed place
+                                * where current FEED will be */
+struct MAINLL *prev_feed_mll_p;        /* points to previous FEED, or proposed
+                                * place where prev FEED will be */
+
+{
+       char dfltlabel[16];
+
+
+       for (mll_p = mll_p->prev; mll_p != 0; mll_p = mll_p->prev) {
+               if (mll_p == prev_feed_mll_p) {
+                       /* not the first; so don't use default for first */
+                       return(0.0);
+               }
+
+               if (mll_p->str == S_SSV && mll_p->u.ssv_p->context == C_SCORE &&
+                                       mll_p->u.ssv_p->used[LABEL] == YES) {
+                       /* explicit label for first, so don't use default */
+                       return(0.0);
+               }
+       }
+       (void) sprintf(dfltlabel, "%c%c            ", FONT_TR, DFLT_SIZE);
+       return(strwidth(dfltlabel));
+}