Example #1
0
File: z19.c Project: thektulu/lout
int AttachGalley(OBJECT hd, OBJECT *inners, OBJECT *suspend_pt)
{ OBJECT hd_index;		/* the index of hd in the enclosing galley   */
  OBJECT hd_inners;		/* inner galleys of hd, if unsized           */
  OBJECT dest;			/* the target @Galley hd empties into        */
  OBJECT dest_index;		/* the index of dest                         */
  OBJECT target;		/* the target indefinite containing dest     */
  OBJECT target_index;		/* the index of target                       */
  OBJECT target_galley;		/* the body of target, made into a galley    */
  OBJECT tg_inners;		/* inner galleys of target_galley            */
  BOOLEAN need_precedes = FALSE;/* true if destination lies before galley    */
  OBJECT recs;			/* list of recursive definite objects        */
  OBJECT link, y = nilobj;	/* for scanning through the components of hd */
  CONSTRAINT c;			/* temporary variable holding a constraint   */
  OBJECT env, n1, tmp, zlink, z, sym;	/* placeholders and temporaries	     */
  BOOLEAN was_sized;		/* true if sized(hd) initially               */
  int dim;			/* the galley direction                      */
  FULL_LENGTH perp_back, perp_fwd;
  OBJECT why, junk;

  debug2(DGA, D, "[ AttachGalley(Galley %s into %s)",
	SymName(actual(hd)), SymName(whereto(hd)));
  ifdebug(DGA, DD, DebugGalley(hd, nilobj, 4));
  assert( Up(hd) != hd, "AttachGalley: no index!" );
  Parent(hd_index, Up(hd));
  assert( type(hd_index) == UNATTACHED, "AttachGalley: not UNATTACHED!" );
  hd_inners = tg_inners = nilobj;
  was_sized = sized(hd);
  dim = gall_dir(hd);

  for(;;)
  {
    /*************************************************************************/
    /*                                                                       */
    /*  Search for a destination for hd.  If hd is unsized, search for       */
    /*  inner galleys preceding it first of all, then for receptive objects  */
    /*  following it, possibly in inner galleys.  If no luck, exit.          */
    /*  If hd is sized, search only for receptive objects in the current     */
    /*  galley below the current spot, and fail if cannot find any.          */
    /*                                                                       */
    /*************************************************************************/

    sym = whereto(hd);
    if( sized(hd) )
    {
      /* sized galley case: search on from current spot */
      target_index = SearchGalley(Up(hd_index), sym, TRUE, FALSE, TRUE, TRUE);
      if( target_index == nilobj )
      {	
	/* search failed to find any new target, so kill the galley */
	for( link = Down(hd); link != hd; link = NextDown(link) )
	{ Child(y, link);
	  if( type(y) == SPLIT )  Child(y, DownDim(y, dim));
	  if( is_definite(type(y)) )  break;
	}
	if( link != hd )
	  Error(19, 1, "galley %s deleted from here (no target)",
	    WARN, &fpos(y), SymName(actual(hd)));
	if( hd_inners != nilobj )  DisposeObject(hd_inners), hd_inners=nilobj;
	if( tg_inners != nilobj )  DisposeObject(tg_inners), tg_inners=nilobj;
	KillGalley(hd, FALSE);
	*inners = nilobj;
	debug0(DGA, D, "] AttachGalley returning ATTACH_KILLED");
	return ATTACH_KILLED;
      }
      else if( actual(actual(target_index)) == InputSym )
      {
	/* search found input object, so suspend on that */
	DeleteNode(hd_index);
	Link(target_index, hd);
	*inners = nilobj;
	debug0(DGA, D, "] AttachGalley returning ATTACH_INPUT");
	return ATTACH_INPUT;
      }

    }
    else /* unsized galley, either backwards or normal */
    {
      if( foll_or_prec(hd) == GALL_PREC )
      {	target_index= SearchGalley(Up(hd_index), sym, FALSE, TRUE,TRUE,FALSE);
	need_precedes = FALSE;
      }
      else
      {	target_index = SearchGalley(Up(hd_index), sym, FALSE,TRUE,FALSE,FALSE);
	need_precedes = (target_index != nilobj);
	if( target_index == nilobj )
	  target_index = SearchGalley(Up(hd_index), sym, TRUE,TRUE,TRUE,FALSE);
      }

      /* if no luck, exit without error */
      if( target_index == nilobj )
      {	*inners = nilobj;
	debug0(DGA, D, "] AttachGalley returning ATTACH_NOTARGET");
	return ATTACH_NOTARGET;
      }
    }
    assert( type(target_index) == RECEPTIVE, "AttachGalley: target_index!" );
    target = actual(target_index);
    assert( type(target) == CLOSURE, "AttachGalley: target!" );

    /* set target_galley to the expanded value of target */
    debug1(DYY, D, "[ EnterErrorBlock(FALSE) (expanding target %s)",
      SymName(actual(target)));
    EnterErrorBlock(FALSE);
    New(target_galley, HEAD);
    force_gall(target_galley) = FALSE;
    enclose_obj(target_galley) = limiter(target_galley) = nilobj;
    ClearHeaders(target_galley);
    opt_components(target_galley) = opt_constraints(target_galley) = nilobj;
    gall_dir(target_galley) = external_hor(target) ? COLM : ROWM;
    FposCopy(fpos(target_galley), fpos(target));
    actual(target_galley) = actual(target);
    whereto(target_galley) = ready_galls(target_galley) = nilobj;
    foll_or_prec(target_galley) = GALL_FOLL;
    must_expand(target_galley) = FALSE;
    sized(target_galley) = FALSE;

    /* get perpendicular constraint (none if horizontal galley) */
    if( dim == ROWM )
    {
      Constrained(target, &c, 1-dim, &junk);
      if( !constrained(c) )
        Error(19, 2, "receptive symbol %s has unconstrained width",
	  FATAL, &fpos(target), SymName(actual(target)));
      debug2(DSC, DD, "Constrained( %s, 1-dim ) = %s",
	EchoObject(target), EchoConstraint(&c));
      if( !FitsConstraint(0, 0, c) )
      { debug0(DGA, D, "  reject: target_galley horizontal constraint is -1");
	y = nilobj;
        goto REJECT;
      }
    }
    else /* actually unused */
      SetConstraint(c, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH);

    debug1(DGA, DDD, "  expanding %s", EchoObject(target));
    tmp = CopyObject(target, no_fpos);
    Link(target_galley, tmp);
    env = DetachEnv(tmp);
    debug4(DGM, D, "  external_ver(%s) = %s, external_hor(%s) = %s",
      SymName(actual(target)), bool(external_ver(target)),
      SymName(actual(target)), bool(external_hor(target)));
    SizeGalley(target_galley, env,
	external_ver(target) || external_hor(target),
	threaded(target), non_blocking(target_index),
	trigger_externs(target_index), &save_style(target),
	&c, whereto(hd), &dest_index, &recs, &tg_inners,
	enclose_obj(hd) != nilobj ? CopyObject(enclose_obj(hd), no_fpos):nilobj);
    debug1(DGA, DD, "  SizeGalley tg_inners: %s", DebugInnersNames(tg_inners));
    if( recs != nilobj )  ExpandRecursives(recs);
    dest = actual(dest_index);
    if( underline(dest) == UNDER_UNDEF )  underline(dest) = UNDER_OFF;

    /* verify that hd satisfies any horizontal constraint on dest */
    if( dim == ROWM )
    {
      debug1(DGA, DDD, "  checking hor fit of hd in %s",SymName(actual(dest)));
      Constrained(dest, &c, 1-dim, &junk);
      debug3(DSC, DD, "Constrained( %s, %s ) = %s",
	EchoObject(dest), dimen(1-dim), EchoConstraint(&c));
      assert( constrained(c), "AttachGalley: dest unconstrained!" );
      if( !FitsConstraint(0, 0, c) )
      { debug0(DGA, D, "  reject: hd horizontal constraint is -1");
	y = nilobj;
        goto REJECT;
      }
    }

    /* manifest and size the galley if not done yet */
    if( !sized(hd) )
    {
      debug2(DYY, D, "[ EnterErrorBlock(TRUE) (sizing galley %s into %s)",
	SymName(actual(hd)), SymName(whereto(hd)));
      EnterErrorBlock(TRUE);
      n1 = nilobj;
      Child(y, Down(hd));
      env = DetachEnv(y);
      /*** threaded() only defined in ROWM case
      SizeGalley(hd, env, TRUE, threaded(dest), non_blocking(target_index),
	TRUE, &save_style(dest), &c, nilobj, &n1, &recs, &hd_inners);
      *** */
      SizeGalley(hd, env, TRUE, dim == ROWM ? threaded(dest) : FALSE,
	non_blocking(target_index), TRUE, &save_style(dest), &c, nilobj,
	&n1, &recs, &hd_inners, nilobj);
      debug1(DGA,DD,"  SizeGalley hd_inners: %s", DebugInnersNames(hd_inners));
      if( recs != nilobj )  ExpandRecursives(recs);
      if( need_precedes )		/* need an ordering constraint */
      {	OBJECT index1, index2;
        New(index1, PRECEDES);
	New(index2, FOLLOWS);
	blocked(index2) = FALSE;
	tmp = MakeWord(WORD, STR_EMPTY, no_fpos);
	Link(index1, tmp);  Link(index2, tmp);
	Link(Up(hd_index), index1);
	Link(Down(hd), index2);
	debug0(DGA, D, "  inserting PRECEDES and FOLLOWS");
      }
      LeaveErrorBlock(TRUE);
      debug0(DYY, D, "] LeaveErrorBlock(TRUE) (finished sizing galley)");
    }

    if( dim == ROWM )
    { if( !FitsConstraint(back(hd, 1-dim), fwd(hd, 1-dim), c) )
      { debug3(DGA, D, "  reject: hd %s,%s does not fit target_galley %s",
	  EchoLength(back(hd, 1-dim)), EchoLength(fwd(hd, 1-dim)),
	  EchoConstraint(&c));
        Error(19, 3, "too little horizontal space for galley %s at %s",
	  WARN, &fpos(hd), SymName(actual(hd)), SymName(actual(dest)));
        goto REJECT;
      }
    }

    /* check status of first component of hd */
    debug0(DGA, DDD, "  now ready to attach; hd =");
    ifdebug(DGA, DDD, DebugObject(hd));
    for( link = Down(hd);  link != hd;  link = NextDown(link) )
    {
      Child(y, link);
      debug1(DGA, DDD, "  examining %s", EchoIndex(y));
      if( type(y) == SPLIT )  Child(y, DownDim(y, dim));
      switch( type(y) )
      {

	case EXPAND_IND:
	case SCALE_IND:
	case COVER_IND:
	case GALL_PREC:
	case GALL_FOLL:
	case GALL_FOLL_OR_PREC:
	case GALL_TARG:
	case CROSS_PREC:
	case CROSS_FOLL:
	case CROSS_FOLL_OR_PREC:
	case CROSS_TARG:
	case PAGE_LABEL_IND:
	    
	  break;


	case PRECEDES:
	case UNATTACHED:
	    
	  if( was_sized )
	  { /* SizeGalley was not called, so hd_inners was not set by it */
	    if( hd_inners == nilobj )  New(hd_inners, ACAT);
	    Link(hd_inners, y);
	  }
	  break;


	case RECEPTIVE:

	  goto SUSPEND;


	case RECEIVING:
	    
	  goto SUSPEND;


	case FOLLOWS:
	    
	  Child(tmp, Down(y));
	  if( Up(tmp) == LastUp(tmp) )
	  { link = pred(link, CHILD);
	    debug0(DGA, DD, "  disposing FOLLOWS");
	    DisposeChild(NextDown(link));
	    break;
	  }
	  Parent(tmp, Up(tmp));
	  assert(type(tmp) == PRECEDES, "Attach: PRECEDES!");
	  switch( CheckComponentOrder(tmp, target_index) )
	  {
	    case CLEAR:		DeleteNode(tmp);
				link = pred(link, CHILD);
				DisposeChild(NextDown(link));
				break;

	    case PROMOTE:	break;

	    case BLOCK:		debug0(DGA, DD, "CheckContraint: BLOCK");
				goto SUSPEND;

	    case CLOSE:		debug0(DGA, D, "  reject: CheckContraint");
				goto REJECT;
	  }
	  break;


	case GAP_OBJ:

	  underline(y) = underline(dest);
	  if( !join(gap(y)) )  seen_nojoin(hd) = TRUE;
	  break;


	case BEGIN_HEADER:
	case END_HEADER:
	case SET_HEADER:
	case CLEAR_HEADER:

	  /* do nothing until actually promoted out of here */
	  underline(y) = underline(dest);
	  break;


	case CLOSURE:
	case CROSS:
	case FORCE_CROSS:
	case NULL_CLOS:
	case PAGE_LABEL:

	  underline(y) = underline(dest);
	  break;


	case WORD:
	case QWORD:
	case ONE_COL:
	case ONE_ROW:
	case WIDE:
	case HIGH:
	case HSHIFT:
	case VSHIFT:
	case HMIRROR:
	case VMIRROR:
	case HSCALE:
	case VSCALE:
	case HCOVER:
	case VCOVER:
	case HCONTRACT:
	case VCONTRACT:
	case HLIMITED:
	case VLIMITED:
	case HEXPAND:
	case VEXPAND:
	case START_HVSPAN:
	case START_HSPAN:
	case START_VSPAN:
	case HSPAN:
	case VSPAN:
	case ROTATE:
	case BACKGROUND:
	case SCALE:
	case KERN_SHRINK:
	case INCGRAPHIC:
	case SINCGRAPHIC:
	case PLAIN_GRAPHIC:
	case GRAPHIC:
	case LINK_SOURCE:
	case LINK_DEST:
	case LINK_DEST_NULL:
	case LINK_URL:
	case ACAT:
	case HCAT:
	case VCAT:
	case ROW_THR:
	case COL_THR:
	    

	  underline(y) = underline(dest);
	  if( dim == ROWM )
	  {
	    /* make sure y is not joined to a target below (vertical only) */
	    for( zlink = NextDown(link); zlink != hd; zlink = NextDown(zlink) )
	    { Child(z, zlink);
	      switch( type(z) )
	      {
	        case RECEPTIVE:
		
		  if( non_blocking(z) )
		  { zlink = PrevDown(zlink);
		    DeleteNode(z);
		  }
		  else
		  { y = z;
		    goto SUSPEND;
		  }
		  break;


	        case RECEIVING:
		
		  if( non_blocking(z) )
		  { zlink = PrevDown(zlink);
		    while( Down(z) != z )
		    { Child(tmp, Down(y));
		      if( opt_components(tmp) != nilobj )
		      { DisposeObject(opt_components(tmp));
		        opt_components(tmp) = nilobj;
		        debug3(DOG, D, "AttachGalley(%s) de-optimizing %s %s",
			  SymName(actual(hd)), SymName(actual(tmp)), "(join)");
		      }
		      DetachGalley(tmp);
		      KillGalley(tmp, FALSE);
		    }
		    DeleteNode(z);
		  }
		  else
		  { y = z;
		    goto SUSPEND;
		  }
		  break;


	        case GAP_OBJ:
		
		  if( !join(gap(z)) )  zlink = PrevDown(hd);
		  break;


	        default:	break;
	      }
	    }

	    /* if HCAT, try vertical hyphenation (vertical galleys only) */
	    if( type(y) == HCAT )  VerticalHyphenate(y);
	  }


	  /* check availability of parallel space for the first component */
	  why = nilobj;
	  Constrained(dest, &c, dim, &why);
	  debug3(DGF, DD, "  dest parallel Constrained(%s, %s) = %s",
	    EchoObject(dest), dimen(dim), EchoConstraint(&c));
	  if( !FitsConstraint(back(y, dim), fwd(y, dim), c) )
	  { BOOLEAN scaled;

	    /* if forcing galley doesn't fit, try scaling first component */
	    scaled = FALSE;
	    if( force_gall(hd) && size(y, dim) > 0 )
	    { int scale_factor;
	      scale_factor = ScaleToConstraint(back(y,dim), fwd(y,dim), &c);
	      if( scale_factor > 0.5 * SF )
	      {	char num1[20], num2[20];
		sprintf(num1, "%.1fc", (float) size(y, dim) / CM);
		sprintf(num2, "%.1fc", (float) bfc(c) / CM);
		if( dim == ROWM )
		  Error(19, 4, "%s object too high for %s space; %s inserted",
		    WARN, &fpos(y), num1, num2, KW_SCALE);
		else
		  Error(19, 5, "%s object too wide for %s space; %s inserted",
		    WARN, &fpos(y), num1, num2, KW_SCALE);
		y = InterposeScale(y, scale_factor, dim);
		scaled = TRUE;
	      }
	    }

	    /* otherwise we must reject, and warn the user */
	    if( !scaled )
	    { char num1[20], num2[20];
	      debug3(DGA, D, "  reject: vsize %s,%s in %s; y=",
		EchoLength(back(y, dim)), EchoLength(fwd(y, dim)),
		EchoConstraint(&c));
	      ifdebug(DGA, D, DebugObject(y));
	      if( size(y, dim) > 0 )
	      { sprintf(num1, "%.1fc", (float) size(y, dim) / CM);
	        sprintf(num2, "%.1fc", (float) bfc(c) / CM);
	        if( dim == ROWM )
		  Error(19, 12, "%s object too high for %s space; will try elsewhere",
		    WARN, &fpos(y), num1, num2);
	        else
		  Error(19, 13, "%s object too wide for %s space; will try elsewhere",
		    WARN, &fpos(y), num1, num2);
	      }
	      goto REJECT;
	    }

	  }

	  /* check availability of perpendicular space for first component */
	  if( dim == ROWM )
	  { perp_back = back(hd, 1-dim);  perp_fwd = fwd(hd, 1-dim);
	  }
	  else
	  { perp_back = back(y, 1-dim);  perp_fwd = fwd(y, 1-dim);
	  }
	  Constrained(dest, &c, 1-dim, &junk);
	  debug3(DGF, DD, "  dest perpendicular Constrained(%s, %s) = %s",
	    EchoObject(dest), dimen(1-dim), EchoConstraint(&c));
	  if( !FitsConstraint(perp_back, perp_fwd, c) )
	  { BOOLEAN scaled;

	    /* if forcing galley doesn't fit, try scaling first component */
	    scaled = FALSE;
	    if( force_gall(hd) && perp_back + perp_fwd > 0 )
	    { int scale_factor;
	      scale_factor = ScaleToConstraint(perp_back, perp_fwd, &c);
	      if( scale_factor > 0.5 * SF )
	      {	char num1[20], num2[20];
		sprintf(num1, "%.1fc", (float) (perp_back + perp_fwd) / CM);
		sprintf(num2, "%.1fc", (float) bfc(c) / CM);
		if( 1-dim == ROWM )
		  Error(19, 6, "%s object too high for %s space; %s inserted",
		    WARN, &fpos(y), num1, num2, KW_SCALE);
		else
		  Error(19, 7, "%s object too wide for %s space; %s inserted",
		    WARN, &fpos(y), num1, num2, KW_SCALE);
		y = InterposeScale(y, scale_factor, 1-dim);
		scaled = TRUE;
	      }
	    }

	    /* otherwise we must reject, and warn the user */
	    if( !scaled )
	    {
	      debug3(DGA, D, "  reject: vsize %s,%s in %s; y=",
		EchoLength(perp_back), EchoLength(perp_fwd),
		EchoConstraint(&c));
	      ifdebug(DGA, D, DebugObject(y));
	      goto REJECT;
	    }

	  }

	  /* dest seems OK, so perform its size adjustments */
	  debug0(DSA, D, "calling AdjustSize from AttachGalley (a)");
	  AdjustSize(dest, back(y, dim), fwd(y, dim), dim);
	  debug0(DSA, D, "calling AdjustSize from AttachGalley (b)");
	  AdjustSize(dest, perp_back, perp_fwd, 1-dim);


	  /* now check parallel space for target_galley in target */
	  Constrained(target, &c, dim, &why);
	  debug3(DGF, DD, "  target parallel Constrained(%s, %s) = %s",
	    EchoObject(target), dimen(dim), EchoConstraint(&c));
	  Child(z, LastDown(target_galley));  /* works in all cases? */
	  assert( !is_index(type(z)), "AttachGalley: is_index(z)!" );
	  assert( back(z, dim)>=0 && fwd(z, dim)>=0, "AttachGalley: z size!" );
	  if( !FitsConstraint(back(z, dim), fwd(z, dim), c) )
	  { BOOLEAN scaled;

	    debug2(DGA, D, "  why     = %d %s", (int) why, EchoObject(why));
	    debug2(DGA, D, "  limiter = %d %s", (int) limiter(hd),
	      EchoObject(limiter(hd)));

	    /* if forcing galley doesn't fit, try scaling z */
	    scaled = FALSE;
	    if( force_gall(hd) && size(z, dim) > 0 && limiter(hd) != why )
	    { int scale_factor;
	      scale_factor = ScaleToConstraint(back(z,dim), fwd(z,dim), &c);
	      if( scale_factor > 0.5 * SF )
	      {	char num1[20], num2[20];
		sprintf(num1, "%.1fc", (float) size(z, dim) / CM);
		sprintf(num2, "%.1fc", (float) bfc(c) / CM);
		if( dim == ROWM )
		  Error(19, 8, "%s object too high for %s space; %s inserted",
		    WARN, &fpos(y), num1, num2, KW_SCALE);
		else
		  Error(19, 9, "%s object too wide for %s space; %s inserted",
		    WARN, &fpos(y), num1, num2, KW_SCALE);
		z = InterposeWideOrHigh(z, dim);
		z = InterposeScale(z, scale_factor, dim);
		scaled = TRUE;
	      }
	    }

	    if( !scaled )
	    { char num1[20], num2[20];
	      limiter(hd) = why;
	      debug3(DGA, D, "  set limiter(%s) = %d %s", SymName(actual(hd)),
		(int) limiter(hd), EchoObject(limiter(hd)));
	      debug3(DGA, D, "  reject: size was %s,%s in %s; y =",
		EchoLength(back(z, dim)), EchoLength(fwd(z, dim)),
		EchoConstraint(&c));
	      ifdebug(DGA, D, DebugObject(y));
	      if( size(z, dim) > 0 )
	      { sprintf(num1, "%.1fc", (float) size(z, dim) / CM);
	        sprintf(num2, "%.1fc", (float) bfc(c) / CM);
	        if( dim == ROWM )
		  Error(19, 14, "%s object too high for %s space; will try elsewhere",
		    WARN, &fpos(y), num1, num2);
	        else
		  Error(19, 15, "%s object too wide for %s space; will try elsewhere",
		    WARN, &fpos(y), num1, num2);
	      }
	      goto REJECT;
	    }
	  }
	  limiter(hd) = why;
	  debug3(DGA, D, "  set limiter(%s) = %d %s", SymName(actual(hd)),
	    (int) limiter(hd), EchoObject(limiter(hd)));

	  /* now check perpendicular space for target_galley in target */
	  Constrained(target, &c, 1-dim, &junk);
	  debug3(DGF, DD, "  target perpendicular Constrained(%s, %s) = %s",
	    EchoObject(target), dimen(1-dim), EchoConstraint(&c));
	  Child(z, LastDown(target_galley));  /* works in all cases? */
	  assert( !is_index(type(z)), "AttachGalley: is_index(z)!" );
	  assert( back(z, 1-dim)>=0 && fwd(z, 1-dim)>=0,
	    "AttachGalley: z size (perpendicular)!" );
	  if( !FitsConstraint(back(z, 1-dim), fwd(z, 1-dim), c) )
	  { BOOLEAN scaled;

	    /* if forcing galley doesn't fit, try scaling z */
	    scaled = FALSE;
	    if( force_gall(hd) && size(z, 1-dim) > 0 )
	    { int scale_factor;
	      scale_factor = ScaleToConstraint(back(z,1-dim), fwd(z,1-dim), &c);
	      if( scale_factor > 0.5 * SF )
	      {	char num1[20], num2[20];
		sprintf(num1, "%.1fc", (float) size(z, 1-dim) / CM);
		sprintf(num2, "%.1fc", (float) bfc(c) / CM);
		if( 1-dim == ROWM )
		  Error(19, 10, "%s object too high for %s space; %s inserted",
		    WARN, &fpos(y), num1, num2, KW_SCALE);
		else
		  Error(19, 11, "%s object too wide for %s space; %s inserted",
		    WARN, &fpos(y), num1, num2, KW_SCALE);
		z = InterposeWideOrHigh(z, 1-dim);
		z = InterposeScale(z, scale_factor, 1-dim);
		scaled = TRUE;
	      }
	    }

	    if( !scaled )
	    {
	      debug3(DGA, D, "  reject: size was %s,%s in %s; y =",
		EchoLength(back(z, 1-dim)), EchoLength(fwd(z, 1-dim)),
		EchoConstraint(&c));
	      ifdebug(DGA, D, DebugObject(y));
	      goto REJECT;
	    }
	  }

	  /* target seems OK, so adjust sizes and accept */
	  if( external_hor(target) )
	  {
	    /* don't adjust any sizes, none to adjust */
	    debug0(DSA, D, "not calling AdjustSize from AttachGalley (c)");
	  }
	  else if( external_ver(target) )
	  {
	    /* adjust perp size only, to galley size */
	    debug0(DSA, D, "calling AdjustSize from AttachGalley (d)");
	    AdjustSize(target, back(target_galley, 1-dim),
	      fwd(target_galley, 1-dim), 1-dim);
	  }
	  else
	  {
	    /* adjust both directions, using z (last component) */
	    Child(z, LastDown(target_galley));
	    debug0(DSA, D, "AttachGalley AdjustSize using z =");
	    ifdebug(DSA, D, DebugObject(z));
	    debug0(DSA, D, "calling AdjustSize from AttachGalley (e)");
	    AdjustSize(target, back(z, dim), fwd(z, dim), dim);
	    debug0(DSA, D, "calling AdjustSize from AttachGalley (f)");
	    AdjustSize(target, back(z, 1-dim), fwd(z, 1-dim), 1-dim);
	  }

	  goto ACCEPT;


	default:
	    
	  assert1(FALSE, "AttachGalley:", Image(type(y)));
	  break;

      } /* end switch */
    } /* end for */

    /* null galley: promote whole galley without expanding the target */
    debug0(DGA, D, "  null galley");
    if( tg_inners != nilobj )  DisposeObject(tg_inners), tg_inners = nilobj;
    DisposeObject(target_galley);
    LeaveErrorBlock(FALSE);
    debug0(DYY, D, "] LeaveErrorBlock(FALSE) (null galley)");

    /* kill off any null objects within the galley, then transfer it */
    /* don't use Promote() since it does extra unwanted things here  */
    for( link = Down(hd);  link != hd;  link = NextDown(link) )
    { Child(y, link);
      switch( type(y) )
      {

	case GAP_OBJ:
	case CLOSURE:
	case CROSS:
	case FORCE_CROSS:
	case NULL_CLOS:
	case PAGE_LABEL:
	
	  link = PrevDown(link);
	  debug1(DGA, D, "  null galley, disposing %s", Image(type(y)));
	  DisposeChild(NextDown(link));
	  break;

	
	default:
	
	  break;
      }
    }
    TransferLinks(NextDown(hd), hd, Up(target_index));

    /* attach hd temporarily to target_index */
    MoveLink(Up(hd), target_index, PARENT);
    assert( type(hd_index) == UNATTACHED, "AttachGalley: type(hd_index)!" );
    DeleteNode(hd_index);

    /* return; only hd_inners needs to be flushed now */
    *inners = hd_inners;
    debug0(DGA, D, "] AttachGalley returning ATTACH_NULL");
    return ATTACH_NULL;


    REJECT:
	
      /* reject first component */
      /* debug1(DGA, D, "  reject %s", EchoObject(y)); */
      debug0(DGA, D, "  reject first component");
      LeaveErrorBlock(TRUE);
      debug0(DYY, D, "] LeaveErrorBlock(TRUE) (REJECT)");
      if( tg_inners != nilobj )  DisposeObject(tg_inners), tg_inners = nilobj;
      DisposeObject(target_galley);
      if( foll_or_prec(hd) == GALL_PREC && !sized(hd) )
      {
	/* move to just before the failed target */
	MoveLink(Up(hd_index), Up(target_index), PARENT);
      }
      else
      {
	/* move to just after the failed target */
	MoveLink(Up(hd_index), NextDown(Up(target_index)), PARENT);
      }
      continue;


    SUSPEND:
	
      /* suspend at first component */
      debug1(DGA, D, "  suspend %s", EchoIndex(y));
      blocked(y) = TRUE;
      LeaveErrorBlock(FALSE);
      debug0(DYY, D, "] LeaveErrorBlock(FALSE) (SUSPEND)");
      if( tg_inners != nilobj )  DisposeObject(tg_inners), tg_inners = nilobj;
      DisposeObject(target_galley);
      MoveLink(Up(hd_index), Up(target_index), PARENT);
      if( was_sized )
      { /* nothing new to flush if suspending and already sized */
	if( hd_inners != nilobj )  DisposeObject(hd_inners), hd_inners=nilobj;
	*inners = nilobj;
      }
      else
      { /* flush newly discovered inners if not sized before */
	*inners = hd_inners;
      }
      debug0(DGA, D, "] AttachGalley returning ATTACH_SUSPEND");
      *suspend_pt = y;
      return ATTACH_SUSPEND;


    ACCEPT:
	
      /* accept first component; now committed to the attach */
      debug3(DGA, D, "  accept %s %s %s", Image(type(y)), EchoObject(y),
	EchoFilePos(&fpos(y)));
      LeaveErrorBlock(TRUE);
      debug0(DYY, D, "] LeaveErrorBlock(TRUE) (ACCEPT)");

      /* attach hd to dest */
      MoveLink(Up(hd), dest_index, PARENT);
      assert( type(hd_index) == UNATTACHED, "AttachGalley: type(hd_index)!" );
      DeleteNode(hd_index);

      /* move first component of hd into dest */
      /* nb Interpose must be done after all AdjustSize calls */
      if( dim == ROWM && !external_ver(dest) )
	Interpose(dest, VCAT, hd, y);
      else if( dim == COLM && !external_hor(dest) )
      { Interpose(dest, ACAT, y, y);
	Parent(junk, Up(dest));
	assert( type(junk) == ACAT, "AttachGalley: type(junk) != ACAT!" );
	StyleCopy(save_style(junk), save_style(dest));
	adjust_cat(junk) = padjust(save_style(junk));
      }
      debug1(DGS, D, "calling Promote(hd, %s) from AttachGalley/ACCEPT",
	link == hd ? "hd" : "NextDown(link)");
      Promote(hd, link == hd ? hd : NextDown(link), dest_index, TRUE);

      /* move target_galley into target */
      /* nb Interpose must be done after all AdjustSize calls */
      if( !(external_ver(target) || external_hor(target)) )
      {	Child(z, LastDown(target_galley));
	Interpose(target, VCAT, z, z);
      }
      debug0(DGS, D, "calling Promote(target_galley) from AttachGalley/ACCEPT");
      Promote(target_galley, target_galley, target_index, TRUE);
      DeleteNode(target_galley);
      assert(Down(target_index)==target_index, "AttachGalley: target_ind");
      if( blocked(target_index) )  blocked(dest_index) = TRUE;
      DeleteNode(target_index);

      /* return; both tg_inners and hd_inners need to be flushed now;        */
      /* if was_sized, hd_inners contains the inners of the first component; */
      /* otherwise it contains the inners of all components, from SizeGalley */
      if( tg_inners == nilobj ) *inners = hd_inners;
      else if( hd_inners == nilobj ) *inners = tg_inners;
      else
      {	TransferLinks(Down(hd_inners), hd_inners, tg_inners);
	DeleteNode(hd_inners);
	*inners = tg_inners;
      }
      debug0(DGA, D, "] AttachGalley returning ATTACH_ACCEPT");
      ifdebug(DGA, D,
	if( dim == COLM && !external_hor(dest) )
	{ OBJECT z;
	  Parent(z, Up(dest));
	  debug2(DGA, D, "  COLM dest_encl on exit = %s %s",
	    Image(type(z)), EchoObject(z));
	}
      )
      return ATTACH_ACCEPT;

  } /* end for */
Example #2
0
void FlushGalley(OBJECT hd)
{ OBJECT dest;			/* the target galley hd empties into         */
  OBJECT dest_index;		/* the index of dest                         */
  OBJECT inners;		/* list of galleys and PRECEDES to flush     */
  OBJECT link, y;		/* for scanning through the components of hd */
  int dim;			/* direction of galley                       */
  CONSTRAINT dest_par_constr;	/* the parallel size constraint on dest      */
  CONSTRAINT dest_perp_constr;	/* the perpendicular size constraint on dest */
  int pb, pf, f;		/* candidate replacement sizes for dest      */

  OBJECT dest_encl;		/* the VCAT or ACAT enclosing dest, if any   */
  int    dest_side;		/* if dest_encl != nilobj, side dest is on   */
  BOOLEAN need_adjust;		/* TRUE as soon as dest_encl needs adjusting */
  FULL_LENGTH dest_back, dest_fwd; /* the current size of dest_encl or dest  */
  FULL_LENGTH frame_size;	/* the total constraint of dest_encl         */
  OBJECT prec_gap;		/* the gap preceding dest if any else nilobj */
  OBJECT prec_def;		/* the component preceding dest, if any      */
  OBJECT succ_gap;		/* the gap following dest if any else nilobj */
  OBJECT succ_def;		/* the component following dest, if any      */
  OBJECT stop_link;		/* most recently seen gap link of hd         */
  FULL_LENGTH stop_back;        /* back(dest_encl) incl all before stop_link */
  FULL_LENGTH stop_fwd;         /* fwd(dest_encl) incl. all before stop_link */
  FULL_LENGTH stop_perp_back;   /* back(dest_encl) in other direction        */
  FULL_LENGTH stop_perp_fwd;    /* fwd(dest_encl) in other direction         */
  BOOLEAN prnt_flush;		/* TRUE when the parent of hd needs a flush  */
  BOOLEAN target_is_internal;   /* TRUE if flushing into an internal target  */
  BOOLEAN headers_seen;		/* TRUE if a header is seen at all           */
  OBJECT zlink, z, tmp, prnt;  int attach_status;  BOOLEAN remove_target;
  OBJECT why;
  FULL_LENGTH perp_back, perp_fwd;  /* current perp size of dest_encl        */

  debug1(DGF, D, "[ FlushGalley %s (hd)", SymName(actual(hd)));
  prnt_flush = FALSE;
  dim = gall_dir(hd);

  RESUME:
  assert( type(hd) == HEAD, "FlushGalley: type(hd) != HEAD!" );
  debug1(DGF, D, "  resuming FlushGalley %s, hd =", SymName(actual(hd)));
  ifdebugcond(DGF, DD, actual(hd) == nilobj, DebugGalley(hd, nilobj, 4));
  assert( Up(hd) != hd, "FlushGalley: resume found no parent to hd!" );


  /*@@************************************************************************/
  /*                                                                         */
  /*  The first step is to examine the parent of galley hd to determine the  */
  /*  status of the galley.  If this is not suitable for flushing, we do     */
  /*  what we can to change the status.  If still no good, return; so if     */
  /*  this code does not return, then the galley is ready to flush into a    */
  /*  destination in the normal way, and the following variables are set:    */
  /*                                                                         */
  /*     dest_index   the parent of the galley and index of its destination  */
  /*     dest         the destination of the galley, a @Galley object        */
  /*                                                                         */
  /***************************************************************************/

  Parent(dest_index, Up(hd));
  switch( type(dest_index) )
  {

    case DEAD:
    
      /* the galley has been killed off while this process was sleeping */
      debug1(DGF, D, "] FlushGalley %s returning (DEAD)", SymName(actual(hd)));
      return;


    case UNATTACHED:
    
      /* the galley is currently not attached to a destination */
      attach_status = AttachGalley(hd, &inners, &y);
      debug1(DGF, DD, "  ex-AttachGalley inners: %s", DebugInnersNames(inners));
      Parent(dest_index, Up(hd));
      switch( attach_status )
      {

	case ATTACH_KILLED:

	  assert(inners==nilobj, "FlushGalley/ATTACH_KILLED: inners!=nilobj!");
	  debug1(DGF, D, "] FlushGalley %s returning (ATTACH_KILLED)",
	    SymName(actual(hd)));
	  debug1(DGF, D, "    prnt_flush = %s", bool(prnt_flush));
	  return;


	case ATTACH_INPUT:

	  ParentFlush(prnt_flush, dest_index, FALSE);
	  assert(inners==nilobj, "FlushGalley/ATTACH_INPUT: inners!=nilobj!");
	  debug1(DGF, D, "] FlushGalley %s returning (ATTACH_INPUT)",
	    SymName(actual(hd)));
	  return;


	case ATTACH_NOTARGET:

	  ParentFlush(prnt_flush, dest_index, FALSE);
	  assert(inners==nilobj, "FlushGalley/ATTACH_NOTARG: inners!=nilobj!");
	  debug1(DGF, D, "] FlushGalley %s returning (ATTACH_NOTARGET)",
	    SymName(actual(hd)));
	  return;


	case ATTACH_SUSPEND:

	  /* AttachGalley only returns inners here if they really need to */
	  /* be flushed; in particular the galley must be unsized before  */
	  if( inners != nilobj )
	  {
	    debug0(DGF, DD, "  calling FlushInners() from FlushGalley (a)");
	    FlushInners(inners, nilobj);
	    goto RESUME;
	  }
	  stop_link = nilobj;
	  goto SUSPEND;	/* nb y will be set by AttachGalley in this case */


	case ATTACH_NULL:

	  /* hd will have been linked to the unexpanded target in this case */
	  remove_target = (actual(actual(dest_index)) == whereto(hd));
          if( force_gall(hd) )
          {
            /* if hd is a forcing galley, close all predecessors */
	    debug3(DGA, D, "  forcing ATTACH_NULL case for %s into %s (%s)",
	      SymName(actual(hd)), SymName(whereto(hd)),
	      remove_target ? "remove_target" : "not remove_target");
	    Parent(prnt, Up(dest_index));
	    if( !non_blocking(dest_index) && remove_target )
	    {
	      /* ***
	      prnt_flush = TRUE;
	      *** */
	      prnt_flush = non_blocking(dest_index) = TRUE;
	    }
	    FreeGalley(prnt, Up(dest_index), &inners, Up(dest_index),
	      whereto(hd));
          }
          else
	  {
	    debug3(DGA, D, "  non-force ATTACH_NULL case for %s into %s (%s)",
	      SymName(actual(hd)), SymName(whereto(hd)),
	      remove_target ? "remove_target" : "not remove_target");
	    if( blocked(dest_index) && remove_target )  prnt_flush = TRUE;
	  }
	  DetachGalley(hd);
	  KillGalley(hd, TRUE);
          if( inners != nilobj )
	  {
	    debug0(DGF, DD, "  calling FlushInners() from FlushGalley (b)");
	    FlushInners(inners, nilobj);
	  }
	  else ParentFlush(prnt_flush, dest_index, remove_target);
	  debug0(DGF, D, "] FlushGalley returning ATTACH_NULL");
	  return;


	case ATTACH_ACCEPT:

          /* if hd is a forcing galley, or actual(dest_index) is   */
	  /* @ForceGalley, then close all predecessors             */
          if( force_gall(hd) || actual(actual(dest_index)) == ForceGalleySym )
          { Parent(prnt, Up(dest_index));
	    debug1(DGA, D, "  forcing ATTACH_ACCEPT case for %s",
	      SymName(actual(hd)));
	    /* debug0(DGA, DD, "  force: prnt ="); */
	    /* ifdebug(DGA, DD, DebugObject(prnt)); */
	    /* debug1(DGA, D,"  calling FreeGalley from FlushGalley(%s)", */
	    /*   SymName(actual(hd))); */
	    if( !non_blocking(dest_index) )  prnt_flush = TRUE; /* bug fix */
	    FreeGalley(prnt, Up(dest_index), &inners, Up(dest_index),
	      whereto(hd));
	    /* debug0(DGA, DD, "  force: after FreeGalley, prnt ="); */
	    /* ifdebug(DGA, DD, DebugObject(prnt)); */
          }
          else prnt_flush = prnt_flush || blocked(dest_index);
          debug1(DGF, DD, "    force: prnt_flush = %s", bool(prnt_flush));
          if( inners != nilobj )
	  {
	    debug0(DGF, DD, "  calling FlushInners() from FlushGalley (c)");
	    FlushInners(inners, nilobj);
	  }
          goto RESUME;


	default:

	  assert(FALSE, "FlushGalley: attach_status");
	  break;

      }
      break;


    case RECEIVING:
    
      if( actual(actual(dest_index)) == InputSym )
      { ParentFlush(prnt_flush, dest_index, FALSE);
	debug1(DGF, D, "] FlushGalley %s retn, input", SymName(actual(hd)));
	return;
      }
      break;


    default:
    
      assert1(FALSE, "FlushGalley: dest_index", Image(type(dest_index)));
      break;
  }
  dest = actual(dest_index);
  if( underline(dest) == UNDER_UNDEF )  underline(dest) = UNDER_OFF;
  target_is_internal =
    (dim==ROWM && !external_ver(dest)) || (dim==COLM && !external_hor(dest));
  headers_seen = FALSE;
  debug1(DGF, DD, "  dest_index: %s", EchoObject(dest_index));


  /*@@************************************************************************/
  /*                                                                         */
  /*  The second step is to examine the components of the galley one by one  */
  /*  to determine if they can be promoted.  Each component has the format   */
  /*                                                                         */
  /*    { <index> } <object>                                                 */
  /*                                                                         */
  /*  and is always followed by a gap object (except the last component).    */
  /*  An index indicates that the following object has some interesting      */
  /*  feature, and it points to that feature inside the object.  There are   */
  /*  two possible actions for each component, in addition to accepting it:  */
  /*                                                                         */
  /*    REJECT:   The component does not fit, so detach the galley           */
  /*    SUSPEND:  The component is incomplete; go to sleep and wait          */
  /*                                                                         */
  /***************************************************************************/

  stop_link = dest_encl = inners = nilobj;
  need_adjust = FALSE;

  /***************************************************************************/
  /*                                                                         */
  /*  Loop invariant                                                         */
  /*                                                                         */
  /*  The children of hd up to but not including Child(link) have been       */
  /*  examined and pronounced to be promotable, if unbreakable gaps are      */
  /*  ignored.  When unbreakable gaps are taken into account, the most       */
  /*  recent gap where a break is possible is at Child(stop_link), or        */
  /*  nowhere if stop_link == nilobj.                                        */
  /*                                                                         */
  /*  Case 1:  target_is_internal == FALSE                                   */
  /*                                                                         */
  /*  If this flag is FALSE, it means that the target of this galley is      */
  /*  external.  Consequently, there is no need to calculate sizes because   */
  /*  there is no constraint on them.  Also, a REJECT action is impossible   */
  /*  so unbreakable gaps are no impediment.  Variable dest_encl is nilobj.  */
  /*                                                                         */
  /*  Case 2:  target_is_internal == TRUE                                    */
  /*                                                                         */
  /*  If this flag is TRUE, it means that the target of this galley is       */
  /*  internal.  Consequently, sizes need to be calculated, and unbreakable  */
  /*  gaps need to be taken into account.  Variable dest_encl may be not     */
  /*  nilobj, in which case the following variables are defined:             */
  /*                                                                         */
  /*    dest_encl        the object enclosing dest (which must exist)        */
  /*    prec_gap         gap object preceding dest (which must exist)        */
  /*    prec_def         first definite object preceding dest (must exist)   */
  /*    dest_back        back(dest_encl) including effect of accepted compts */
  /*    dest_fwd         fwd(dest_encl) including effect of accepted compts  */
  /*    dest_side        BACK or FWD, i.e. which side of the mark dest is on */
  /*    dest_par_constr  the parallel size constraint on dest                */
  /*    dest_perp_constr the perpendicular size constraint on dest           */
  /*    frame_size       size of frame enclosing dest_encl                   */
  /*    perp_back        back(dest_encl) in other direction, incl accepteds  */
  /*    perp_fwd         fwd(dest_encl) in other direction,  incl accepteds  */
  /*                                                                         */
  /*  if dest_encl is nilobj, these variables are not defined.               */
  /*                                                                         */
  /*  If stop_link is non-nilobj, then in the internal case dest_encl must   */
  /*  be non-nilobj, and the following variables are defined:                */
  /*                                                                         */
  /*    stop_back        back(dest_encl) including all before stop_link      */
  /*    stop_fwd         fwd(dest_encl)  including all before stop_link      */
  /*    stop_perp_back   back(dest_encl) in other direction                  */
  /*    stop_perp_fwd    fwd(dest_encl) in other direction                   */
  /*                                                                         */
  /*  need_adjust is true if at least one definite component has been        */
  /*  accepted for promotion and the destination is internal; hence,         */
  /*  dest_encl is defined and its size needs to be adjusted.                */
  /*                                                                         */
  /*  inners is the set of all PRECEDES and UNATTACHED indexes found.        */
  /*                                                                         */
  /***************************************************************************/

  for( link = Down(hd);  link != hd;  link = NextDown(link) )
  {
    Child(y, link);
    if( type(y) == SPLIT )  Child(y, DownDim(y, dim));
    debug2(DGF, DD, "  examining %s %s", Image(type(y)), EchoObject(y));
    switch( type(y) )
    {

      case GAP_OBJ:

	underline(y) = underline(dest);
	prec_gap = y;
	if( target_is_internal )
	{
	  /* *** not necessarily true
	  assert( dest_encl != nilobj, "FlushGalley/GAP_OBJ: dest_encl!" );
	  *** */
	  if( dest_encl != nilobj && !nobreak(gap(prec_gap)) )
	  {
	    stop_link = link;
	    stop_back = dest_back;
	    stop_fwd  = dest_fwd;
	    stop_perp_back = perp_back;
	    stop_perp_fwd = perp_fwd;
	  }
	}
	else stop_link = link;
	if( !join(gap(y)) )  seen_nojoin(hd) = TRUE;
	break;


      case SCALE_IND:
      case COVER_IND:
      case EXPAND_IND:
      case GALL_PREC:
      case GALL_FOLL:
      case GALL_FOLL_OR_PREC:
      case GALL_TARG:
      case CROSS_PREC:
      case CROSS_FOLL:
      case CROSS_FOLL_OR_PREC:
      case CROSS_TARG:
      case PAGE_LABEL_IND:

	underline(y) = underline(dest);
	break;


      case PRECEDES:
      case UNATTACHED:
	  
	if( inners == nilobj )  New(inners, ACAT);
	Link(inners, y);
	break;


      case RECEIVING:
      case RECEPTIVE:
	  
	goto SUSPEND;


      case FOLLOWS:
	  
	Child(tmp, Down(y));
	if( Up(tmp) == LastUp(tmp) )
	{ link = PrevDown(link);
	  DisposeChild(NextDown(link));
	  break;
	}
	Parent(tmp, Up(tmp));
	assert(type(tmp) == PRECEDES, "Flush: PRECEDES!");
	switch( CheckComponentOrder(tmp, dest_index) )
	{
	  case CLEAR:	DeleteNode(tmp);
			link = PrevDown(link);
			DisposeChild(NextDown(link));
			break;

	  case PROMOTE:	break;

	  case BLOCK:	goto SUSPEND;

	  case CLOSE:	if( opt_components(hd) != nilobj )
			{ DisposeObject(opt_components(hd));
			  opt_components(hd) = nilobj;
			  debug2(DOG, D, "FlushGalley(%s) de-optimizing %s",
			    "(CLOSE problem)", SymName(actual(hd)));
			}
			debug1(DGF, DD, "  reject (a) %s", EchoObject(y));
			goto REJECT;
	}
	break;


      case BEGIN_HEADER:
      case END_HEADER:
      case SET_HEADER:
      case CLEAR_HEADER:

	/* do nothing except take note, until actually promoted out of here */
	headers_seen = TRUE;
	break;


      case NULL_CLOS:
      case PAGE_LABEL:
      case WORD:
      case QWORD:
      case ONE_COL:
      case ONE_ROW:
      case WIDE:
      case HIGH:
      case HSHIFT:
      case VSHIFT:
      case HSCALE:
      case VSCALE:
      case HCOVER:
      case VCOVER:
      case HCONTRACT:
      case VCONTRACT:
      case HLIMITED:
      case VLIMITED:
      case HEXPAND:
      case VEXPAND:
      case START_HVSPAN:
      case START_HSPAN:
      case START_VSPAN:
      case HSPAN:
      case VSPAN:
      case ROTATE:
      case BACKGROUND:
      case SCALE:
      case KERN_SHRINK:
      case INCGRAPHIC:
      case SINCGRAPHIC:
      case PLAIN_GRAPHIC:
      case GRAPHIC:
      case LINK_SOURCE:
      case LINK_DEST:
      case ACAT:
      case HCAT:
      case VCAT:
      case ROW_THR:
      case CLOSURE:
      case CROSS:
      case FORCE_CROSS:

	underline(y) = underline(dest);
	if( dim == ROWM )
	{
	  /* make sure y is not joined to a target below (vertical case only) */
	  for( zlink = NextDown(link); zlink != hd; zlink = NextDown(zlink) )
	  { Child(z, zlink);
	    switch( type(z) )
	    {
	      case RECEPTIVE:
	      case RECEIVING:	y = z;
				goto SUSPEND;

	      case GAP_OBJ:	if( !join(gap(z)) )  zlink = PrevDown(hd);
				break;

	      default:		break;
	    }
	  }

	  /* try vertical hyphenation before anything else */
	  if( type(y) == HCAT )  VerticalHyphenate(y);

	}

	/* check size constraint */
	if( target_is_internal )
	{
	  /* initialise dest_encl etc if not done yet */
	  if( dest_encl == nilobj )
	  { assert( UpDim(dest,1-dim) == UpDim(dest,dim), "FlushG: UpDims!" );
	    /* *** weird old code, trying for UpDim(dest, ROWM)?
	    Parent(dest_encl, NextDown(Up(dest)));
	    *** */
	    Parent(dest_encl, Up(dest));
	    debug4(DGF, DD, "  flush dest = %s %s, dest_encl = %s %s",
	      Image(type(dest)), EchoObject(dest),
	      Image(type(dest_encl)), EchoObject(dest_encl));
	    assert( (dim==ROWM && type(dest_encl)==VCAT) ||
	            (dim==COLM && type(dest_encl)==ACAT),
	      "FlushGalley: dest != VCAT or ACAT!" );
	    SetNeighbours(Up(dest), FALSE, &prec_gap, &prec_def,
	      &succ_gap, &succ_def, &dest_side);
	    assert(prec_gap != nilobj || is_indefinite(type(y)),
	      "FlushGalley: prec_gap == nilobj && !is_indefinite(type(y))!" );
	    assert(succ_gap == nilobj, "FlushGalley: succ_gap != nilobj!" );
	    assert(dest_side == FWD || is_indefinite(type(y)),
	      "FlushGalley: dest_side != FWD || !is_indefinite(type(y))!");
	    dest_back = back(dest_encl, dim);
	    dest_fwd  = fwd(dest_encl, dim);
	    perp_back = back(dest_encl, 1-dim);
	    perp_fwd  = fwd(dest_encl, 1-dim);
	    Constrained(dest_encl, &dest_par_constr, dim, &why);
	    Constrained(dest_encl, &dest_perp_constr, 1-dim, &why);
	    debug1(DGF, DD, "  setting dest_perp_constr = %s",
	      EchoConstraint(&dest_perp_constr));
	    frame_size = constrained(dest_par_constr) ? bfc(dest_par_constr) :0;
	  }

	  if( !is_indefinite(type(y)) )
	  {
	    ifdebugcond(DGF, DD,  mode(gap(prec_gap)) == NO_MODE,
	      DebugGalley(hd, y, 4));

	    /* calculate parallel effect of adding y to dest */
	    f = dest_fwd  + fwd(y, dim) - fwd(prec_def, dim) +
		  ActualGap(fwd(prec_def, dim), back(y, dim),
			fwd(y, dim), &gap(prec_gap), frame_size,
			dest_back + dest_fwd - fwd(prec_def, dim));
	    debug5(DGF, DD, "  f = %s + %s - %s + %s (prec_gap %s)",
	      EchoLength(dest_fwd), EchoLength(fwd(y, dim)),
	      EchoLength(fwd(prec_def, dim)), EchoLength(
		  ActualGap(fwd(prec_def, dim), back(y, dim),
			fwd(y, dim), &gap(prec_gap), frame_size,
			dest_back + dest_fwd - fwd(prec_def, dim))
	      ), EchoGap(&gap(prec_gap)));
	    debug3(DGF, DD, "  b,f: %s,%s;   dest_encl: %s",
			EchoLength(dest_back), EchoLength(f),
			EchoConstraint(&dest_par_constr));

	    /* check new size against parallel constraint */
	    if( (units(gap(prec_gap))==FRAME_UNIT && width(gap(prec_gap)) > FR)
	        || !FitsConstraint(dest_back, f, dest_par_constr)
		|| (opt_components(hd) != nilobj && opt_comps_permitted(hd)<=0)
	      )
	    {
	      if( opt_components(hd) != nilobj )
	      { OBJECT z;

		/* record the size of this just-completed target area for hd */
		New(z, WIDE);
		CopyConstraint(constraint(z), dest_par_constr);
		Link(opt_constraints(hd), z);
		ifdebug(DOG, D,
		  debug2(DOG, D, "FlushGalley(%s) adding constraint %s",
		    SymName(actual(hd)), EchoConstraint(&constraint(z)));
		  if( units(gap(prec_gap))==FRAME_UNIT &&
		      width(gap(prec_gap)) > FR ) 
		  { debug1(DOG, D, "  prec_gap = %s", EchoGap(&gap(prec_gap)));
		  }
		  if( !FitsConstraint(dest_back, f, dest_par_constr) )
		  { debug3(DOG, D, "  !FitsConstraint(%s, %s, %s)",
		      EchoLength(dest_back), EchoLength(f),
		      EchoConstraint(&dest_par_constr));
		  }
		  if( opt_comps_permitted(hd) <= 0 )
		  { debug1(DOG, D, "  opt_comps_permitted = %2d",
		      opt_comps_permitted(hd));
		  }
		  debug4(DOG, D, "prec_gap = %s;  y = %s (%s,%s):",
		    EchoGap(&gap(prec_gap)), Image(type(y)),
		    EchoLength(back(y, dim)), EchoLength(fwd(y, dim)));
		  DebugObject(y);
		)

		/* refresh the number of components permitted into the next target */
		if( opt_counts(hd) != nilobj && Down(opt_counts(hd)) != opt_counts(hd) )
		{ Child(z, Down(opt_counts(hd)));
		  opt_comps_permitted(hd) += comp_count(z) - 1;
		  DisposeChild(Up(z));
		}
		else opt_comps_permitted(hd) = MAX_FILES;  /* a large number */
		debug1(DOG, D, "  REJECT permitted = %2d", opt_comps_permitted(hd));
	      }
	      debug1(DGF, DD, "  reject (b) %s", EchoObject(y));
	      goto REJECT;
	    }

	    /* calculate perpendicular effect of adding y to dest */
	    if( seen_nojoin(hd) )
	    {
	      pb = 0;
	      pf = find_max(perp_fwd,  size(y, 1-dim));
	    }
	    else
	    {
	      pb = find_max(perp_back, back(y, 1-dim));
	      pf = find_max(perp_fwd,  fwd(y,  1-dim));
	    }

	    /* check new size against perpendicular constraint */
	    if( !FitsConstraint(pb, pf, dest_perp_constr) )
	    {
	      if( opt_components(hd) != nilobj )
	      { DisposeObject(opt_components(hd));
		opt_components(hd) = nilobj;
		debug1(DOG, D, "FlushGalley(%s) de-optimizing (perp problem)",
		  SymName(actual(hd)));
	      }
	      if( dim == ROWM )
	      {
		Error(20, 3, "component too wide for available space",
		  WARN, &fpos(y));
		debug6(DGF, DD, "  %s,%s [%s,%s] too wide for %s, y = %s",
		  EchoLength(pb), EchoLength(pf),
		  EchoLength(back(y, 1-dim)), EchoLength(fwd(y, 1-dim)),
		  EchoConstraint(&dest_perp_constr), EchoObject(y));
	      }
	      debug1(DGF, DD, "  reject (c) %s", EchoObject(y));
	      goto REJECT;
	    }

	    /* accept definite component */
	    dest_fwd = f;  prec_def = y;
	    perp_back = pb;  perp_fwd = pf;
	    need_adjust = TRUE;
	    if( opt_components(hd) != nilobj )
	    { opt_comps_permitted(hd)--;
	      debug1(DOG, D, "  ACCEPT permitted = %2d", opt_comps_permitted(hd));
	    }
	  }
	  /* accept indefinite component */
	} /* end if( target_is_internal ) */