/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	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.

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

#include <string.h>

extern "C" {
#include	"memory_debug.h"
#include	"text_render.h"
#include	"window.h"
#include	"utils.h"
#include	"xl.h"

void gc_gb_sexp(XL_SEXP*);
void gc_gblisp_env(XLISP_ENV *);

}

#include	"v/v.h"
#include	"v/vobj_utils.h"

typedef struct vtr_work {
	TR_CHAR_INFO	ci;
	TR_ELEMENT *	prev_element_ptr;
	TR_ELEMENT *	element_ptr;
	char		el_type;
#define SPWT_STANDARD_ELEMENT	1
#define SPWT_ILLEAGAL_ELEMENT	2
	char		rotate;
	char		el_dir;

	int		real_line_pixels;

	VTR_SEQUENCE 	param;

} VTR_WORK;

typedef struct vtr_element_work {
	I_POINT			offset;
	LC_FONT *		font;
	LC_WRITING_STYLE *	ws;
	int			size;
	int			len;
	L_CHAR *		str;
	TR_PTR *		ptr;
	TR_ATTR *		attr;
	char			glue_start;
	char			glue_end;
	short			ll_dir;
	short			ln_dir;

	VObject *		tr_obj;

	VTR_OBJECT		obj_data;
	XL_SEXP *		obj_data_list;
} VTR_ELEMENT_WORK;

typedef struct vtr_box_line_work {
	I_POINT			offset;
	short			return_dir;

	VObject *		tr_obj;
} VTR_BOX_LINE_WORK;

typedef struct vtr_box_work {
	I_POINT			offset;

	VObject *		tr_obj;
} VTR_BOX_WORK;

int
vtr_new_sequence(TR_SEQUENCE *,void *);
void
vtr_start_lang_line_op(
	TR_SEQUENCE * sq,
	TR_CHAR_INFO * ci);
TR_ERROR
vtr_put_box_line(
	TR_SEQUENCE * sq,
	TR_CHAR_INFO * ci,
	TR_PTR p,
	TR_CHAR_ELEMENT * el);
void
vtr_new_box_line(TR_SEQUENCE * sq,TR_BOX_LINE * bl);
TR_ERROR
vtr_lang_line_ctl(TR_SEQUENCE *,TR_CHAR_INFO * ci,TR_LL_CTL *);
TR_ERROR
vtr_stop_lang_line_op(TR_SEQUENCE *,TR_CHAR_INFO *);
TR_ERROR vtr_pop_box_line(
		struct tr_sequence * sq,
		struct tr_char_info *,
		TR_PTR);
void
vtr_unmapping_element(TR_ELEMENT * el);
void
vtr_free_box_line(TR_SEQUENCE *,TR_BOX_LINE *);
void
vtr_new_box(TR_SEQUENCE *,TR_BOX *);
void
vtr_free_box(TR_SEQUENCE *,TR_BOX *);
void vtr_init_thread(TR_SEQUENCE*);
void vtr_exit_thread(TR_SEQUENCE*);
void *
vtr_copy_obj(void *);
void vtr_free_obj(void*);
void vtr_gc_obj(void*);
void vtr_gc_tr(TR_SEQUENCE *);
void vtr_waitsync(TR_SEQUENCE *,WAITSYNC_CLIENT*,int);
XL_SEXP * vtr_obj_list_element(XL_SEXP * ol,TR_ELEMENT * el);
XL_SEXP * vtr_obj_list_box(XL_SEXP * ol,TR_BOX * b);
XL_SEXP * vtr_object_list(XLISP_ENV* env,TR_SEQUENCE * sq);
void vtr_get_vobj_obj(TR_SEQUENCE * sq,
	VTR_WORK * vw,VTR_ELEMENT_WORK * ew,TR_CHAR_INFO * ci);
void vtr_get_vobj(TR_SEQUENCE * sq,
	VTR_WORK * vw,VTR_ELEMENT_WORK * ew,TR_CHAR_INFO * ci);
int vtr_min_line_pixels(TR_BOX_LINE * bl);
TR_ERROR vtr_last_ll_destroy(TR_SEQUENCE * sq,VTR_WORK * vw);
TR_ERROR vtr_last_el_destroy(TR_SEQUENCE * sq,VTR_WORK * vw);
TR_ERROR vtr_line_check(TR_SEQUENCE * sq,VTR_WORK * vw);
TR_ERROR vtr_flush_element(TR_SEQUENCE * sq,TR_CHAR_INFO * ci,VTR_WORK * vw);
TR_ERROR vtr_new_element(TR_SEQUENCE * sq,VTR_WORK * vw);
TR_ERROR vtr_lang_line_ctl_weight(
	TR_SEQUENCE * sq,
	TR_CHAR_INFO * ci,
	TR_LL_CTL * ctl);
TR_ERROR vtr_lang_line_ctl_return_code(
	TR_SEQUENCE * sq,
	TR_CHAR_INFO * ci,
	TR_LL_CTL * ctl);
XL_SEXP * _vtr_obn_element(TR_ELEMENT * el,L_CHAR * name);
XL_SEXP * _vtr_obn_box(TR_BOX * b,L_CHAR * name);
XL_SEXP * _vtr_get_object_by_name(TR_SEQUENCE * sq,L_CHAR * name);
void vtr_gc_tr_element(TR_ELEMENT * el);
void vtr_gc_tr_box(TR_BOX * b);
void _vtr_waitsync_ob_element(TR_ELEMENT * el,WAITSYNC_CLIENT * c,int);
void _vtr_waitsync_ob_box(TR_BOX * b,WAITSYNC_CLIENT * c,int);

TR_OBJ_OP vobj_obj_op = {
	vtr_copy_obj,
	0,
	vtr_free_obj,
	vtr_gc_obj,
};

extern "C" {
TR_BOX_OP vobj_tr_op = {
	vtr_new_sequence,	/* new_sequence */
	vtr_init_thread,	/** init_thread */
	vtr_exit_thread,	/** exit_thread */

	vtr_unmapping_element,	/* unmapping_element */
	vtr_new_box_line,	/* new_box_line */
	vtr_free_box_line,	/* free_box_line */
	vtr_new_box,		/** new_box */
	vtr_free_box,		/* free_box */
	vtr_start_lang_line_op,	/* start_lang_line_op */
	vtr_stop_lang_line_op,	/* _stop_lang_line_op */
	vtr_put_box_line,	/* put_box_line */
	vtr_pop_box_line,	/* pop_box_line */
	vtr_lang_line_ctl,	/* lang_line_ctl */
	vtr_gc_tr,
	vtr_waitsync,
	{0,0},
	&vobj_obj_op
};
}



void *
vtr_copy_obj(void * src)
{
VTR_OBJECT * _src, * _dest;
	_src = (VTR_OBJECT*)src;
	_dest = (VTR_OBJECT*)d_alloc(sizeof(*_dest));
	*_dest = *_src;

	return (void*)_dest;
}

void
vtr_free_obj(void * obj)
{
	d_f_ree(obj);
}

void
vtr_gc_obj(void * obj)
{
VTR_OBJECT * _obj;
	_obj = (VTR_OBJECT*)obj;
	gc_gb_sexp(_obj->sexp);
	gc_gblisp_env(_obj->env);
}


void
vtr_init_thread(TR_SEQUENCE * sq)
{
XL_INTERPRETER * xli;
	xli = new_xl_interpreter();
	xli->a_type = XLA_SELF;
	setup_i(xli);
}

void
vtr_exit_thread(TR_SEQUENCE * sq)
{
	close_self_interpreter();
}

void
vtr_unmapping_element(TR_ELEMENT * el)
{
VTR_ELEMENT_WORK * ew;
	ew = (VTR_ELEMENT_WORK*)el->work;
	if ( ew == 0 )
		return;
	if ( ew->str )
		d_f_ree(ew->str);
	if ( ew->ptr )
		d_f_ree(ew->ptr);
	_tr_free_attr(ew->attr);
	if ( ew->tr_obj )
		ew->tr_obj->destroy();
}

int
vtr_new_sequence(TR_SEQUENCE * sq,void * work)
{
VTR_WORK * w;
VTR_SEQUENCE * vsq;
	w = (VTR_WORK*)d_alloc(sizeof(*w));
	memset(w,0,sizeof(*w));
	sq->work = w;
	vsq = (VTR_SEQUENCE*)work;
	w->param = *vsq;
	return 0;
}

void
vtr_new_box_line(TR_SEQUENCE * sq,TR_BOX_LINE * bl)
{
VTR_BOX_LINE_WORK * bw;
TR_BOX * b;
VTR_BOX_WORK * bbw;
VObjectStatus sts;
VExError err;
	bw = (VTR_BOX_LINE_WORK*)d_alloc(sizeof(*bw));
	memset(bw,0,sizeof(*bw));
	bl->work = bw;
	b = bl->box;
	bbw = (VTR_BOX_WORK*)b->work;

	sts.alignh = sts.alignv = VALIGN_FILL;
	sts.padding.w = sts.padding.h = 0;
	sts.spacing.w = sts.spacing.h = 0;
	sts.visible = 1;
	sts.parent = bbw->tr_obj;
	if ( b->attr.default_lattr.default_dir & VSD_V ) {
		sts.alignv = VALIGN_TOP;
		sts.alignh = VALIGN_FILL;
		bw->tr_obj = VVAlignView::create(
					&sts,
					VSF_ALIGN|
					VSF_PADDING|
					VSF_SPACING|
					VSF_VISIBLE|
					VSF_PARENT,
					&err);
	}
	else {
		sts.alignv = VALIGN_BOTTOM;
		sts.alignh = VALIGN_FILL;
		bw->tr_obj = VHAlignView::create(
					&sts,
					VSF_ALIGN|
					VSF_PADDING|
					VSF_SPACING|
					VSF_VISIBLE|
					VSF_PARENT,
					&err);
	}
	if ( err.code != V_ER_NO_ERR )
		bw->tr_obj = 0;
}

void
vtr_free_box_line(TR_SEQUENCE * sq,TR_BOX_LINE * bl)
{
VTR_BOX_LINE_WORK * bw;
	bw = (VTR_BOX_LINE_WORK*)bl->work;
	if ( bw->tr_obj )
		bw->tr_obj->destroy();
	d_f_ree(bl->work);
}

void
vtr_new_box(TR_SEQUENCE *sq,TR_BOX * b)
{
VTR_BOX_WORK * spbw;
VObjectStatus sts;
VExError err;
VTR_WORK * vw;
	spbw = (VTR_BOX_WORK*)d_alloc(sizeof(*spbw));
	memset(spbw,0,sizeof(*spbw));
	b->work = spbw;
	vw = (VTR_WORK*)sq->work;
	if ( vw->param.new_box_vobj )
		spbw->tr_obj = (*vw->param.new_box_vobj)(sq,b);
	else {
		sts.alignh = sts.alignv = VALIGN_FILL;
		sts.padding.w = sts.padding.h = 0;
		sts.spacing.w = sts.spacing.h = 0;
		sts.visible = 1;
		sts.parent = vw->param.base_obj;
		if ( b->attr.default_lattr.default_dir & VSD_V ) {
			sts.alignh = VALIGN_RIGHT;
			sts.alignv = VALIGN_FILL;
			spbw->tr_obj = VHAlignView::create(
						&sts,
						VSF_ALIGN|
						VSF_PADDING|
						VSF_SPACING|
						VSF_VISIBLE|
						VSF_PARENT,
						&err);
		}
		else {
			sts.alignh = VALIGN_LEFT;
			sts.alignv = VALIGN_FILL;
			spbw->tr_obj = VVAlignView::create(
						&sts,
						VSF_ALIGN|
						VSF_PADDING|
						VSF_SPACING|
						VSF_VISIBLE|
						VSF_PARENT,
						&err);
		}
		if ( err.code != V_ER_NO_ERR )
			spbw->tr_obj = 0;
	}
}

void
vtr_free_box(TR_SEQUENCE * sq,TR_BOX * b)
{
VTR_BOX_WORK * bw;
	bw = (VTR_BOX_WORK*)b->work;
	if ( bw->tr_obj )
		bw->tr_obj->destroy();
	d_f_ree(b->work);
}


void
vtr_start_lang_line_op(
	TR_SEQUENCE * sq,
	TR_CHAR_INFO * ci)
{
VTR_WORK * vw;
	vw = (VTR_WORK*)sq->work;
	vw->ci = *ci;
	vw->element_ptr = ci->lang_line->element_list;
	if ( vw->ci.box_line->attr.default_dir & VSD_V )
		vw->el_dir = VSD_V;
	else	vw->el_dir = VSD_H;
	if ( vw->ci.lang_line->dir & vw->el_dir )
		vw->rotate = 0;
	else	vw->rotate = 1;
	if ( vw->el_dir == VSD_V ) {
		if ( vw->rotate == 0 )
			vw->el_type = SPWT_ILLEAGAL_ELEMENT;
		else 	vw->el_type = SPWT_STANDARD_ELEMENT;
	}
	else {
		if ( vw->ci.lang_line->dir &
				vw->ci.lang->dir_flags )
			vw->el_type = SPWT_STANDARD_ELEMENT;
		else	vw->el_type = SPWT_ILLEAGAL_ELEMENT;
	}
}


XL_SEXP *
vtr_obj_list_element(XL_SEXP * ol,TR_ELEMENT * el)
{
VTR_ELEMENT_WORK * ew;
	ew = (VTR_ELEMENT_WORK*)el->work;
	if ( ew == 0 )
		return ol;
	return append(ew->obj_data_list,ol);
}

XL_SEXP *
vtr_obj_list_box(XL_SEXP * ol,TR_BOX * b)
{
TR_BOX_LINE * bl;
TR_ELEMENT * el;
	for ( bl = b->line_list ; bl ; bl = bl->next )
		for ( el = bl->el_head->next ;
				el != bl->el_head;
				el = el->next )
			ol = vtr_obj_list_element(ol,el);
	return ol;
}

XL_SEXP *
vtr_object_list(XLISP_ENV* env,TR_SEQUENCE * sq)
{
TR_BOX * b;
XL_SEXP * ol;
	ol = eval(env,n_get_symbol("__object_list"));
	if ( get_type(ol) != XLT_PAIR )
		ol = 0;
	for ( b = sq->box_ring->next ; b != sq->box_ring ; b = b->next ) {
		ol = vtr_obj_list_box(ol,b);
	}
	return ol;
}


void
vtr_get_vobj_obj(TR_SEQUENCE * sq,
	VTR_WORK * vw,VTR_ELEMENT_WORK * ew,TR_CHAR_INFO * ci)
{
VObjectStatus sts;
TR_BOX_LINE * bl;
VTR_BOX_LINE_WORK * blw;
XLISP_ENV * env;
XL_SEXP * xl_sts,* od_list;
	if ( vw->rotate ) {
		er_panic("no support");
	}
	else {
		bl = ci->box_line;
		blw = (VTR_BOX_LINE_WORK*)bl->work;

		if ( blw->tr_obj == 0 ) {
			ew->tr_obj = 0;
			return;
		}
		blw->tr_obj->get_status(&sts,VSF_ID);
		gc_push(0,0,"vtr_get_vobj_obj");
		env = new_env(ew->obj_data.env);
		xl_sts = eval(ew->obj_data.env,n_get_symbol("VObjectStatus"));
		if ( get_type(xl_sts) != XLT_SYMBOL ) {
			xl_sts = n_get_symbol("VObjectStatus");
		}
		switch ( bl->attr.align ) {
		case TRT_CENTER:
			if ( ci->lang_line->dir & VSD_V ) {
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignh"),
					l_string(std_cm,"fill"));
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignv"),
					l_string(std_cm,"fill"));
			}
			else {
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignh"),
					l_string(std_cm,"fill"));
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignv"),
					l_string(std_cm,"bottom"));
			}
			break;
		case TRT_CAPITA_BASIS_ALL:
			if ( ci->lang_line->dir & VSD_V ) {
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignh"),
					l_string(std_cm,"fill"));
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignv"),
					l_string(std_cm,"expand"));
			}
			else {
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignh"),
					l_string(std_cm,"expand"));
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignv"),
					l_string(std_cm,"bottom"));
			}
			break;
		case TRT_LINE_START:
			switch ( bl->lang_list->dir ) {
			case VSD_V_R2L:
			case VSD_V_L2R:
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignh"),
					l_string(std_cm,"fill"));
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignv"),
					l_string(std_cm,"top"));
				break;
			case VSD_H_R2L:
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignh"),
					l_string(std_cm,"right"));
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignv"),
					l_string(std_cm,"bottom"));
				break;
			case VSD_H_L2R:
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignh"),
					l_string(std_cm,"left"));
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignv"),
					l_string(std_cm,"bottom"));
				break;
			default:
				er_panic("element");
			}
			break;
		case TRT_LINE_END:
			switch ( bl->lang_list->dir ) {
			case VSD_V_R2L:
			case VSD_V_L2R:
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignh"),
					l_string(std_cm,"fill"));
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignv"),
					l_string(std_cm,"bottom"));
				break;
			case VSD_H_R2L:
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignh"),
					l_string(std_cm,"left"));
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignv"),
					l_string(std_cm,"bottom"));
				break;
			case VSD_H_L2R:
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignh"),
					l_string(std_cm,"right"));
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignv"),
					l_string(std_cm,"bottom"));
				break;
			default:
				er_panic("obj_element");
			}
			break;
		case TRT_CAPITA_BASIS:
			if ( ci->lang_line->dir & VSD_V ) {
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignh"),
					l_string(std_cm,"fill"));
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignv"),
					l_string(std_cm,"expand"));
			}
			else {
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignh"),
					l_string(std_cm,"expand"));
				xl_sts = replace_attribute(
					xl_sts,
					l_string(std_cm,"alignv"),
					l_string(std_cm,"bottom"));
			}
			break;
		default:
			er_panic("obj_element");
		}
		set_env(env,l_string(std_cm,"VObjectStatus"),xl_sts);
		set_env(env,l_string(std_cm,"__object_list"),
				vtr_object_list(env,sq));
		od_list = vobj_eval_child(sts.id,
			env,
			List(n_get_symbol("dummy"),ew->obj_data.sexp,-1));
		if ( get_type(od_list) == XLT_ERROR ) {
			log_print_sexp(LOG_ERROR,LOG_LAYER_GB,0,"v_tr OBJECT",od_list,0);
			ew->obj_data_list = 0;
		}
		else {
			ew->obj_data_list = od_list;
		}
		gc_pop(0,0);
	}
}

void
vtr_get_vobj(TR_SEQUENCE * sq,
	VTR_WORK * vw,VTR_ELEMENT_WORK * ew,TR_CHAR_INFO * ci)
{
VObjectStatus sts;
TR_BOX_LINE * bl;
VTR_BOX_LINE_WORK * blw;
L_CHAR * d;
VExError err;
	if ( ew->ws == 0 && ew->obj_data.sexp == 0 )
		return;
	if ( ew->ws == 0 ) {
		vtr_get_vobj_obj(sq,vw,ew,ci);
		return;
	}
	if ( vw->rotate ) {
		er_panic("no support");
	}
	else {
		bl = ci->box_line;
		blw = (VTR_BOX_LINE_WORK*)bl->work;

		if ( blw->tr_obj == 0 ) {
			ew->tr_obj = 0;
			return;
		}
		sts.parent = blw->tr_obj;
		d = (L_CHAR*)d_alloc(sizeof(L_CHAR)*(ew->len+1));
		memcpy(d,ew->str,sizeof(L_CHAR)*ew->len);
		d[ew->len] = 0;
		sts.descriptor = d;
		sts.ws = ew->ws;
		sts.fsize = ew->size;
		sts.visible = 1;
		sts.spacing.h = sts.spacing.w = 0;
		sts.padding = sts.spacing;
		if ( ci->lang_line->dir & VSD_V )
			sts.vert_desc = 1;
		else 	sts.vert_desc = 0;
		switch ( bl->attr.align ) {
		case TRT_CENTER:
			if ( ci->lang_line->dir & VSD_V ) {
				sts.alignh = VALIGN_FILL;
				sts.alignv = VALIGN_FILL;
			}
			else {
				sts.alignh = VALIGN_FILL;
				sts.alignv = VALIGN_BOTTOM;
			}
			break;
		case TRT_CAPITA_BASIS_ALL:
			if ( ci->lang_line->dir & VSD_V ) {
				sts.alignh = VALIGN_FILL;
				sts.alignv = VALIGN_EXPAND;
			}
			else {
				sts.alignh = VALIGN_EXPAND;
				sts.alignv = VALIGN_BOTTOM;
			}
			break;
		case TRT_LINE_START:
			switch ( bl->lang_list->dir ) {
			case VSD_V_R2L:
			case VSD_V_L2R:
				sts.alignh = VALIGN_FILL;
				sts.alignv = VALIGN_TOP;
				break;
			case VSD_H_R2L:
				sts.alignh = VALIGN_RIGHT;
				sts.alignv = VALIGN_BOTTOM;
				break;
			case VSD_H_L2R:
				sts.alignh = VALIGN_LEFT;
				sts.alignv = VALIGN_BOTTOM;
				break;
			default:
				er_panic("element");
			}
			break;
		case TRT_LINE_END:
			switch ( bl->lang_list->dir ) {
			case VSD_V_R2L:
			case VSD_V_L2R:
				sts.alignh = VALIGN_FILL;
				sts.alignv = VALIGN_BOTTOM;
				break;
			case VSD_H_R2L:
				sts.alignh = VALIGN_LEFT;
				sts.alignv = VALIGN_BOTTOM;
				break;
			case VSD_H_L2R:
				sts.alignh = VALIGN_RIGHT;
				sts.alignv = VALIGN_BOTTOM;
				break;
			default:
				er_panic("element");
			}
			break;
		case TRT_CAPITA_BASIS:
			if ( ci->lang_line->dir & VSD_V ) {
				sts.alignh = VALIGN_FILL;
				sts.alignv = VALIGN_EXPAND;
			}
			else {
				sts.alignh = VALIGN_EXPAND;
				sts.alignv = VALIGN_BOTTOM;
			}
			break;
		default:
			er_panic("element");
		}
		if ( ew->tr_obj )
			err = ew->tr_obj->set_status(&sts,
				VSF_DESC|VSF_VERTD|VSF_WS|VSF_FSIZE|
				VSF_ALIGN|
				VSF_VISIBLE|VSF_PADDING|VSF_SPACING);
		else	ew->tr_obj = VStaticText::create(&sts,
				VSF_PARENT|VSF_DESC|VSF_VERTD|VSF_WS|VSF_FSIZE|
				VSF_ALIGN|
				VSF_VISIBLE|VSF_PADDING|VSF_SPACING,&err);
		if ( err.code != 0 )
			ew->tr_obj = 0;
	}
}

int
vtr_min_line_pixels(TR_BOX_LINE * bl)
{
VObjectStatus sts;
VTR_BOX_LINE_WORK * blw;
	blw = (VTR_BOX_LINE_WORK*)bl->work;
	blw->tr_obj->get_status(&sts,VSF_SIZE);
	if ( bl->box->attr.default_lattr.default_dir & VSD_V )
		return sts.size.h;
	else	return sts.size.w;
}

TR_ERROR
vtr_last_ll_destroy(TR_SEQUENCE * sq,VTR_WORK * vw)
{
TR_LANG_LINE * ll,* ll_prev;
TR_ERROR err;
	ll_prev = 0;
	ll = _tr_next_lang_line(vw->ci.lang_line);
	for ( ; ll ; ll = _tr_next_lang_line(ll) ) {
		if ( ll->box_line != vw->ci.box_line )
			break;
		ll_prev = ll;
	}
	if ( ll_prev == 0 ) {
		err.code = TRE_INSERT_CHAR;
		return err;
	}
	_tr_destroy_lang_line(sq,ll_prev);
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	return err;
}



TR_ERROR
vtr_last_el_destroy(TR_SEQUENCE * sq,VTR_WORK * vw)
{
TR_ELEMENT * el;
TR_ERROR err;
	if ( vw->element_ptr == 0 ||
		vw->element_ptr->ll_next == 0 ) {
		err.code = TRE_INSERT_CHAR;
		return err;
	}
	for ( el = vw->element_ptr ; el->ll_next ; el = el->ll_next );
	_tr_destroy_element(sq,vw->ci.lang_line,el);
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	return err;
}



TR_ERROR
vtr_line_check(TR_SEQUENCE * sq,VTR_WORK * vw)
{
int min;
TR_ERROR err;
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	if ( vw->ci.box->attr.max_line_pixels < 0 ) {
		return err;
	}
	else {
		for ( ; ; ) {
			min = vtr_min_line_pixels(vw->ci.box_line);
			if ( min <= vw->ci.box->attr.max_line_pixels )
				return err;
			err = vtr_last_ll_destroy(sq,vw);
			if ( err.code != TRE_OK )
				break;
		}
		for ( ; ; ) {
			min = vtr_min_line_pixels(vw->ci.box_line);
			if ( min <= vw->ci.box->attr.max_line_pixels )
				return err;
			err = vtr_last_el_destroy(sq,vw);
			if ( err.code != TRE_OK )
				break;
		}
		return err;
	}
}

TR_ERROR
vtr_flush_element(TR_SEQUENCE * sq,TR_CHAR_INFO * ci,VTR_WORK * vw)
{
VTR_ELEMENT_WORK * ew;
TR_ERROR err;
TR_ERROR ret;
	if ( vw->element_ptr == 0 )
		er_panic("sp_flush_element");
	ret.code = TRE_OK;
	ret.subcode = TRE_OK_NONE;
	if ( vw->element_ptr->cr )
		return ret;
	ew = (VTR_ELEMENT_WORK*)vw->element_ptr->work;
	if ( ew == 0 )
		return ret;
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	vtr_get_vobj(sq,vw,ew,ci);

	ew->ll_dir = ci->lang_line->dir;
	ew->ln_dir = ci->box_line->attr.default_dir;
	return vtr_line_check(sq,vw);
}


TR_ERROR
vtr_new_element(TR_SEQUENCE * sq,VTR_WORK * vw)
{
TR_ELEMENT * elp;
VTR_ELEMENT_WORK * w;
TR_ERROR err;
	err = _tr_new_element(0,sq,vw->ci.lang_line);
	if ( err.code != TRE_OK )
		return err;
	for ( elp = vw->ci.lang_line->element_list ; elp->ll_next ;
			elp = elp->ll_next );
	vw->element_ptr = elp;
	w = (VTR_ELEMENT_WORK*)d_alloc(sizeof(*w));
	memset(w,0,sizeof(*w));
	elp->work = w;
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	return err;
}

TR_ERROR
vtr_stop_lang_line_op(
	TR_SEQUENCE * sq,
	TR_CHAR_INFO * ci)
{
TR_ERROR err;
VTR_WORK * vw;
	vw = (VTR_WORK*)sq->work;
	if ( vw->element_ptr ) {
		err = vtr_flush_element(sq,ci,vw);
		if ( err.code != TRE_OK )
			return err;
	}
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	return err;
}

TR_ERROR
vtr_put_box_line(
	TR_SEQUENCE * sq,
	TR_CHAR_INFO * ci,
	TR_PTR p,
	TR_CHAR_ELEMENT * el)
{
VTR_WORK * vw;
VTR_ELEMENT_WORK * ew;
TR_ERROR err;
LCF_SET *set;
LC_WS_COND wsc;
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	vw = (VTR_WORK*)sq->work;
	if ( vw->element_ptr == 0 ) {
		err = vtr_new_element(sq,vw);
		if ( err.code != TRE_OK )
			return err;
	}
	set = 0;
	ew = (VTR_ELEMENT_WORK*)vw->element_ptr->work;
	if ( ew->str ) {
		if ( el->d.type < 0 )
			goto new_el;
		wsc.cond = WSC_FET_LIST;
		wsc.fet = vobj_fet;
		set = ws_convert(el->d.ws.ws,&wsc,el->d.ws.size,&el->ch,1,
				CBF_SRC_PLANE|CBF_DST_PLANE);
		if ( set == 0 )
			er_panic("put_char");
		if ( set->font != ew->font )
			goto new_el;
		if ( set->size != ew->size )
			goto new_el;
		if ( _tr_cmp_attr(el->attr,ew->attr) )
			goto new_el;
		if ( vw->el_type == SPWT_ILLEAGAL_ELEMENT )
			goto new_el;
		goto next;
	new_el:
		err = vtr_flush_element(sq,ci,vw);
		if (err.code != TRE_OK )
			goto end;
		vw->element_ptr = vw->element_ptr->ll_next;
		if ( vw->element_ptr == 0 ) {
			err = vtr_new_element(sq,vw);
			if ( err.code != TRE_OK )
				goto end;
			ew = (VTR_ELEMENT_WORK*)vw->element_ptr->work;
		}
		else {
			ew = (VTR_ELEMENT_WORK*)vw->element_ptr->work;
			if ( ew->str )
				d_f_ree(ew->str);
			if ( ew->ptr )
				d_f_ree(ew->ptr);
			ew->str = 0;
			ew->len = 0;
			ew->glue_start = 0;
			ew->glue_end = 0;
		}
	next:
		if ( el->d.type >= 0 ) {
			ew->font = set->font;
			ew->ws = el->d.ws.ws;
			ew->size = set->size;
			ew->len ++;
			ew->str = (L_CHAR*)d_re_alloc(ew->str,
					sizeof(L_CHAR)*ew->len);
			ew->ptr = (TR_PTR*)d_re_alloc(ew->ptr,
					sizeof(TR_PTR)*ew->len);
			ew->str[ew->len-1] = set->ch;
			ew->ptr[ew->len-1] = p;
		}
		else {
		VTR_OBJECT * vop;
			vop = (VTR_OBJECT*)el->d.obj.obj;
			ew->obj_data = *vop;
			ew->font = 0;
			ew->ws = 0;
			ew->size = 0;
			ew->len = 0;
		}
	}
	else {
		if ( el->d.type < 0 )
			goto new_el;
		wsc.cond = WSC_FET_LIST;
		wsc.fet = vobj_fet;
		set = ws_convert(el->d.ws.ws,&wsc,el->d.ws.size,&el->ch,1,
				CBF_SRC_PLANE|CBF_DST_PLANE);

		ew->len = 1;
		ew->str = (L_CHAR*)d_alloc(sizeof(L_CHAR)*ew->len);
		ew->ptr = (TR_PTR*)d_alloc(sizeof(TR_PTR)*ew->len);
		ew->font = set->font;
		ew->ws = el->d.ws.ws;
		ew->size = set->size;
		ew->str[ew->len-1] = set->ch;
		ew->ptr[ew->len-1] = p;
		ew->attr = _tr_copy_attr(el->attr);
	}
	ew->glue_end = TRW_ZERO;
	if ( set )
		d_f_ree(set);
end:
	return err;
}

TR_ERROR vtr_pop_box_line(
		struct tr_sequence * sq,
		struct tr_char_info * ci,
		TR_PTR p)
{
TR_ELEMENT * el, * el2;
VTR_ELEMENT_WORK * ew;
int i;
VTR_WORK * vw;
	vw = (VTR_WORK*)sq->work;
	for ( el = ci->lang_line->element_list ; el ; el = el->ll_next ) {
		ew = (VTR_ELEMENT_WORK*)el->work;
		for ( i = 0 ; i < ew->len ; i ++ ) {
			if ( tr_ptr_cmp(ew->ptr[i],p) == 0 ) {
				if ( i == 0 )
					goto el_destroy;
				ew->len = i;
				vtr_get_vobj(sq,vw,ew,ci);
				el = el->ll_next;
				goto el_destroy;
			}
		}
	}
	goto check;
el_destroy:
	for ( ; el ; ) {
		el2 = el->ll_next;
		_tr_destroy_element(sq,ci->lang_line,el);
		el = el2;
	}
check:
	vw = (VTR_WORK*)sq->work;
	return vtr_line_check(sq,vw);
}

TR_ERROR
vtr_lang_line_ctl_weight(
	TR_SEQUENCE * sq,
	TR_CHAR_INFO * ci,
	TR_LL_CTL * ctl)
{
VTR_WORK * vw;
VTR_ELEMENT_WORK * ew;
TR_ERROR err;
	vw = (VTR_WORK*)sq->work;
	if ( vw->element_ptr == 0 ) {
		err = vtr_new_element(sq,vw);
		if ( err.code != TRE_OK )
			return err;
	}
	ew = (VTR_ELEMENT_WORK*)vw->element_ptr->work;
	if ( ew->len == 0 ) {
		if ( ew->glue_start < ctl->w.weight )
			ew->glue_start = ctl->w.weight;
	}
	else {
		if ( ew->glue_end < ctl->w.weight )
			ew->glue_end = ctl->w.weight;
	}
	if ( ew->glue_end == TRW_RABER ) {
		err = vtr_flush_element(sq,ci,vw);
		if (err.code != TRE_OK )
			return err;
		vw->element_ptr = vw->element_ptr->ll_next;
		if ( vw->element_ptr == 0 ) {
			err = vtr_new_element(sq,vw);
			if ( err.code != TRE_OK )
				return err;
			ew = (VTR_ELEMENT_WORK*)vw->element_ptr->work;
		}
		else {
			ew = (VTR_ELEMENT_WORK*)vw->element_ptr->work;
			if ( ew->str )
				d_f_ree(ew->str);
			ew->str = 0;
			ew->len = 0;
			ew->glue_start = 0;
			ew->glue_end = 0;
		}
	}
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	return err;
}

TR_ERROR
vtr_lang_line_ctl_return_code(
	TR_SEQUENCE * sq,
	TR_CHAR_INFO * ci,
	TR_LL_CTL * ctl)
{
TR_ERROR err;
VTR_BOX_LINE_WORK * blw;
	blw = (VTR_BOX_LINE_WORK*)ci->box_line->work;
	blw->return_dir = ctl->rc.dir;
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	return err;
}



TR_ERROR
vtr_lang_line_ctl(
	TR_SEQUENCE * sq,
	TR_CHAR_INFO * ci,
	TR_LL_CTL * ctl)
{
TR_ERROR err;
	switch ( ctl->h.cmd ) {
	case LLC_WEIGHT:
		return vtr_lang_line_ctl_weight(sq,ci,ctl);
	case LLC_RETURN_CODE:
		return vtr_lang_line_ctl_return_code(sq,ci,ctl);
	default:
		er_panic("sp_lang_line_ctl");
	}
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	return err;
}

XL_SEXP *
_vtr_obn_element(TR_ELEMENT * el,L_CHAR * name)
{
VTR_ELEMENT_WORK * ew;
	ew = (VTR_ELEMENT_WORK*)el->work;
	if ( ew == 0 )
		return 0;
	return vobj_get_object_by_name(ew->obj_data_list,name,0);
}


XL_SEXP *
_vtr_obn_box(TR_BOX * b,L_CHAR * name)
{
TR_BOX_LINE * bl;
XL_SEXP * ret;
TR_ELEMENT * el;
	for ( bl = b->line_list ; bl ; bl = bl->next ) {
		for ( el = bl->el_head->next ; el != bl->el_head ;
				el = el->next ){
			ret = _vtr_obn_element(el,name);
			if ( get_type(ret) == XLT_ERROR )
				continue;
			if ( ret )
				return ret;
		}
	}
	return 0;
}


XL_SEXP *
_vtr_get_object_by_name(TR_SEQUENCE * sq,L_CHAR * name)
{
XL_SEXP * ret;
TR_BOX * b;
	for ( b = sq->box_ring->next ; b != sq->box_ring ; b = b->next ) {
		ret = _vtr_obn_box(b,name);
		if ( ret )
			return ret;
	}
	return 0;
}


XL_SEXP *
vtr_get_object_by_name(TR_SEQUENCE * sq,L_CHAR * name)
{
XL_SEXP * ret;
	tr_lock(sq);
	ret = _vtr_get_object_by_name(sq,name);
	tr_unlock(sq);
	return ret;
}

void
vtr_gc_tr_element(TR_ELEMENT * el)
{
VTR_ELEMENT_WORK * ew;
	ew = (VTR_ELEMENT_WORK*)el->work;
	if ( ew == 0 )
		return;
	gc_gb_sexp(ew->obj_data_list);
	gc_gb_sexp(ew->obj_data.sexp);
	gc_gblisp_env(ew->obj_data.env);
}

void
vtr_gc_tr_box(TR_BOX * b)
{
TR_BOX_LINE * bl;
TR_ELEMENT * el;
	for ( bl = b->line_list ; bl ; bl = bl->next )
		for ( el = bl->el_head->next ;
				el != bl->el_head;
				el = el->next )
			vtr_gc_tr_element(el);
}

void
vtr_gc_tr(TR_SEQUENCE * sq)
{
TR_BOX * b;
	for ( b = sq->box_ring->next ; b != sq->box_ring ; b = b->next ) {
		vtr_gc_tr_box(b);
	}
}




void
_vtr_waitsync_ob_element(TR_ELEMENT * el,WAITSYNC_CLIENT * c,int type)
{
VTR_ELEMENT_WORK * ew;
XL_SEXP * p, * lst, * id;
VObject * obj;
	ew = (VTR_ELEMENT_WORK*)el->work;
	if ( ew == 0 )
		return;
	for ( p = ew->obj_data_list ; get_type(p) == XLT_PAIR ; p = cdr(p) ) {
		lst = car(p);
		if ( get_type(lst) != XLT_PAIR )
			continue;
		id = get_el(lst,1);
		if ( get_type(id) != XLT_INTEGER )
			continue;
		obj = VObject::get_object_by_id(id->integer.data);
		if ( obj == 0 )
			continue;
		obj->waitsync(c,type);
	}
}


void
_vtr_waitsync_ob_box(TR_BOX * b,WAITSYNC_CLIENT * c,int type)
{
TR_BOX_LINE * bl;
TR_ELEMENT * el;
	for ( bl = b->line_list ; bl ; bl = bl->next ) {
		for ( el = bl->el_head->next ; el != bl->el_head ;
				el = el->next ){
			_vtr_waitsync_ob_element(bl->el_head,c,type);
		}
	}
}


void
vtr_waitsync(TR_SEQUENCE * sq,WAITSYNC_CLIENT * c,int type)
{
TR_BOX * b;
	for ( b = sq->box_ring->next ; b != sq->box_ring ; b = b->next ) {
		_vtr_waitsync_ob_box(b,c,type);
	}
}

