/**********************************************************************
 
	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"

void er_panic(char*);

} // extern "C"


#include	"v/v.h"
#include 	"v/VxlTreeView.h"


#define BACK_COLOR_G_R	0xc0
#define BACK_COLOR_G_G	0xc0
#define BACK_COLOR_G_B	0xc0
#define BACK_COLOR_G_A	0x40

void copy_VxlTreeViewStatus(VxlTreeViewStatus * tv1,VxlTreeViewStatus * tv2,int flags);
void tv_lines_setup(VxlTreeView_work * w,int line_no);
void tv_lines_setup_small(VxlTreeView_work * w,int line_no);
V_CALLBACK_D(tv_arc);
void set_arc_callback(VxlTreeView * tv_obj,LINE_LIST * ll);
void tv_attach_line(VxlTreeView_work * w,int line_no);
void tv_insert_lines(VxlTreeView * tv,VxlTreeView_work * w,int line_no );
void tv_delete_lines(VxlTreeView * tv,VxlTreeView_work * w,int line_no );
VImage * direct_copy(VxlTreeView_work * wk,int no);
VImage * mirror_copy(VxlTreeView_work * wk,int no);
VImage * get_arc_element(VxlTreeView_work * w,int no);
VexDraw * _new_arc(VxlTreeView_work * w);
void tv_attach_child(VxlTreeView_work * w,int line_no);
V_CALLBACK_D(tv_disable_focus_handler);
V_CALLBACK_D(tv_focus_command_status);
V_CALLBACK_D(tv_focus_obey_command);
V_CALLBACK_D(tv_backcolor_handler);
V_CALLBACK_D(tv_bc_destroy_handler);

void
copy_VxlTreeViewStatus(VxlTreeViewStatus * tv1,VxlTreeViewStatus * tv2,int flags)
{
	if ( flags & VSF_XL_TREEVIEW_DIR )
		tv1->dir = tv2->dir;
	if ( flags & VSF_XL_TREEVIEW_LINE_NOS )
		tv1->line_nos = tv2->line_nos;
	if ( flags & VSF_XL_TREEVIEW_IMG ) {
		memcpy(&tv1->elr,&tv2->elr,sizeof(tv1->elr));
		tv1->img = tv2->img;
		v_image_ref(tv1->img);
	}
	if ( flags & VSF_XL_TREEVIEW_FOCUS ) {
		tv1->focus_handler = tv2->focus_handler;
		tv1->focus_handler_arg = tv2->focus_handler_arg;
	}
}


void
VxlTreeView::_tv_backcolor_handler(VObject * obj)
{
VBackColorView * bcv;
int new_f;
BC_LIST * bc,* bc2;
int h;
void * arg;
int	(*f)(
		int event_type,
		VObject * tv,
		int edit_type,
		int data1,
		VBackColorView ** data2,
		void*);

	bcv = static_cast<VBackColorView*>(obj);
	_VM_OP_START_VOID
	if ( tv_w.tv_focus == 0 )
		goto end;
	if ( VEditable::get_focused_object() != tv_w.tv_focus )
		new_f = 1;
	else	new_f = 0;
	for ( bc = tv_w.bc ; bc ; bc = bc->next )
		if ( bc->bc == bcv )
			break;
	if ( new_f == 0 ) {
		bc->selected
			= 1 - bc->selected;
		if ( bc->selected == 0 ) {
			SET_RGB8_32(sts.attr,
				0,0,
				0,0);
			bc->bc->set_status(&sts,VSF_ATTR);
		}
		else {
			sts.attr = vobject_get_hilite_color();
			bc->bc->set_status(&sts,VSF_ATTR);
			for ( bc2 = tv_w.bc ; bc2 ; bc2 = bc2->next ) {
				if ( bc2 == bc )
					continue;
				if ( bc2->selected ) {
					SET_RGB8_32(sts.attr,
						0,0,
						0,0);
					bc2->bc->set_status(&sts,VSF_ATTR);
					bc2->selected = 0;
				}
			}
		}
	}
	h = 0;
	for ( bc = tv_w.bc ; bc ; bc = bc->next ) {
		if ( bc->selected == 0 )
			continue;
		h ++;
		if ( new_f == 1 ) {
			sts.attr = vobject_get_hilite_color();
			bc->bc->set_status(&sts,VSF_ATTR);
		}
	}
	tv_w.tv_focus->focus();
	f = tv_w.tv_sts.focus_handler;
	arg = tv_w.tv_sts.focus_handler_arg;

	if (f)
		tv_w.tv_focus->set_editable_flags((*f)
			(FHT_CLICK_BC,this,0,h,0,arg));
end:
	VM_OP_END
}

V_CALLBACK_D(tv_backcolor_handler)
{
VxlTreeView * tv;
	tv = (VxlTreeView*)user_arg;
	tv->_tv_backcolor_handler(object);
}


void
VxlTreeView::_tv_focus_obey_command(int type)
{
int h;
int i;
BC_LIST * bc;
VBackColorView ** bcp;
void * arg;
int	(*f)(
		int event_type,
		VObject * tv,
		int edit_type,
		int data1,
		VBackColorView ** data2,
		void*);

	_VM_OP_START_VOID

	h = 0;
	for ( bc = tv_w.bc ; bc ; bc = bc->next ) {
		if ( bc->selected == 0 )
			continue;
		h ++;
	}
	bcp = (VBackColorView**)d_alloc(sizeof(*bcp)*(h+1));
	for ( i = 0 , bc = tv_w.bc ; bc ; bc = bc->next ) {
		if ( bc->selected == 0 )
			continue;
		bcp[i++] = bc->bc;
	}
	

	f = tv_w.tv_sts.focus_handler;
	arg = tv_w.tv_sts.focus_handler_arg;
	VM_OP_END

	if (f)
		(*f)(FHT_OBEY_EDIT,
				this,
				type,
				h,
				bcp,
				arg);

	d_f_ree(bcp);

	_VM_OP_START_VOID

	h = 0;
	for ( bc = tv_w.bc ; bc ; bc = bc->next ) {
		if ( bc->selected == 0 )
			continue;
		h ++;
	}
	if (f)
		tv_w.tv_focus->set_editable_flags((*f)
			(FHT_CLICK_BC,this,0,h,0,arg));

	VM_OP_END
}

V_CALLBACK_D(tv_focus_obey_command)
{
VxlTreeView * tv;
	tv = static_cast<VxlTreeView*>(user_arg);
	tv->_tv_focus_obey_command(*(int*)sys_arg);
}

void
VxlTreeView::_tv_focus_command_status()
{
int hilite_nos = 0;
	_VM_OP_START_VOID
	for ( BC_LIST * bc = tv_w.bc ; bc ; bc = bc->next )
		if ( bc->selected )
			hilite_nos ++;
	if (tv_w.tv_sts.focus_handler)
		tv_w.tv_focus->set_editable_flags(
			(*tv_w.tv_sts.focus_handler)(
				FHT_CLICK_BC,this,0,
				hilite_nos,0,tv_w.tv_sts.focus_handler_arg));
	else 
		tv_w.tv_focus->set_editable_flags(0);
	VM_OP_END
}

V_CALLBACK_D(tv_focus_command_status)
{
VxlTreeView * tv;
	tv = static_cast<VxlTreeView*>(user_arg);
	tv->_tv_focus_command_status();
}

void
VxlTreeView::_disable_focus_handler(bool type)
{
VObjectStatus sts;
BC_LIST * bc;
	_VM_OP_START_VOID
	if ( type ) {
		for ( bc = tv_w.bc ; bc ; bc = bc->next ) {
			if ( bc->selected == 0 )
				continue;
			sts.attr = vobject_get_hilite_color();
			bc->bc->set_status(&sts,VSF_ATTR);
		}
	}
	else {
		for ( bc = tv_w.bc ; bc ; bc = bc->next ) {
			if ( bc->selected == 0 )
				continue;
			SET_RGB8_32(sts.attr,
				BACK_COLOR_G_R,BACK_COLOR_G_G,
				BACK_COLOR_G_B,BACK_COLOR_G_A);
			bc->bc->set_status(&sts,VSF_ATTR);
		}
	}
	VM_OP_END
}

V_CALLBACK_D(tv_disable_focus_handler)
{
VxlTreeView * tv;
	tv = (VxlTreeView*)user_arg;
	tv->_disable_focus_handler(*(bool*)sys_arg);
}





VExError
VxlTreeView::create_do(const VObjectStatus* s, int flags,VObject * nmp, void * arg)
{
VExError err;
VObjectAppStatusAry * app;
VxlTreeViewStatus * t_sts;
int t_flags;
VObjectStatus _sts;

	err = initial_VExError(V_ER_NO_ERR,VSF_SPACING,0);

	info = 0;

	memset(&tv_w.tv_sts,0,sizeof(tv_w.tv_sts));
	if ( arg ) {
		app = (VObjectAppStatusAry*)arg;
		t_sts = (VxlTreeViewStatus*)app[0].sts;
		t_flags = app[0].flags;
		if ( t_sts == 0 ) {
			t_sts = 0;
			t_flags = 0;
		}
	}
	copy_VxlTreeViewStatus(&tv_w.tv_sts,t_sts,t_flags);
	copy_vobject_status(&_sts,(VObjectStatus*)s,flags);
	tv_w.bc = 0;
	_sts.parent = this;
	if ( t_flags & VSF_XL_TREEVIEW_FOCUS ) {
		tv_w.tv_focus = VFocusView::create(&_sts,
				flags|VSF_PARENT|VSF_SPACING);
		tv_w.tv_focus->set_obey_command_handler(tv_focus_obey_command, (void*)this);
		tv_w.tv_focus->set_command_status_handler(tv_focus_command_status, (void*)this);
		tv_w.tv_focus->set_focused_event_handler(tv_disable_focus_handler, (void*)this);
		_sts.parent = tv_w.tv_focus;
		_sts.padding.w = _sts.padding.h = 4;
		_sts.spacing.w = _sts.spacing.h = 0;
		_sts.visible = 1;
		flags |= VSF_PADDING|VSF_VISIBLE;
	}
	else	tv_w.tv_focus = 0;
	if ( t_flags & VSF_XL_TREEVIEW_SCROLL ) {
		tv_w.tv_scroll = VScrollView::create(&_sts,
				flags|VSF_PARENT|VSF_SPACING);
		_sts.parent = tv_w.tv_scroll;
		_sts.alignv = VALIGN_TOP;
		_sts.alignh = VALIGN_FILL;
		_sts.padding.w = _sts.padding.h = 0;
		_sts.spacing.w = _sts.spacing.h = 0;
		_sts.visible = 1;
		flags |= VSF_PADDING|VSF_VISIBLE;
	}
	else	tv_w.tv_scroll = 0;

	if ( tv_w.tv_sts.dir & VSD_V ) {
		tv_w.tv_obj = VTableView::create(&_sts,
				flags|VSF_PARENT,
				2,0,&err);
	}
	else {
		tv_w.tv_obj = VTableView::create(&_sts,
				flags|VSF_PARENT,
				0,2,&err);
	}
	tv_w.tv_sts.line_nos = 0;
	tv_w.tv_lines = (LINE_LIST*)d_alloc(sizeof(LINE_LIST));
	return err;

}





void
VxlTreeView::destroy_do(VObject * nmp)
{
	if ( tv_w.tv_lines )
		d_f_ree(tv_w.tv_lines);
	tv_w.tv_lines = 0;
	tv_w.tv_obj = 0;
}

void
VxlTreeView::destroy_do_out_of_lock(VObject * nmp)
{
BC_LIST * bc;
	vm_lock((VMacro*)(this),__FILE__,__LINE__);
	for ( ; tv_w.bc ; ) {
		bc = tv_w.bc;
		tv_w.bc = bc->next;
		d_f_ree(bc);
	}
	vm_unlock((VMacro*)this);
}

void
VxlTreeView::redraw(VRect * rect) const
{
}

VxlTreeView::~VxlTreeView()
{
}

VExError
VxlTreeView::set_status(const VObjectStatus * s,int flags)
{
	return VMacro::set_status(s, flags);
}

VExError
VxlTreeView::get_status(VObjectStatus * s,int flags) const
{
	return VMacro::get_status(s,flags);
}

void
tv_lines_setup(VxlTreeView_work * w,int line_no)
{
int new_line_no;
int i;
	if ( line_no >= w->tv_sts.line_nos ) {
		new_line_no = line_no + 1;
		if ( w->tv_sts.dir & VSD_H )
			w->tv_obj->resize_table(2*new_line_no,2);
		else	w->tv_obj->resize_table(2,2*new_line_no);
		w->tv_lines = (LINE_LIST*)d_re_alloc(w->tv_lines,new_line_no*sizeof(LINE_LIST));
		for ( i = w->tv_sts.line_nos ; i < new_line_no ; i ++ ) {
			w->tv_lines[i].obj = 0;
			w->tv_lines[i].child = 0;
			w->tv_lines[i].arc = 0;
		}
		w->tv_sts.line_nos = new_line_no;
	}
}

void
tv_lines_setup_small(VxlTreeView_work * w,int line_no)
{
int new_line_no;
int i;
LINE_LIST * ll;
	if ( line_no < w->tv_sts.line_nos ) {
		new_line_no = line_no;
		for ( i = line_no ; i < w->tv_sts.line_nos ; i ++ ) {
			ll = &w->tv_lines[i];
			if ( ll->obj )
				ll->obj->destroy();
			if ( ll->child )
				ll->child->destroy();
			if ( ll->arc )
				ll->arc->destroy();
		}
		if ( w->tv_sts.dir & VSD_H )
			w->tv_obj->resize_table(2*new_line_no,2);
		else	w->tv_obj->resize_table(2,2*new_line_no);
		w->tv_lines = (LINE_LIST*)d_re_alloc(w->tv_lines,new_line_no*sizeof(LINE_LIST));
		w->tv_sts.line_nos = new_line_no;
	}
}

VExError
VxlTreeView::set_xltreeview_status(
			const VxlTreeViewStatus * tv_sts,
			int flags)
{
	VM_OP_START_EX
	if ( flags & VSF_XL_TREEVIEW_FOCUS ) {
		tv_w.tv_sts.focus_handler = tv_sts->focus_handler;
		tv_w.tv_sts.focus_handler_arg = tv_sts->focus_handler_arg;
	}
	if ( flags & VSF_XL_TREEVIEW_LINE_NOS ) {
		if ( tv_sts->line_nos > tv_w.tv_sts.line_nos )
			tv_lines_setup(&tv_w,tv_sts->line_nos-1);
		else if ( tv_sts->line_nos < tv_w.tv_sts.line_nos )
			tv_lines_setup_small(&tv_w,tv_sts->line_nos);
		flags &= ~VSF_XL_TREEVIEW_LINE_NOS;	
	}
	VM_OP_END
	return initial_VExError(V_ER_NO_ERR,flags,0);
}


VExError
VxlTreeView::get_xltreeview_status(
			VxlTreeViewStatus * s,
			int flags) const
{
	VM_OP_START_EX
	copy_VxlTreeViewStatus(s,(VxlTreeViewStatus*)&tv_w.tv_sts,flags);
	VM_OP_END
	return initial_VExError(V_ER_NO_ERR,0,0);
}

V_CALLBACK_D(tv_arc)
{
TV_CALLBACK * tvc;
	tvc = (TV_CALLBACK*)user_arg;
	tvc->tv_obj->_click_event_handler(tvc);
}

void
set_arc_callback(VxlTreeView * tv_obj,LINE_LIST * ll)
{
TV_CALLBACK * tvc;
VObjectStatus _sts;
	if ( ll->arc == 0 )
		return;
	tvc = (TV_CALLBACK*)d_alloc(sizeof(*tvc));
	tvc->tv_obj = tv_obj;
	tvc->ll = *ll;
	_sts.value_event_handler = tv_arc;
	_sts.value_eh_arg = (void*)tvc;
	ll->arc->set_status(&_sts,VSF_VALUE_EH);
	ll->arc->free_on_release(tvc,0);
}



void
tv_attach_line(VxlTreeView_work * w,int line_no)
{
LINE_LIST * ll;
	ll = &w->tv_lines[line_no];
	switch ( w->tv_sts.dir ) {
	case VSD_H_L2R:
		w->tv_obj->attach_child(ll->obj,2*line_no,1);
		break;
	case VSD_H_R2L:
		w->tv_obj->attach_child(ll->obj,2*line_no,0);
		break;
	case VSD_V_L2R:
		w->tv_obj->attach_child(ll->obj,1,2*line_no);
		break;
	case VSD_V_R2L:
		w->tv_obj->attach_child(ll->obj,1,
			2*(w->tv_sts.line_nos - line_no) - 1);
		break;
	}
}


void
tv_insert_lines(VxlTreeView * tv,VxlTreeView_work * w,int line_no )
{
int i;
LINE_LIST * ll;
	for ( i = w->tv_sts.line_nos - 2 ; i >= line_no  ; i -- )
		w->tv_lines[i+1] = w->tv_lines[i];
	w->tv_lines[line_no].obj = 0;
	w->tv_lines[line_no].child = 0;
	w->tv_lines[line_no].arc = 0;

	switch ( w->tv_sts.dir ) {
	case VSD_H_L2R:
		for ( i = w->tv_sts.line_nos - 1 ; i > line_no  ; i -- ) {
			ll = &w->tv_lines[i];
			if ( ll->obj )
				w->tv_obj->attach_child(ll->obj,2*i,1);
			if ( ll->child )
				w->tv_obj->attach_child(ll->child,2*i+1,1);
			if ( ll->arc ) {
				w->tv_obj->attach_child(ll->arc,2*i,0);
			}
		}
		break;
	case VSD_H_R2L:
		for ( i = w->tv_sts.line_nos - 1 ; i > line_no  ; i -- ) {
			ll = &w->tv_lines[i];
			if ( ll->obj )
				w->tv_obj->attach_child(ll->obj,2*i,0);
			if ( ll->child )
				w->tv_obj->attach_child(ll->child,2*i+1,0);
			if ( ll->arc ) {
				w->tv_obj->attach_child(ll->arc,2*i,1);
			}
		}
		break;
	case VSD_V_L2R:
		for ( i = w->tv_sts.line_nos - 1 ; i > line_no  ; i -- ) {
			ll = &w->tv_lines[i];
			if ( ll->obj )
				w->tv_obj->attach_child(ll->obj,1,2*i);
			if ( ll->child )
				w->tv_obj->attach_child(ll->child,1,2*i+1);
			if ( ll->arc ) {
				w->tv_obj->attach_child(ll->arc,0,2*i);
			}
		}
		break;
	case VSD_V_R2L:
		for ( i = w->tv_sts.line_nos - 1 ; i > line_no  ; i -- ) {
			ll = &w->tv_lines[i];
			if ( ll->obj )
				w->tv_obj->attach_child(ll->obj,1,
					2*(w->tv_sts.line_nos - i) - 1);
			if ( ll->child )
				w->tv_obj->attach_child(ll->child,1,
					2*(w->tv_sts.line_nos - i) - 2);
			if ( ll->arc ) {
				w->tv_obj->attach_child(ll->arc,0,
					2*(w->tv_sts.line_nos - i) - 1);
			}
		}
		break;
	}
}

void
tv_delete_lines(VxlTreeView * tv,VxlTreeView_work * w,int line_no )
{
int i;
LINE_LIST * ll;
	ll = &w->tv_lines[line_no];
	if ( ll->obj )
		ll->obj->destroy();
	if ( ll->child )
		ll->child->destroy();
	if ( ll->arc )
		ll->arc->destroy();
	for ( i = line_no ; i < w->tv_sts.line_nos-1 ; i ++ )
		w->tv_lines[i] = w->tv_lines[i+1];
	w->tv_lines = (LINE_LIST*)d_re_alloc(w->tv_lines,
			sizeof(LINE_LIST)*w->tv_sts.line_nos-1);
	w->tv_sts.line_nos --;

	switch ( w->tv_sts.dir ) {
	case VSD_H_L2R:
		for ( i = line_no ; i < w->tv_sts.line_nos  ; i ++ ) {
			ll = &w->tv_lines[i];
			if ( ll->obj )
				w->tv_obj->attach_child(ll->obj,2*i,1);
			if ( ll->child )
				w->tv_obj->attach_child(ll->child,2*i+1,1);
			if ( ll->arc ) {
				w->tv_obj->attach_child(ll->arc,2*i,0);
			}
		}
		break;
	case VSD_H_R2L:
		for ( i = line_no ; i < w->tv_sts.line_nos  ; i ++ ) {
			ll = &w->tv_lines[i];
			if ( ll->obj )
				w->tv_obj->attach_child(ll->obj,2*i,0);
			if ( ll->child )
				w->tv_obj->attach_child(ll->child,2*i+1,0);
			if ( ll->arc ) {
				w->tv_obj->attach_child(ll->arc,2*i,1);
			}
		}
		break;
	case VSD_V_L2R:
		for ( i = line_no ; i < w->tv_sts.line_nos  ; i ++ ) {
			ll = &w->tv_lines[i];
			if ( ll->obj )
				w->tv_obj->attach_child(ll->obj,1,2*i);
			if ( ll->child )
				w->tv_obj->attach_child(ll->child,1,2*i+1);
			if ( ll->arc ) {
				w->tv_obj->attach_child(ll->arc,0,2*i);
			}
		}
		break;
	case VSD_V_R2L:
		for ( i = line_no ; i < w->tv_sts.line_nos  ; i ++ ) {
			ll = &w->tv_lines[i];
			if ( ll->obj )
				w->tv_obj->attach_child(ll->obj,1,
					2*(w->tv_sts.line_nos - i) - 1);
			if ( ll->child )
				w->tv_obj->attach_child(ll->child,1,
					2*(w->tv_sts.line_nos - i) - 2);
			if ( ll->arc ) {
				w->tv_obj->attach_child(ll->arc,0,
					2*(w->tv_sts.line_nos - i) - 1);
			}
		}
		break;
	}
}


VImage *
direct_copy(VxlTreeView_work * wk,int no)
{
VImage * ret;
int w_border,h,ww;
int x,y;
int fx,fy;
	ret = v_image_new(ww=wk->tv_sts.elr[no].r - wk->tv_sts.elr[no].l,
			h=wk->tv_sts.elr[no].b - wk->tv_sts.elr[no].t,32);
	w_border = wk->tv_sts.img->w_border;
	v_image_draw_start(ret,0);
	v_image_draw_start(wk->tv_sts.img,0);
	for ( x = 0 ; x < ww ; x ++ ) {
		fx = x + wk->tv_sts.elr[no].l;
		for ( y = 0 ; y < h ; y ++ ) {
			fy = y + wk->tv_sts.elr[no].t;
			ret->buf_32[x + ret->w_border*y]
				= wk->tv_sts.img->buf_32[fx + fy*w_border];
		}
	}
	v_image_draw_end(wk->tv_sts.img);
	v_image_draw_end(ret);
	return ret;
}

VImage *
mirror_copy(VxlTreeView_work * wk,int no)
{
VImage * ret;
int w_border,h,ww;
int x,y;
int fx,fy;
	ret = v_image_new(ww=wk->tv_sts.elr[no].r - wk->tv_sts.elr[no].l,
			h=wk->tv_sts.elr[no].b - wk->tv_sts.elr[no].t,32);
	w_border = wk->tv_sts.img->w_border;
	v_image_draw_start(ret,0);
	v_image_draw_start(wk->tv_sts.img,0);
	for ( x = 0 ; x < ww ; x ++ ) {
		fx = x + wk->tv_sts.elr[no].l;
		for ( y = 0 ; y < h ; y ++ ) {
			fy = y + wk->tv_sts.elr[no].t;
			ret->buf_32[ww - x - 1 + ret->w_border*y]
				= wk->tv_sts.img->buf_32[fx + fy*w_border];
		}
	}
	v_image_draw_end(wk->tv_sts.img);
	v_image_draw_end(ret);
	return ret;
}

VImage *
get_arc_element(VxlTreeView_work * w,int no)
{
	switch ( w->tv_sts.dir ) {
	case VSD_H_L2R:
		switch ( no ) {
		case 0:
			return direct_copy(w,1);
		case 1:
			return direct_copy(w,0);
		case 2:
			return direct_copy(w,2);
		}
		break;
	case VSD_H_R2L:
		switch ( no ) {
		case 0:
			return mirror_copy(w,1);
		case 1:
			return mirror_copy(w,0);
		case 2:
			return mirror_copy(w,2);
		}
		break;
	case VSD_V_L2R:
		switch ( no ) {
		case 0:
			return direct_copy(w,2);
		case 1:
			return direct_copy(w,0);
		case 2:
			return direct_copy(w,1);
		}
		break;
	case VSD_V_R2L:
		switch ( no ) {
		case 0:
			return mirror_copy(w,2);
		case 1:
			return mirror_copy(w,0);
		case 2:
			return mirror_copy(w,1);
		}
		break;
	}
	er_panic("get_arc_element");
	return 0;
}


VexDraw *
_new_arc(VxlTreeView_work * w)
{
VexDraw * ret;
VImage ** imgs;
VObjectStatus _sts;
int j;

	imgs = (VImage**)d_alloc(sizeof(VImage*)*3);
	for (  j = 0 ; j < 3 ; j ++ )
		imgs[j] = get_arc_element(w,j);

	_sts.parent = w->tv_obj;
	_sts.alignv = VALIGN_TOP;
	_sts.alignh = VALIGN_LEFT;
	_sts.padding.w = _sts.padding.h = 0;
	_sts.spacing.w = _sts.spacing.h = 0;
	_sts.size = _sts.min_size = imgs[0]->size;
	_sts.attr = VexDraw::click_rotate;
	_sts.attr |= VexDraw::press_img_zero;
	ret = VexDraw::create(&_sts,
			VSF_PARENT|VSF_ALIGN|VSF_SPACING|
			VSF_PADDING|VSF_MIN_SIZE|VSF_SIZE|
			VSF_ATTR);
	ret->set_images(3,imgs,imgs[0]->size);
	for ( j = 0 ; j < 3 ;j ++ ) {
		v_image_unref(imgs[j]);
	}
	d_f_ree(imgs);
	return ret;
}


void
tv_attach_child(VxlTreeView_work * w,int line_no)
{
LINE_LIST * ll;
VObjectStatus _sts;
VexDraw * arc;
	ll = &w->tv_lines[line_no];
	arc = ll->arc;
	if ( ll->arc == 0 )
		ll->arc = _new_arc(w);
	ll->child->get_status(&_sts,VSF_VISIBLE);
	if ( _sts.visible ) {
		_sts.value = 2;
		ll->arc->set_status(&_sts,VSF_VALUE);
	}
	else {
		_sts.value = 1;
		ll->arc->set_status(&_sts,VSF_VALUE);
	}
	switch ( w->tv_sts.dir ) {
	case VSD_H_L2R:
		w->tv_obj->attach_child(ll->child,2*line_no+1,1);
		if ( arc == 0 )
			w->tv_obj->attach_child(ll->arc,2*line_no,0);
		break;
	case VSD_H_R2L:
		w->tv_obj->attach_child(ll->child,2*line_no+1,0);
		if ( arc == 0 )
			w->tv_obj->attach_child(ll->arc,2*line_no,1);
		break;
	case VSD_V_L2R:
		w->tv_obj->attach_child(ll->child,1,2*line_no+1);
		if ( arc == 0 )
			w->tv_obj->attach_child(ll->arc,0,2*line_no);
		break;
	case VSD_V_R2L:
		w->tv_obj->attach_child(ll->child,1,
			2*(w->tv_sts.line_nos - line_no) - 2);
		if ( arc == 0 )
			w->tv_obj->attach_child(ll->arc,0,
				2*(w->tv_sts.line_nos - line_no) - 1);
		break;
	}
}


void
VxlTreeView::_click_event_handler(TV_CALLBACK * tvc)
{
VObjectStatus _sts;
	_VM_OP_START_VOID
	if ( tvc->ll.arc == 0 || tvc->ll.child == 0 )
		goto end;
	tvc->ll.arc->get_status(&_sts,VSF_VALUE);
	switch ( _sts.value ) {
	case 1:
		_sts.visible = 0;
		break;
	case 2:
		_sts.visible = 1;
		break;
	}
	tvc->ll.child->set_status(&_sts,VSF_VISIBLE);

end:
	VM_OP_END
}

VExError
VxlTreeView::attach_line(VObject * child,int line_no)
{
LINE_LIST * ll;
	VM_OP_START_EX
	if ( line_no < 0 )
		line_no = tv_w.tv_sts.line_nos;
	tv_lines_setup(&tv_w,line_no);
	ll = &tv_w.tv_lines[line_no];
	if ( ll->obj )
		ll->obj->destroy();
	ll->obj = child;
	tv_attach_line(&tv_w,line_no);
	set_arc_callback(this,ll);
	VM_OP_END
	return initial_VExError(V_ER_NO_ERR,0,0);
}

VExError
VxlTreeView::attach_child(VObject * child,int line_no)
{
LINE_LIST * ll;
	VM_OP_START_EX
	if ( line_no < 0 )
		line_no = tv_w.tv_sts.line_nos - 1;
	tv_lines_setup(&tv_w,line_no);
	ll = &tv_w.tv_lines[line_no];
	if ( ll->child )
		ll->child->destroy();

	ll->child = child;
	if ( ll->child == 0 ) {
		ll->arc->destroy();
		ll->arc = 0;
	}
	else 	tv_attach_child(&tv_w,line_no);
	set_arc_callback(this,ll);
	VM_OP_END
	return initial_VExError(V_ER_NO_ERR,0,0);
}

VExError
VxlTreeView::insert_line(VObject * child,int line_no)
{
int last_line;
LINE_LIST * ll;
	VM_OP_START_EX

	if ( line_no < 0 )
		line_no = tv_w.tv_sts.line_nos;
	last_line = line_no > tv_w.tv_sts.line_nos ?
			line_no : tv_w.tv_sts.line_nos;
	tv_lines_setup(&tv_w,last_line);
	tv_insert_lines(this,&tv_w,line_no);
	ll = &tv_w.tv_lines[line_no];
	if ( ll->obj )
		ll->obj->destroy();

	ll->obj = child;
	tv_attach_line(&tv_w,line_no);
	set_arc_callback(this,ll);
	VM_OP_END
	return initial_VExError(V_ER_NO_ERR,0,0);

}


VExError
VxlTreeView::delete_line(int line_no)
{
	VM_OP_START_EX
	if ( line_no < 0 )
		line_no = tv_w.tv_sts.line_nos-1;
	if ( line_no < 0 )
		goto end;
	if ( line_no >= tv_w.tv_sts.line_nos )
		goto end;
	tv_delete_lines(this,&tv_w,line_no);
end:
	VM_OP_END
	return initial_VExError(V_ER_NO_ERR,0,0);

}


VObject *
VxlTreeView::macro_base_parent()
{
	return tv_w.tv_obj;
}


void
VxlTreeView::_tv_bc_destroy_handler(VObject * object)
{
BC_LIST ** bcp,*bc;
	_VM_OP_START_VOID
	for ( bcp = &tv_w.bc ; *bcp ; bcp = &(*bcp)->next ) {
		if ( (*bcp)->bc != (VBackColorView*)object )
			continue;
		bc = *bcp;
		*bcp = (*bcp)->next;
		d_f_ree(bc);
		break;
	}
	VM_OP_END
}

V_CALLBACK_D(tv_bc_destroy_handler)
{
VxlTreeView * tv;
	tv = (VxlTreeView*)user_arg;
	tv->_tv_bc_destroy_handler(object);
}


VObject *
VxlTreeView::get_backcolorview(VObjectStatus * f_sts,int flags)
{
VObjectStatus _sts;
BC_LIST * bc;
VBackColorView * ret;

	_VM_OP_START_ZERO
	copy_vobject_status(&_sts,
		(const VObjectStatus*)f_sts,flags);
	if ( (flags & VSF_PARENT) == 0 )
		_sts.parent = tv_w.tv_obj;
	_sts.value_event_handler = tv_backcolor_handler;
	_sts.value_eh_arg = (void*)this;
	_sts.destroy_handler = tv_bc_destroy_handler;
	_sts.destroy_h_arg = (void*)this;
	if ( (flags & VSF_ALIGN) == 0 ) {
		_sts.alignv = VALIGN_TOP;
		_sts.alignh = VALIGN_FILL;
	}
	if ( (flags & VSF_PADDING) == 0 )
		_sts.padding.w = _sts.padding.h = 0;
	if ( (flags & VSF_PADDING) == 0 )
		_sts.spacing.w = _sts.spacing.h = 0;
	SET_RGB8_32(_sts.attr,0,0,0,0);
	ret = VBackColorView::create(&_sts,
		VSF_PARENT|VSF_ATTR|VSF_PADDING|VSF_SPACING|VSF_ALIGN|VSF_VALUE_EH|flags);
	bc = (BC_LIST*)d_alloc(sizeof(*bc));
	bc->bc = ret;
	bc->selected = 0;
	bc->next = tv_w.bc;
	tv_w.bc = bc;
	VM_OP_END
	return ret;
}


