X-Git-Url: https://git.distorted.org.uk/~mdw/mup/blobdiff_plain/cdb3c0882392596f814cf939cbfbd38adc6f2bfe..ddf6330b56bcfb657e0186b24b9b1422c51d3424:/mup/mup/brac.c diff --git a/mup/mup/brac.c b/mup/mup/brac.c new file mode 100644 index 0000000..44ca1f1 --- /dev/null +++ b/mup/mup/brac.c @@ -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)); + + + +/* 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); +} + + +/* 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); +} + + +/* 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; +} + + +/* 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)); +} + + +/* 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; + } +} + + +/* + * 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); + } +} + + +/* 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; +} + + +/* 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); +} + + +/* 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); +} + + +/* 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; + } +} + + +/* 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); +} + + +/* 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); + } +} + + +/* 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); +} + + +/* 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); +} + + +/* 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); +} + + +/* 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); +} + + +/* 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)); +}