/*************************************************
*    The PMW Music Typesetter - 3rd incarnation  *
*************************************************/

/* Copyright (c) Philip Hazel, 1991 - 2020 */

/* Written by Philip Hazel, starting November 1991 */
/* This file last modified: April 2020 */


/* This file contains code for splitting the music bars into systems and making
up pages from them. */


#include "pmwhdr.h"
#include "pagehdr.h"



enum { page_state_newmovt, page_state_newsystem,
       page_state_insystem, page_state_donesystem,
       page_state_donemovt };



/*************************************************
*          Pagination function                   *
*************************************************/

/* This function is called when the music has been successfully read in to
memory.

Arguments:  none
Returns:    nothing
*/

void
paginate_go(void)
{
int i;
int nextbarwidth;
int xposition;
int adjustkeyposition;
int adjusttimeposition;
int lengthwarn = 0;
int page_layoutptr = 0;
int save_notespacing[8];
int page_layoutstack[20];

int page_layoutstackptr = 0;
int page_state = page_state_newmovt;
BOOL page_done = FALSE;

DEBUG(("paginate_go() start\n"));

/* Font initialization */

font_reset();        /* To set no transformation */
font_xstretch = 0;   /* No justification */

/* Set up page and line lengths in magnified units, and compute the total
number of bars to be set. Once we have the line length, we can split and
justify heading and footing lines. */

main_pagelength = (main_truepagelength * 1000)/main_magnification;
main_totalbars = 0;

for (i = 1; i <= main_lastmovement; i++)
  {
  curmovt = movement[i];
  curmovt->linelength = (curmovt->truelinelength * 1000)/main_magnification;
  main_totalbars += curmovt->barcount;
  page_justifyheading(curmovt->heading);
  page_justifyheading(curmovt->footing);
  page_justifyheading(curmovt->lastfooting);
  }

/* Get store for the three data structures */

page_accepteddata = store_Xget(sizeof(pagedatastr));
page_nextdata     = store_Xget(sizeof(pagedatastr));
page_previousdata = store_Xget(sizeof(pagedatastr));

/* Other initialization */

page_barcount = 0;
page_movtnumber = 1;

/* Set up the first page block */

curpage = main_pageanchor = store_Xget(sizeof(pagestr));
curpage->next = NULL;
curpage->number = main_lastpage = main_firstpage;
curpage->spaceleft = main_pagelength;
curpage->overrun = 0;

curpage->sysblocks = NULL;
curpage->footing = NULL;
page_sysprevptr = &(curpage->sysblocks);
page_countsystems = 0;
page_lastsystem = NULL;

page_movtpending = FALSE;
curmovt = movement[page_movtnumber];
page_footing = page_footnotes = NULL;
page_footnotedepth = 0;

/* Set the spreading parameters for the first page */

page_botmargin = curmovt->botmargin;
page_justify = curmovt->justify;
page_topmargin = curmovt->topmargin;

/* Working clefs while measuring. The current clef is needed only for measuring
special keys, which may depend on the clef. We can't use the page_cont versions
because they are not updated until after we know which bars are to be included
in this system. */

page_sysclef = store_Xget(sizeof(uschar) * (MAX_STAVE + 1));

/* Loop that does the job; page_state controls which action is taken. */

while (!page_done) switch(page_state)
  {

  /****************************************************************************/

  /* Deal with the start of a movement. We deal with the heading lines and then
  set up for paginating the rest of the movement. */

  case page_state_newmovt:
  format_movt = curmovt;

  if (curmovt->barlinespace == (int)0x80000000)
    {
    page_barlinewidth = (curmovt->notespacing)[minim]/2 - 5000;
    if (page_barlinewidth < 3000) page_barlinewidth = 3000;
    curmovt->barlinespace = page_barlinewidth;
    }
  else page_barlinewidth = curmovt->barlinespace;

  memcpy(page_stavemap, curmovt->staves, STAVE_BITVEC_SIZE*sizeof(int));
  page_ulaysize = ((curmovt->fontsizes)->fontsize_text)[ff_offset_ulay];
  page_olaysize = ((curmovt->fontsizes)->fontsize_text)[ff_offset_olay];

  /* Deal with heading texts if we know the movement is to go on this page. If
  we are at the top of a page, do the page heading unless it has been turned
  off explicitly. Also, set up a footing for this page. */

  if (!page_movtpending)
    {
    if (page_movtnumber > 1 && curmovt->pageheading != NULL &&
      (curmovt->movt_opt & movt_nopageheading) == 0 &&
        curpage->spaceleft == main_pagelength)
          page_dopageheading(curmovt->pageheading);
    if (curmovt->heading != NULL) page_dopageheading(curmovt->heading);
    if (curmovt->footing != NULL) page_footing = curmovt->footing;
    }

  /* Create vector of per-bar data structures */

  curmovt->posvector =
    (barposstr *)store_Xget(curmovt->barcount * sizeof(barposstr)) - 1;

  /* Now set up to process the bars. Start by finding the highest possible
  stave number. If no staves are present in the movement, the result is -1. */

  page_lastwanted = -1;
    {
    usint x;
    for (i = STAVE_BITVEC_SIZE - 1; i >= 0; i--)
      {
      if ((x = curmovt->staves[i]) != 0)
        {
        page_lastwanted += i << 5;
        break;
        }
      }
    while (x != 0)
      {
      page_lastwanted++;
      if (page_lastwanted >= curmovt->laststave) break;
      x = x >> 1;
      }
    }

  /* Cut back the count of staves to those requested */

  curmovt->laststave = page_lastwanted;

  /* Initialize values in the accepted data structure */

  for (i = 0; i < STAVE_BITVEC_SIZE; i++)
    page_accepteddata->notsuspend[i] = curmovt->staves[i] & (~curmovt->suspend[i]);

  memcpy(page_accepteddata->notespacing, curmovt->notespacing, 8*sizeof(int));

  page_accepteddata->stavenames =
    store_Xget((page_lastwanted+1)*sizeof(uschar *));

  for (i = 1; i <= page_lastwanted; i++)
    {
    snamestr *sn = ((curmovt->stavetable)[i])->stave_name;
    page_accepteddata->stavenames[i] = sn;
    }

  /* Stavespacing is set only if it has been mentioned. If not, set the
  default. */

  if (curmovt->stave_spacing == NULL && page_lastwanted >= 0)
    {
    page_ssnext = store_Xget((page_lastwanted+1)*sizeof(int));
    page_ssnext[0] = 0;  /* No space for stave 0 */
    for (i = 1; i <= page_lastwanted; i++) page_ssnext[i] = 44000;
    }
  else page_ssnext = curmovt->stave_spacing;

  page_sgnext = curmovt->systemgap;

  /* Create vectors for {und,ov}erlay level handling */

  page_ulevel = store_Xget((page_lastwanted+1)*sizeof(int));
  page_ulhere = store_Xget((page_lastwanted+1)*sizeof(int));
  page_olevel = store_Xget((page_lastwanted+1)*sizeof(int));
  page_olhere = store_Xget((page_lastwanted+1)*sizeof(int));

  for (i = 0; i <= page_lastwanted; i++)
    page_ulevel[i] = page_olevel[i] = BIGNUMBER;

  /* The ensure spacing data is often NULL, but if not, set up separate "here"
  and "next" vectors. */

  page_ssenext = curmovt->stave_ensure;
  page_ssehere = (page_ssenext == NULL)? NULL :
    store_Xget((page_lastwanted+1)*sizeof(int));

  /* Set up the continuation data vector, in the default state */

  page_cont = store_Xget((page_lastwanted+1)*sizeof(contstr));
  for (i = 0; i <= page_lastwanted; i++)
    {
    contstr *p = page_cont + i;
    p->slurs = NULL;
    p->hairpin = NULL;
    p->nbar = NULL;
    p->ulay = NULL;
    p->tie = NULL;
    p->overbeam = NULL;
    p->tiex = 0;
    p->noteheadstyle = nh_normal;
    p->flags = cf_default;
    p->clef = clef_treble;
    p->time = curmovt->time;
    p->key = transpose_key(curmovt->key, curmovt->transpose);
    }

  /* Final set up */

  if (curmovt->startnotime) mac_initstave(page_showtimes, 0); else
    {
    mac_initstave(page_showtimes, ~0);
    mac_clearstave(page_showtimes, 0);
    }

  page_firstsystem = TRUE;
  page_startline = curmovt->startline;
  page_state = page_state_newsystem;

  /* Set up for fixed layout if required */

  if (curmovt->layout == NULL) page_layoutptr = -1; else
    {
    page_layoutstack[0] = BIGNUMBER;
    page_layoutstackptr = 1;
    page_layoutptr = 0;

    while (curmovt->layout[page_layoutptr++] == lv_repeatcount)
      page_layoutstack[page_layoutstackptr++] = curmovt->layout[page_layoutptr++];
    }

  /* The left and right justification bits can be set immediately, so that they
  apply to all systems in the new movement. The top and bottom bits can't be
  changed yet, because we don't know if this movement will start on the current
  page. */

  page_justifyLR = curmovt->justify;

  /* Initialize the bar number and deal with the case of no bars of music in
  the movement. */

  if ((page_barnumber = 1) > curmovt->barcount)
    {
    page_botmargin = curmovt->botmargin;    /* Normally set when we know */
    page_justify = curmovt->justify;        /* that the first system fits */
    page_topmargin = curmovt->topmargin;    /* on the page. This code copes */
    page_state = page_state_donemovt;       /* with a single movement file */
    }
  break;




  /****************************************************************************/

  /* Deal with the start of a new system. There will always be at least one bar
  left when control gets here. */

  case page_state_newsystem:
    {
    int keyxposition = 0;
    int timexposition = 0;
    int timewidth = 0;
    int Cmajor = TRUE;

    page_newpagewanted = FALSE;                       /* initialize flags */
    page_layout_stretchn = page_layout_stretchd = 1;  /* first time; no stretch */
    if (page_ssenext != NULL)
      memcpy(page_ssehere, page_ssenext, (page_lastwanted+1)*sizeof(int));

    /* If page_ssenext is NULL, but page_sshere is not, it means that
    page_ssehere was set up in the previous system by [ssabove]. */

    else if (page_ssehere != NULL)
      {
      store_free(page_ssehere);
      page_ssehere = NULL;
      }

    /* Get a new system block and initialize */

    page_sysblock = store_Xget(sizeof(sysblock));
    page_sysblock->next = NULL;
    page_sysblock->type = sh_system;
    page_sysblock->flags = 0;
    page_sysblock->movt = curmovt;
    memcpy(page_sysblock->notsuspend, page_accepteddata->notsuspend,
      STAVE_BITVEC_SIZE*sizeof(int));
    mac_initstave(page_sysblock->showtimes, 0);
    page_sysblock->barstart = page_barnumber;
    page_sysblock->barend = page_barnumber;
    page_sysblock->stavenames = page_accepteddata->stavenames;
    page_sysblock->stavespacing = page_ssnext;
    page_sysblock->systemgap = page_sgnext;

    page_sysblock->cont = misc_copycontstr(page_cont, page_lastwanted, TRUE);

    /* Set up working clefs at start of system; these are needed only for use
    when measuring the widths of special key signatures, which may depend on
    the clef. We can't use the page_cont values because they don't get updated
    dynamically ('cause we don't want to go past the bars we accept). */

    for (i = 1; i <= page_lastwanted; i++)
      page_sysclef[i] = page_sysblock->cont[i].clef;

    page_sysblock->ulevel = store_Xget((page_lastwanted+1) * sizeof(int));
    page_sysblock->olevel = store_Xget((page_lastwanted+1) * sizeof(int));

    for (i = 0; i <= page_lastwanted; i++)
      {
      contstr *c = page_cont + i;
      if (c->overbeam != NULL)     /* Clear continued beams for next system */
        {
        store_free(c->overbeam);
        c->overbeam = NULL;
        }

      page_ulhere[i] = page_olhere[i] = 0;
      page_sysblock->ulevel[i] = -page_ulaysize - 1000;
      page_sysblock->olevel[i] = 20000;
      }

    /* Update the current key/time signatures if necessary */

    page_setsignatures();

    /* Initialize those fields of the current data structure that are reset for
    each system */

    page_accepteddata->endkey = FALSE;
    page_accepteddata->endtime = FALSE;
    page_accepteddata->endbar = page_barnumber - 1;
    page_accepteddata->count = 0;

    /* Save the initial notespacing so that it can be restored for re-spacing
    bars when there's a big stretch factor. */

    memcpy(save_notespacing, page_accepteddata->notespacing, 8*sizeof(int));

    /* If all staves are suspended (can happen during part extraction with the
    use of [newline] or with S! bars) unsuspend the lowest numbered stave. If
    any staves get resumed in the system, this gets undone again later. */

    if (!mac_anystave(page_sysblock->notsuspend)) for (i = 0; i < STAVE_BITVEC_SIZE; i++)
      {
      if (curmovt->staves[i] != 0)
        {
        page_sysblock->notsuspend[i] = curmovt->staves[i] & (-curmovt->staves[i]);
        break;
        }
      }

    /* Find the starting position of the stave */

    page_accepteddata->startxposition = page_startwidth(page_accepteddata,
      page_stavemap, page_sysblock->notsuspend);

    /* Compute position for key signatures. We make them all line up
    vertically. The top bit in the stavelines value indicates no clefs are ever
    printed. This is to support the old [percussion] directive (which pre-dates
    [noclef]). For the moment, the value generated is relative to
    startxposition. */

    for (i = 1; i <= page_lastwanted; i++)
      {
      stavestr *ss = curmovt->stavetable[i];
      if (mac_teststave2(page_stavemap, page_sysblock->notsuspend, i) &&
          (!ss->omitempty || (ss->barindex)[page_barnumber] != NULL))
        {
        mac_setstavesize(i);
        if ((curmovt->stavetable[i])->stavelines < 127)
          {
          int clef = (page_sysblock->cont[i]).clef;
          int xpos =
            curmovt->clefwidths[clef] * main_stavemagn + page_startline->clefspace;
          if (xpos > keyxposition) keyxposition = xpos;
          }
        }
      }

    /* Compute the position for time signatures after the widest key signature
    (again relative to startxposition). Again, the top stavelines bit
    suppresses key signatures. */

    for (i = 1; i <= page_lastwanted; i++)
      {
      if (mac_teststave2(page_stavemap, page_sysblock->notsuspend, i))
        {
        mac_setstavesize(i);

        if ((curmovt->stavetable[i])->stavelines < 127)
          {
          int key = (page_sysblock->cont[i]).key;
          int xpos = keyxposition +
            (main_stavemagn * misc_keywidth(key, page_sysblock->cont[i].clef))/1000;
          if (xpos > timexposition) timexposition = xpos;
          if (key != 2) Cmajor = FALSE;
          }
        }
      }

    /* If at least one key signature is not C major, insert extra space
    before the key position. */

    if (!Cmajor)
      {
      keyxposition += page_startline->keyspace;
      timexposition += page_startline->keyspace;
      }

    page_sysblock->keyxposition = keyxposition;

    /* If all staves are [percussion] staves, no clefs or keys get written.
    This can also happen for [noclef] staves with no key signature. In this
    case we need to put a little bit of space before the time signature or
    first note. */

    if (timexposition == 0) timexposition += 2000;

    /* We are now at the position for the time signature, or first note if
    there is no time signature. */

    if (mac_anystave(page_showtimes))
      {
      timexposition += page_startline->timespace;
      for (i = 1; i <= page_lastwanted; i++)
        {
        if (mac_teststave2(page_stavemap, page_sysblock->notsuspend, i))
          {
          int tw;
          mac_setstavesize(i);
          tw = (main_stavemagn *
            misc_timewidth((page_sysblock->cont[i]).time))/1000;
          if (tw > timewidth) timewidth = tw;
          }
        }
      memcpy(page_sysblock->showtimes, page_showtimes, STAVE_BITVEC_SIZE * sizeof(int));
      mac_initstave(page_showtimes, 0);
      }

    page_sysblock->timexposition = timexposition;

    /* Set the first note position */

    page_sysblock->firstnoteposition =
      timexposition + timewidth + PAGE_LEFTBARSPACE + page_startline->notespace;

    /* The xposition is an absolute position, used during calculations */

    page_accepteddata->xposition =
      page_accepteddata->startxposition + page_sysblock->firstnoteposition;

    /* Initialize start of line flags and accidental space insertion flag */

    page_startlinebar = TRUE;        /* becomes FALSE after one acceptance */
    page_lastendwide = FALSE;        /* at start of line */
    page_lastenddouble = FALSE;      /* ditto */

    /* Enter the mid-system state */

    page_state = page_state_insystem;
    }
  break;




  /****************************************************************************/

  /* In the middle of a system - measure the next bar and see if it will fit.
  It it doesn't, and it starts with a key or time signature and there is not
  even enough room for that, we have to back off from accepting the previous
  bar. When the system is full, change state again. */

  case page_state_insystem:

  /* Make copy of current status for MakePosTable to update */

  *page_nextdata = *page_accepteddata;

  /* Measure the bar -- this also sets various flags such as page_warnkey and
  also sets page_xxwidth if there is a key and/or time signature. The
  lengthwarn variable is normally zero (or less), but is set to 1 after the end
  of a system so that re-measuring the bar, for the next system, doesn't give a
  length warning again. If we back up two bars, in order to fit in a key/time
  signature, lengthwarn is set to 2. */

  nextbarwidth = page_makepostable(lengthwarn-- < 1);

  /* Compute position if bar were accepted */

  xposition = page_accepteddata->xposition + nextbarwidth;

  /* If a stave has been resumed in this bar, or if a stave name has changed,
  it may be necessary to change the width of the stave name space (i.e.
  startxposition). This may make the bar unacceptable.

  We must also check the clef of the resumed stave, if it has a bar to print at
  the start of the system, because a wider clef will alter the position of the
  key signature.

  Likewise the key signature of the resumed stave, because a wider key
  signature will alter the position of the time signature. */

  adjustkeyposition = 0;
  adjusttimeposition = 0;

  if (mac_diffstave(page_nextdata->notsuspend, page_accepteddata->notsuspend) ||
      page_nextdata->stavenames != page_accepteddata->stavenames)
    {
    int newkeyxposition = 0;
    int newtimexposition = 0;

    /* Deal with stave names */

    int newstartx = page_startwidth(page_nextdata,
      page_stavemap, page_nextdata->notsuspend);
    page_nextdata->startxposition = newstartx;
    xposition += newstartx - page_accepteddata->startxposition;

    /* Deal with change of key signature position */

    for (i = 1; i <= page_lastwanted; i++)
      {
      stavestr *ss = curmovt->stavetable[i];
      if (mac_teststave2(page_stavemap, page_nextdata->notsuspend, i) &&
          (!ss->omitempty || (ss->barindex)[page_sysblock->barstart] != NULL))
        {
        mac_setstavesize(i);
        if ((curmovt->stavetable[i])->stavelines < 127)
          {
          int clef = (page_sysblock->cont[i]).clef;
          int key = (page_sysblock->cont[i]).key;
          int xp =
            curmovt->clefwidths[clef] * main_stavemagn + page_startline->clefspace;
          if (xp > newkeyxposition) newkeyxposition = xp;
          xp = newkeyxposition +
            (main_stavemagn * misc_keywidth(key, clef))/1000;
          if (xp > newtimexposition) newtimexposition = xp;
          }
        }
      }

    if (newkeyxposition > page_sysblock->keyxposition)
      {
      adjustkeyposition = newkeyxposition - page_sysblock->keyxposition;
      xposition += adjustkeyposition;
      }

    if (newtimexposition > page_sysblock->timexposition + adjustkeyposition)
      {
      adjusttimeposition = newtimexposition - page_sysblock->timexposition -
        adjustkeyposition;
      xposition += adjusttimeposition;
      }
    }

  /* Default overrun is "infinity" */

  page_sysblock->overrun = 255;

  /* If this is not the first bar on the line, see if it will fit. We always
  accept one bar - it gets squashed (with a warning). If a fixed layout has
  been specified, accept bars until we have one more than required. */

  if ((page_layoutptr < 0 && page_accepteddata->count > 0 &&
        xposition > curmovt->linelength) ||
      (page_layoutptr >= 0 &&
        page_accepteddata->count >= curmovt->layout[page_layoutptr]))
    {
    int overrun = xposition - curmovt->linelength;
    lengthwarn = 1;       /* Don't warn when we reprocess the bar */

    /* See if cautionary signature(s) are needed. */

    if (!page_startlinebar && (page_warnkey || page_warntime))
      {
      xposition = page_accepteddata->xposition + page_xxwidth;

      /* If there is not even enough space for the end-of-line signature(s),
      back up to the previous bar, unless there isn't one to back up to, in
      which case give an overlong line warning. */

      if (page_layoutptr < 0 && xposition > curmovt->linelength)
        {
        if (page_accepteddata->count > 1)
          {
          pagedatastr *temp = page_previousdata;
          page_previousdata = page_accepteddata;
          page_accepteddata = temp;
          page_barcount -= page_lastbarcountbump;
          page_barnumber -= page_lastbarcountbump;
          overrun = xposition - curmovt->linelength;
          lengthwarn++;   /* Two bars not to warn for */
          }
        else
          {
          error_moan(ERR56, page_previousdata->endbar);
          overrun = 25500;
          }
        }

      /* Room for key/time -- set flag(s) and xposition */

      else
        {
        page_accepteddata->xposition = xposition + page_barlinewidth;
        page_accepteddata->endkey = page_warnkey;
        page_accepteddata->endtime = page_warntime;
        }
      }

    /* Set up the overrun value as a whole number of points, rounded up */

    overrun = (overrun + 999)/1000;
    if (overrun > 255) overrun = 255;
    page_sysblock->overrun = (uschar)overrun;

    /* In all cases we've finished the system */

    page_state = page_state_donesystem;
    }

  /* There is room on the line for this bar, or it is the first bar on the
  line, so accept it, first freeing any vectors that previous points to which
  are no longer needed. Give a warning for an over- flowing single bar, which
  will be squashed to fit. */

  else
    {
    pagedatastr *temp = page_previousdata;
    page_previousdata = page_accepteddata;
    page_accepteddata = page_nextdata;
    page_nextdata = temp;

    if (page_accepteddata->count == 0 && xposition > curmovt->linelength)
      {
      error_moan(ERR55, page_accepteddata->endbar + 1, nextbarwidth,
        xposition-nextbarwidth, curmovt->linelength);
      }

    /* If this bar starts with a key or time change, then bump the previous x
    position to account for it, as that will be what happens if we have to back
    off. */

    if (page_xxwidth > 0 && !page_startlinebar)
      {
      page_previousdata->xposition += page_xxwidth + page_barlinewidth;
      page_previousdata->endkey = page_warnkey;
      page_previousdata->endtime = page_warntime;
      }
    else
      {
      page_previousdata->endkey = FALSE;
      page_previousdata->endtime = FALSE;
      }

    /* Allow for key & time signature positioning adjustment */

    if (adjustkeyposition || adjusttimeposition)
      {
      page_sysblock->keyxposition += adjustkeyposition;
      page_sysblock->timexposition += adjustkeyposition + adjusttimeposition;
      page_sysblock->firstnoteposition += adjustkeyposition + adjusttimeposition;
      }

    /* Update xposition, endbar number, and counts */

    page_accepteddata->xposition = xposition + page_barlinewidth;

    if (page_manyrest >= 2)
      {
      page_accepteddata->endbar += page_manyrest;
      page_barcount += page_manyrest;                /* total count */
      page_barnumber += page_manyrest;
      page_lastbarcountbump = page_manyrest;              /* for backing off */
      }
    else
      {
      page_accepteddata->endbar++;
      page_barcount++;
      page_barnumber++;
      page_lastbarcountbump = 1;
      }

    page_accepteddata->count += 1;                   /* counts printed bars */
    page_startlinebar = FALSE;

    if (page_barnumber > curmovt->barcount) page_state = page_state_donesystem;
    }
  break;




  /****************************************************************************/

  /* Completed a system - tidy the data structures and see if it fits onto the
  current page. The positions at the start of the line can be made absolute now
  that startxposition is known. We also perform the stretching operation on the
  position tables in the bars at this point. */

  case page_state_donesystem:
    {
    int startxposition = page_accepteddata->startxposition;
    int barlinewidth = page_barlinewidth;
    int justbits, stretchn, stretchd, sysdepth, sysfootdepth;

    page_sysblock->stavenames = page_accepteddata->stavenames;
    page_sysblock->barend = page_accepteddata->endbar;

    /* If there are no unsuspended staves in the accepted data, then leave the
    sysblock alone, as it will have had one stave forced into it. Otherwise,
    overwrite with the accepted value, thereby turning off the fudged stave if
    there was one. */

    if (mac_anystave(page_accepteddata->notsuspend))
      for (i = 0; i < STAVE_BITVEC_SIZE; i++)
        page_sysblock->notsuspend[i] = page_accepteddata->notsuspend[i];

    /* Fix various initial positions */

    page_sysblock->startxposition = startxposition;
    page_sysblock->joinxposition = startxposition;
    page_sysblock->keyxposition += startxposition;
    page_sysblock->timexposition += startxposition;
    page_sysblock->firstnoteposition += startxposition;

    if (page_accepteddata->endkey) page_sysblock->flags |= sysblock_warnkey;
    if (page_accepteddata->endtime) page_sysblock->flags |= sysblock_warntime;

    /* Advance the continuation data to the end of the system, ready for the
    next one. This scan also handles changes of stave and system spacing, and
    local justification. Because we do not yet know if this system is going to
    fit on the page, any vertical justification changes that it makes are
    placed in page_sys_xxx variables. We initialize them to negative numbers to
    detect changes.

    There is an unfortunate chicken-and-egg situation here. We need to set the
    barlinewidth, as it is used when computing beam slopes for beams that cross
    barlines. It should really be set to the stretched value, but we can't
    compute the stretching factor until we've done the barcont stuff, in order
    to know if we have to justify or not. We cheat by setting it to the
    unstretched value and hoping that is near enough... */

    page_sysblock->barlinewidth = barlinewidth;
    page_sys_topmargin = page_sys_botmargin = page_sys_justify = -1;

    /* The page_setcont() routine also collects footnotes and system notes. We
    initialize the various variables before calling it. */

    page_newfootnotes = NULL;
    page_newfootnotedepth = 0;
    page_setcont();

    /* Set up the xposition of the end of the line and the justify bits, and
    compute the spreading parameters. We spread if the line is wider than a
    proportion of the linewidth as set by the stretchthresh variables, or if
    it's too long (when the "spreading" is actually squashing). Note that
    left/right justification bits are taken from this system's flags, if there
    were any. */

    xposition = page_accepteddata->xposition - barlinewidth;
    justbits = page_justifyLR & (just_left + just_right);

    /* Left + right justification */

    if ((justbits == just_left + just_right &&
      xposition - page_sysblock->startxposition >
        (main_stretchthreshnum*
          (curmovt->linelength - page_sysblock->startxposition)) /
            main_stretchthreshden) ||
              xposition > curmovt->linelength)
      {
      int save_xxwidth = page_xxwidth;
      int xxadjust = page_sysblock->firstnoteposition +
        ((page_accepteddata->endkey || page_accepteddata->endtime)?
          page_xxwidth + barlinewidth : 0);

      stretchn = curmovt->linelength - xxadjust;
      stretchd = xposition - xxadjust;

      page_sysblock->xjustify = 0;
      page_sysblock->flags |= sysblock_stretch;

      /* If the stretching factor is large enough, throw away the position
      tables and re-format all the bars using the known stretching factor. They
      should not get any wider. Then compute revised stretching factors. Repeat
      if necessary, up to 4 times. Note that we have to keep re-stretching the
      barlinewidth.

      From release 4.22 we also do this when the stretching is actually
      squashing by a large enough amount, which can happen when the layout
      directive forces more bars onto a line than would normally fit. This is
      necessary when there is underlay, where words might crash when a bar is
      squashed. (While testing this, it turns out that the squashing version
      also sometimes kicks in after a stretching time round the loop, which
      sometimes overdoes things, it seems. It does no harm.)

      There's an option to prevent this new logic from happening, for old
      files. It currently selects between three states: do nothing, do it only
      for over-stretching, do it for both over-stretching and squashing. */

      page_layout_stretchn = stretchn;
      page_layout_stretchd = stretchn;   /* sic - see below - it changes cumulatively */

      i = 0;
      while (i++ < 4 &&
             (
             (mac_muldiv(stretchd, 1000, stretchn) > main_stretchrespacethresh &&
               opt_stretchrule >= 2) ||
             (mac_muldiv(stretchn, 1000, stretchd) > main_stretchrespacethresh &&
               opt_stretchrule >= 1)
             ))
        {
        int j;
        page_layout_stretchd = mac_muldiv(page_layout_stretchd, stretchd, stretchn);
        xposition = page_sysblock->firstnoteposition;
        page_startlinebar = TRUE;        /* becomes FALSE after one acceptance */
        page_lastendwide = FALSE;        /* at start of line */
        page_lastenddouble = FALSE;      /* ditto */

        barlinewidth = mac_muldiv(barlinewidth, stretchn, stretchd);
        memcpy(page_nextdata->notespacing, save_notespacing, 8*sizeof(int));

        /* Reset clefs at system start */

        for (j = 1; j <= page_lastwanted; j++)
          page_sysclef[j] = page_sysblock->cont[j].clef;

        for (page_barnumber = page_sysblock->barstart;
             page_barnumber <= page_sysblock->barend;
             page_barnumber++)
          {
          barposstr *bp = curmovt->posvector + page_barnumber;
          store_free(bp->vector);
          xposition += page_makepostable(FALSE) + barlinewidth;
          page_startlinebar = FALSE;
          if (page_manyrest >= 2) page_barnumber += page_manyrest - 1;
          }

        xposition -= barlinewidth;
        if (page_accepteddata->endkey || page_accepteddata->endtime)
          xposition += save_xxwidth + curmovt->barlinespace;  /* Unstretched barlinewidth */
        stretchd = xposition - xxadjust;

        if (main_tracepos == (-1) ||
            (page_sysblock->barstart <= main_tracepos &&
             page_sysblock->barend >= main_tracepos))
          debug_printf("i=%d bars %d-%d old=%d new=%d\n", i,
            page_sysblock->barstart, page_sysblock->barend,
              mac_muldiv(page_layout_stretchn, 1000, page_layout_stretchd),
                mac_muldiv(stretchn, 1000, stretchd));
        }
      }

    /* Deal with right only or no justification */

    else
      {
      stretchn = stretchd = 1;
      if ((justbits & just_left) == 0)
        {
        int xjustify = curmovt->linelength - xposition;
        if (justbits == 0) xjustify /= 2;
        page_sysblock->xjustify = xjustify;
        }
      else page_sysblock->xjustify = 0;
      }

   /* The barline width for the system is the final stretched value. */

    page_sysblock->barlinewidth = mac_muldiv(barlinewidth, stretchn, stretchd);

    /* Now apply the stretching operation to the bars in the system. Key and
    time signatures and left repeats at the start of a bar are not stretched.
    Grace notes are kept at the same distance from their successors. */

    for (i = page_sysblock->barstart; i <= page_sysblock->barend; i++)
      {
      barposstr *bp = curmovt->posvector + i;
      posstr *p = bp->vector;
      int count = bp->count;

      /* If this is the first bar of a multi-rest, make a correction to the
      value of i to skip the others. */

      i += bp->multi - 1;

      /* Skip over any clefs, key signatures or time signatures at the start of
      the bar. For big stretches, it is in fact not enough to do this, as the
      stretched barlinewidth can make their positioning look silly. We move
      them to the left in this case.

      This is probably less relevant now that we re-lay-out lines to get the
      stretching factor down. */

      /***** PRO TEM remove fix to retain previous state pending revised
      stretching. In this state, clefs are not tested here (they don't normally
      occur at line starts). We need a revised stretching algorithm to keep the
      first note fixed even after clefs, keys, and times. ****/

      if ((p->moff <= posx_timelast && p->moff >= posx_keyfirst))  /*** || p->moff == posx_clef) ***/
        {
        int n = 9;
        while ((count > 0 && p->moff >= posx_keyfirst && p->moff <= posx_timelast) ||
               p->moff == posx_clef)
          {
          p->xoff -= ((page_sysblock->barlinewidth - page_barlinewidth)*n)/10;
          if (n > 2) n -= 2;
          p++;
          count--;
          }
        }

      /* Else skip over any grace notes and accidentals, and also the first
      note, which we do not want to move. But if there is nothing in the bar,
      don't skip over the first (= last) item. */

      else
        {
        while (count > 0 && p->moff < 0) { p++; count--; }
        if (count > 1) { p++; count--; }
        }

      /* Now stretch the remaining items, dealing specially with grace notes,
      which are identified by finding the next full note and checking the
      offset. Also deal specially with clefs. */

      while (count-- > 0)
        {
        int old;
        posstr *pp = p;

        while (count > 0 && (pp+1)->moff - pp->moff <= -posx_max)
          {
          pp++;
          count--;
          }

        old = pp->xoff;
        pp->xoff = mac_muldiv(pp->xoff, stretchn, stretchd);

        while (p < pp)
          {
          int d = -(pp->moff - p->moff);
          int rightmost = p->xoff + pp->xoff - old;
          int leftmost = mac_muldiv(p->xoff, stretchn, stretchd);

          /* Clef positions are stretched just a bit if they are the last thing
          in the bar. Otherwise, the position used is halfway between an
          unstretched and stretched position. */

          if (d == posx_clef)
            p->xoff = (count == 0)? rightmost - (rightmost - leftmost)/5 :
              (rightmost+leftmost)/2;

          /* Grace notes are never stretched at all; other things are stretched
          a bit, but not the full amount. */

          else if (d >= posx_gracefirst && d <= posx_gracelast) p->xoff = rightmost;
            else p->xoff = (rightmost + leftmost)/2;
          p++;
          }

        p++;
        }
      }

    /* If this was the first system of a movement, re-compute the stave name
    structure to use the second name. Also, if an indent is set for the
    brackets and braces, adjust the position of the joining signs. */

    if (page_firstsystem)
      {
      int j;
      page_firstsystem = FALSE;

      page_accepteddata->stavenames =
        store_Xget((page_lastwanted+1)*sizeof(uschar *));
      for (j = 1; j <= page_lastwanted; j++)
        {
        snamestr *sn = ((curmovt->stavetable)[j])->stave_name;
        page_accepteddata->stavenames[j] = (sn != NULL)? sn->next : NULL;
        }

      if (curmovt->startbracketbar >= page_sysblock->barstart &&
          curmovt->startbracketbar <  page_sysblock->barend)
        {
        int blw = 0;
        page_sysblock->joinxposition = page_sysblock->firstnoteposition;
        for (i = page_sysblock->barstart; i <= curmovt->startbracketbar; i++)
          {
          barposstr *bp = curmovt->posvector + i;
          posstr *p = bp->vector;
          page_sysblock->joinxposition += p[bp->count-1].xoff + blw;
          blw = page_sysblock->barlinewidth;
          }
        }
      }

    /* Check that the stavespacing vector conforms to the ensure values, and if
    not, make a new one that does. At the same time, compute the total depth of
    the system. If an unsuspended stave has a zero stave spacing, make sure
    that the following stave is not suspended. */

    sysdepth = 0;

    for (i = 1; i <= page_lastwanted; i++)
      {
      if (mac_teststave2(page_stavemap, page_sysblock->notsuspend, i))
        {
        int j = i;
        int next = i+1;

        while (j < page_lastwanted && page_sysblock->stavespacing[j++] == 0)
          {
          mac_setstave(page_sysblock->notsuspend, j);
          }

        while (next <= page_lastwanted &&
          mac_testNstave2(page_stavemap, page_sysblock->notsuspend, next))
            next++;

        if (next <= page_lastwanted)
          {
          if (page_ssehere != NULL &&
            page_sysblock->stavespacing[i] < page_ssehere[next])
              {
              if (page_sysblock->stavespacing == page_ssnext)
                page_sysblock->stavespacing = store_copy(page_ssnext);
              page_sysblock->stavespacing[i] = page_ssehere[next];
              }
          sysdepth += page_sysblock->stavespacing[i];
          }

        page_lastulevel = page_sysblock->ulevel[i];
        }
      }
    page_sysblock->systemdepth = sysdepth;

    /* Compute a testing depth consisting of the system depth plus the total
    depth of any footnotes, and space below the current system. And space
    between the current footnotes and any new ones. */

    sysfootdepth = sysdepth + page_footnotedepth + page_newfootnotedepth;
    if (page_footnotedepth + page_newfootnotedepth > 0)
      {
      sysfootdepth += - page_lastulevel;
      if (page_footnotedepth > 0 && page_newfootnotedepth > 0)
        sysfootdepth += curmovt->footnotesep;
      }

    /* If this system is deeper than the page depth, we can't handle it. After
    the error, it will cause a new page to be started, but it will never be
    printed. */

    if (sysfootdepth > main_pagelength)
      {
      int overflow = sysfootdepth - main_pagelength;
      error_moan(ERR64, page_sysblock->barstart, page_movtnumber,
        overflow, (overflow == 1000)? "" : "s");
      }

    /* If we have a new movement pending, find the depth of the headings and
    see if the headings plus this system will fit on the current page. If the
    system depth is zero, we have a single-stave system, in which case we
    insist on there being room for another one as well. */

    if (page_movtpending)
      {
      headstr *h = curmovt->heading;
      int depth = (h == NULL)? 0 : 17000;

      while (h != NULL)
        {
        depth += h->space;
        h = h->next;
        }

      depth += (sysfootdepth == 0)? page_sysblock->systemgap : sysfootdepth;

      /* If no room, terminate the page and start a new one. We must arrange
      that footings are printed from the *previous* movement, but take the
      option for lastfooting and pageheading from the *current* movement. */

      if (curpage->spaceleft < depth)
        {
        BOOL ph = ((curmovt->movt_opt) & movt_nopageheading) == 0;
        BOOL lf = ((curmovt->movt_opt) & movt_uselastfooting) != 0;
        curmovt = movement[page_movtnumber - 1];
        page_endpage(lf);
        curmovt = movement[page_movtnumber];
        page_newpage(curmovt->heading, ph? curmovt->pageheading : NULL);
        }

      /* There is room: output the new heading on this page, and set the
      justification parameters from the new movement. (The horizontal ones will
      have been set already, but the vertical ones can't be changed until the
      page is known.) We also change the bottom margin, but leave the top
      margin until the next page. */

      else
        {
        page_justify = curmovt->justify;
        page_botmargin = curmovt->botmargin;
        if (curmovt->heading != NULL)
          {
          page_dopageheading(curmovt->heading);
          curpage->spaceleft -= 17000;
          }
        }

      /* Set up a new footing, if present. Note that if there isn't one, and we
      didn't start a new page, and there is one still set up from the previous
      movement already (in page_footing), then it will still get printed at the
      bottom of this page. */

      if (curmovt->footing != NULL) page_footing = curmovt->footing;

      /* Cancel pending flag */

      page_movtpending = FALSE;
      }

    /* If this system does not fit on the page, start a new one. */

    if (curpage->spaceleft < sysfootdepth)
      {
      curpage->overrun = sysfootdepth - curpage->spaceleft;
      page_endpage(FALSE);
      page_newpage(NULL, curmovt->pageheading);
      }

    /* Connect the system to the chain and keep count of the number of
    vertically spreadable systems on the page. */

    *page_sysprevptr = page_sysblock;
    page_sysprevptr = &(page_sysblock->next);
    page_countsystems++;

    /* If there were any footnotes, connect them to the page's footnote list
    for inclusion at the end. Save the current spacing value for use if the
    page does actually end here. */

    if (page_newfootnotes != NULL)
      {
      if (page_footnotes == NULL) page_footnotes = page_newfootnotes; else
        {
        page_lastfootnote->next = page_newfootnotes;
        page_newfootnotes->spaceabove = curmovt->footnotesep;
        page_footnotedepth += curmovt->footnotesep;
        }
      page_lastfootnote = page_lastnewfootnote;
      page_footnotedepth += page_newfootnotedepth;
      }
    page_footnotespacing = - page_lastulevel;

    /* Update the space left on the page; just take off the space for the
    music (the system), not the footnotes. They'll be considered again with the
    next system. */

    curpage->spaceleft -= sysdepth + page_sysblock->systemgap;
    page_state = page_state_newsystem;
    page_lastsystem = page_sysblock;

    /* Update the vertical justification parameters if they changed in this
    system. */

    if (page_sys_justify != -1)   page_justify = page_sys_justify;
    if (page_sys_topmargin != -1) page_topmargin = page_sys_topmargin;
    if (page_sys_botmargin != -1) page_botmargin = page_sys_botmargin;

    /* If we have an explicit layout, deal with advancing the pointer and
    checking for a forced new page. */

    if (page_layoutptr >= 0)
      {
      page_layoutptr++;
      for (;;)
        {
        while (curmovt->layout[page_layoutptr] == lv_newpage)
          {
          page_layoutptr++;
          if (page_barnumber <= curmovt->barcount) page_newpagewanted = TRUE;
          }

        if (curmovt->layout[page_layoutptr] == lv_repeatptr)
          {
          if ((page_layoutstack[page_layoutstackptr-1] -= 1) > 0)
            page_layoutptr = curmovt->layout[page_layoutptr+1];
          else
            {
            page_layoutstackptr--;
            page_layoutptr += 2;
            }
          }
        else
          {
          while (curmovt->layout[page_layoutptr++] == lv_repeatcount)
            page_layoutstack[page_layoutstackptr++] = curmovt->layout[page_layoutptr++];
          break;
          }
        }
      }

    /* If a new page was forced after this system, set it up. This can only
    happen via [newpage] if there are more bars; hence it can't also be a
    movement end. Via explicit layout, it is also only set if there are more
    bars. */

    if (page_newpagewanted)
      {
      page_endpage(FALSE);
      page_newpage(NULL, curmovt->pageheading);
      }

    /* Handle the end of a movement */

    if (page_barnumber > curmovt->barcount) page_state = page_state_donemovt;
    }
  break;




  /****************************************************************************/

  /* Completed a movement. Tidy the data structures and deal with the end of
  the whole piece or with starting a subsequent movement. */

  case page_state_donemovt:
  misc_freecontstr(page_cont, page_lastwanted);
  if (page_ssehere != NULL) store_free(page_ssehere);
  store_free(page_ulevel);
  store_free(page_ulhere);
  store_free(page_olevel);
  store_free(page_olhere);

  /* End of the last movement */

  if (page_movtnumber++ >= main_lastmovement)
    {
    page_endpage(TRUE);
    page_done = TRUE;
    }

  /* There is another movement to follow. If it contains no staves, we must
  deal with the headings here. */

  else
    {
    movtstr *nextmovt = movement[page_movtnumber];
    int movt_opt = (nextmovt->movt_opt & ~(movt_nopageheading | movt_uselastfooting));

    /* Deal with the case of no staves in the movement; we must decide now
    whether it fits on the page or not if nothing is specified. */

    if (nextmovt->barcount < 1 && movt_opt == movt_default)
      {
      headstr *h = nextmovt->heading;
      int depth = 0;
      while (h != NULL)
        {
        depth += h->space;
        h = h->next;
        }
      movt_opt = (curpage->spaceleft < depth)? movt_newpage : movt_thispage;
      }

    /* Handle forced new page; set page_heading NULL to prevent any heading
    output, which will be done by the start-of-movt code. */

    if (movt_opt == movt_newpage)
      {
      page_endpage((nextmovt->movt_opt & movt_uselastfooting) != 0);
      curmovt = nextmovt;
      page_newpage(NULL, NULL);
      }

    /* If no page option was specified, we can't decide whether to start a new
    page until after the next system has been read. We just set a flag for the
    work to be done then. For the very special case of "thisline", we remove
    and vertical advance from the last system. Another system of the same depth
    will then always fit. We must also reduce the count of spreadable systems,
    since this one should not get additional space added to it! */

    else
      {
      curmovt = nextmovt;
      if (movt_opt == movt_thisline && page_lastsystem != NULL)
        {
        curpage->spaceleft += page_lastsystem->systemdepth +
          page_lastsystem->systemgap;
        page_lastsystem->flags |= sysblock_noadvance;
        page_countsystems--;
        }
      if (movt_opt != movt_thispage) page_movtpending = TRUE;
      }

    /* Change state */

    page_state = page_state_newmovt;
    }
  break;
  }

store_free(page_accepteddata);
store_free(page_nextdata);
store_free(page_previousdata);

DEBUG(("paginate_go() end\n"));
}

/* End of paginate.c */
