/*@z19.c:Galley Attaching:DetachGalley()@*************************************/
/* */
/* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.41) */
/* COPYRIGHT (C) 1991, 2023 Jeffrey H. Kingston */
/* */
/* Jeffrey H. Kingston (jeff@it.usyd.edu.au) */
/* School of Information Technologies */
/* The University of Sydney 2006 */
/* AUSTRALIA */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either Version 3, or (at your option) */
/* any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the Free Software */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA */
/* */
/* FILE: z19.c */
/* MODULE: Galley Attaching */
/* EXTERNS: SearchGalley(), AttachGalley(), DetachGalley() */
/* */
/*****************************************************************************/
#include "externs.h"
#include "parent.h"
#include "child.h"
/*****************************************************************************/
/* */
/* OBJECT InterposeScale(y, scale_factor, dim) */
/* */
/* Interpose a @Scale symbol above y with the given scale factor. */
/* */
/*****************************************************************************/
static OBJECT InterposeScale(OBJECT y, int scale_factor, int dim)
{ OBJECT res;
New(res, SCALE);
FposCopy(fpos(res), fpos(y));
if( dim == COLM )
{ bc(constraint(res)) = scale_factor;
fc(constraint(res)) = 1 * SF;
}
else
{ bc(constraint(res)) = 1 * SF;
fc(constraint(res)) = scale_factor;
}
back(res, dim) = (back(y, dim) * scale_factor) / SF;
fwd(res, dim) = (fwd(y, dim) * scale_factor) / SF;
back(res, 1-dim) = back(y, 1-dim);
fwd(res, 1-dim) = fwd(y, 1-dim);
ReplaceNode(res, y);
Link(res, y);
return res;
} /* end InterposeScale */
/*****************************************************************************/
/* */
/* OBJECT InterposeWideOrHigh(y, dim) */
/* */
/* Interpose a @Wide or @High symbol above y with the same size as y, with */
/* a value which prevents any further increase in the size of y. */
/* */
/*****************************************************************************/
static OBJECT InterposeWideOrHigh(OBJECT y, int dim)
{ OBJECT res;
New(res, dim == COLM ? WIDE : HIGH);
FposCopy(fpos(res), fpos(y));
back(res, dim) = back(y, dim);
fwd(res, dim) = fwd(y, dim);
back(res, 1-dim) = back(y, 1-dim);
fwd(res, 1-dim) = fwd(y, 1-dim);
SetConstraint(constraint(res), MAX_FULL_LENGTH, size(res, dim), MAX_FULL_LENGTH);
ReplaceNode(res, y);
Link(res, y);
return res;
} /* end InterposeWideOrHigh */
/*****************************************************************************/
/* */
/* DetachGalley(hd) */
/* */
/* Detach galley hd from its target. */
/* */
/*****************************************************************************/
void DetachGalley(OBJECT hd)
{ OBJECT prnt, index;
assert( type(hd) == HEAD && Up(hd) != hd, "DetachGalley: precondition!" );
debug1(DGA, D, "DetachGalley( %s )", SymName(actual(hd)));
Parent(prnt, Up(hd));
assert( Up(prnt) != prnt, "DetachGalley: parent!" );
New(index, UNATTACHED);
actual(index) = nilobj;
non_blocking(index) = TRUE;
blocked(index) = FALSE;
pinpoint(index) = nilobj;
MoveLink(Up(hd), index, PARENT);
Link(NextDown(Up(prnt)), index);
debug0(DGA, D, "DetachGalley returning.");
} /* end DetachGalley */
/*@::SearchGalley()@**********************************************************/
/* */
/* OBJECT SearchGalley(start, sym, forwards, subgalleys, closures, input) */
/* */
/* Search a galley and its sub-galleys for a target which uses sym. The */
/* meanings of the flags are as follows: */
/* */
/* forwards If TRUE, search forwards from just after start, else */
/* search backwards from just before start */
/* subgalleys If TRUE, search down into sub-galleys of this galley */
/* closures If TRUE, closures in this galley are acceptable results */
/* input If TRUE, InputSym is an acceptable result */
/* */
/*****************************************************************************/
OBJECT SearchGalley(OBJECT start, OBJECT sym, BOOLEAN forwards,
BOOLEAN subgalleys, BOOLEAN closures, BOOLEAN input)
{ OBJECT y, res, z, zlink, link;
ifdebug(DGA, D, Parent(y, start));
debug6(DGA, D, "[ SearchGalley(%s, %s, %s, %s, %s, %s)",
type(start) == HEAD ? (char *) SymName(actual(start)) :
type(y) == HEAD ? (char *) SymName(actual(y)) : "link",
SymName(sym),
forwards ? "fwd" : "back", subgalleys ? "subgalleys" : "nosubgalleys",
closures ? "closures" : "noclosures", input ? "input" : "noinput");
assert( type(start) == LINK || type(start) == HEAD, "SearchGalley: start!" );
link = forwards ? NextDown(start) : PrevDown(start);
res = nilobj;
while( res == nilobj && type(link) != HEAD )
{ Child(y, link);
switch( type(y) )
{
case UNATTACHED:
case RECEIVING:
debug1(DGA, D, " examining %s", EchoIndex(y));
if( subgalleys )
for( zlink = Down(y); zlink!=y && res==nilobj; zlink=NextDown(zlink) )
{ Child(z, zlink);
res = SearchGalley(z, sym, TRUE, TRUE, TRUE, input);
}
if( res == nilobj && input && type(y) == RECEIVING &&
actual(actual(y)) == InputSym )
res = y;
break;
case RECEPTIVE:
debug1(DGA, D, " examining %s", EchoIndex(y));
if( closures && type(actual(y)) == CLOSURE
&& SearchUses(actual(actual(y)), sym) ) res = y;
else if( input && actual(actual(y)) == InputSym ) res = y;
break;
default:
break;
}
link = forwards ? NextDown(link) : PrevDown(link);
}
debug1(DGA, D, "] SearchGalley returning %s", EchoIndex(res));
return res;
} /* end SearchGalley */
/*@@**************************************************************************/
/* */
/* int AttachGalley(hd, inners, suspend_pt) */
/* */
/* Attach galley hd, which may be unsized, to a destination. This involves */
/* searching for a destination forward or back from the attachment point of */
/* hd and promoting up to and including the first definite component of hd. */
/* */
/* Although AttachGalley never flushes any galleys, it may identify some */
/* galleys which should be flushed, even if the attach is itself not */
/* successful. These are returned in *inners, or nilobj if none. */
/* */
/* The integer returned by AttachGalley indicates what happened to hd: */
/* */
/* ATTACH_KILLED The galley was sized to begin with but no target */
/* for it could be found. The galley has been killed */
/* and that's the end of it. */
/* */
/* ATTACH_INPUT When searching for a target for the galley we came */
/* upon InputSym, suggesting that the target might be */
/* still to be read. So the galley has been linked to */
/* that InputSym and must now wait. */
/* */
/* ATTACH_NOTARGET The galley is unsized and no target could be found */
/* for it. This is fine, it just means that we can't */
/* flush the galley now and we must try again later. */
/* */
/* ATTACH_SUSPEND The galley is sized and a target was found for it, */
/* but the first component of the galley proved to be */
/* indefinite so could not be promoted. The galley */
/* remains unattached but is moved to just before its */
/* target so that it can find it easily later when its */
/* first component becomes definite and it is flushed. */
/* */
/* ATTACH_NULL The galley is sized and a target was found for it, */
/* but the body of the galley proved to be null (i.e. */
/* there were no definite components to be flushed). */
/* This is to be treated just like the normal case */
/* following, except that the target is replaced by */
/* @Null rather than by its body. */
/* */
/* ATTACH_ACCEPT The galley is sized and a target was found for it, */
/* and one component of the galley has been promoted. */
/* */
/*****************************************************************************/
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;
actual(target_galley) = actual(target);
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));
whereto(target_galley) = ready_galls(target_galley) = nilobj;
foll_or_prec(target_galley) = GALL_FOLL;
must_expand(target_galley) = FALSE;
sized(target_galley) = FALSE;
seen_nojoin(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, D, " AttachGalley 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, D," AttachGalley 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, DD, 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, DD, 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, DD, " why = %d %s", (int) why, EchoObject(why));
debug2(DGA, DD, " 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, DD, 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, DD, " 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, DD, 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 */
debug2(DGA, D, " accept first component %s %s", Image(type(y)),
EchoFilePos(&fpos(y)));
/* ***
debug3(DGA, DD, " 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;
}
debug1(DGA, D, "] AttachGalley returning ATTACH_ACCEPT (inners %s)",
DebugInnersNames(*inners));
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 */
} /* end AttachGalley */