/**********************************************************************
 
	Copyright (C) 2003-2004
	Hirohisa MORI <joshua@nichibun.ac.jp>
	Tomoki SEKIYAMA <sekiyama@yahoo.co.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 <stdlib.h>

#include "v/VWindow.h"
#include "v/VTableView.h"
#include "v/VScrollView.h"
#include "v/VSplitView.h"
#include "v/VLayout.h"

extern "C" {
#include "task.h"
}

void layout_calc_align_homogen(
	const short	nos,
	const short p_min_size,
	const short p_size,
	const short	p_spacing,
	const short	pad_list[],
	const char	align_list[],
	short		x_list[],
	short		w_list[]);
void layout_calc_align(
	const short	nos,
	const short p_min_size,
	const short p_size,
	const short	p_spacing,
	const short	pad_list[],
	const char	align_list[],
	short		x_list[],
	short		w_list[]);
int table_layout_sort_rows(const void *a0, const void *b0);
int table_layout_sort_cols(const void *a0, const void *b0);
void layout_calc_one(
	const short	p_size,
	const short	p_spacing,
	const short	padding,
	const char	align,
	const short	min_size,
	short&		tl,
	short&		br);


VObjectList*		VLayout::marked = 0;

SEM v_layout_lock;

VLayout::VLayout()
{
	nosChildren = 0;
	childrenRect = 0;
	margin_tl.w = margin_tl.h = 0;
	margin_br.w = margin_br.h = 0;
	sts.children = 0;
}

VLayout::~VLayout()
{
	if ( childrenRect )
		delete childrenRect;
	free_vobject_status(&sts);
}

void
layout_calc_one(
	const short	p_size,
	const short	p_spacing,
	const short	padding,
	const char	align,
	const short	min_size,
	short&		tl,
	short&		br)
{
	switch ( align ) {

	case VALIGN_LEFT:
		tl = p_spacing + padding;
		br = tl + min_size;
		break;

	case VALIGN_RIGHT:
		br = p_size - p_spacing - padding;
		tl = br - min_size;
		break;

	case VALIGN_EXPAND:
		tl = (p_size - min_size)/2;
		br = tl + min_size;
		break;

	default:
		tl = p_spacing + padding;
		br = p_size - tl;
	}
}


void
layout_calc_align_homogen(
	const short	nos,
	const short p_min_size,
	const short p_size,
	const short	p_spacing,
	const short	pad_list[],
	const char	align_list[],
	short		x_list[],
	short		w_list[])
{
	float x,w;
	short l, r;
	x = 0;
	w = (float)(p_size - p_spacing*(nos-1)) / nos;
	
	for ( int i = 0 ; i < nos ; i++ ) {
		layout_calc_one((short)w, 0, pad_list[i], align_list[i], w_list[i], l, r);
		x_list[i] = (short)x+l;
		w_list[i] = r-l;
		x += w+p_spacing;
	}
}


void
layout_calc_align(
	const short	nos,
	const short p_min_size,
	const short p_size,
	const short	p_spacing,
	const short	pad_list[],
	const char	align_list[],
	short		x_list[],
	short		w_list[])
{
	int i, r_start;
	float x = 0;

	int nos_ex = 0;
	for ( i = 0 ; i < nos ; i++ )
		if ( align_list[i] == VALIGN_EXPAND || align_list[i] == VALIGN_FILL )
			nos_ex++;
	if ( nos_ex > 0 ) {
		float ex = (float)(p_size - p_min_size) / nos_ex / 2;
		for ( i = 0 ; i < nos ; i++ ) {
			x += pad_list[i];
			x_list[i] = (short)x;
			x += w_list[i] + pad_list[i] + p_spacing;
			if ( align_list[i] == VALIGN_EXPAND ) {
				x_list[i] += (short)ex;
				x += ex*2;
			}
			else if ( align_list[i] == VALIGN_FILL ) {
				w_list[i] += (short)(ex*2);
				x += ex*2;
			}
		}
		return;
	}

	i = 0;
	while( align_list[i] == VALIGN_LEFT ) {
		x += pad_list[i];
		x_list[i] = (short)x;
		x += w_list[i] + pad_list[i] + p_spacing;
		if ( ++i == nos )
			return;
	}
	r_start = i;

	i = nos-1;
	x = p_size;
	do {
		x -= pad_list[i] + w_list[i];
		x_list[i] = (short)x;
		x -= pad_list[i] + p_spacing;
	} while ( --i >= r_start );
}




void
VLayout::layout_in_frame(VObject* obj, bool onlyMinSize, int child_index)
{
	VObjectList *target_l;
	VObject *target;
	VObjectStatus child;

	obj->get_status(&sts, VSF_SIZE | VSF_CHILDREN | VSF_SPACING);

	// specialization for VScrollView
	VScrollView* scv = dynamic_cast<VScrollView*>(obj);
	if ( scv )
		sts.size = scv->get_internal_size_cur();

	parentSize = sts.size;

	if ( childrenRect ) {
		delete childrenRect;
		childrenRect = 0;
	}
	
	if ( sts.children == 0 ) {
		nosChildren = 0;
		margin_tl.w = margin_tl.h = 0;
		margin_br.w = margin_br.h = 0;
		parentMinSize.w = 0;
		parentMinSize.h = 0;
		return;
	}
	
	target_l = sts.children;
	while ( child_index-- && target_l ) {
		target_l = target_l->next;
	}
	target = target_l->object ? target_l->object->get_nmc(0) : 0;
	if ( target == 0 )
		return;
	free_vobject_status(&sts);
	sts.children = new VObjectList;
	sts.children->object = target;
	sts.children->next = 0;

	childrenRect = new VRect;
	nosChildren = 1;
	
	target->get_status(&child, VSF_VISIBLE | VSF_MIN_SIZE | VSF_PADDING | VSF_ALIGN);
	
	if ( child.visible ) {
		parentMinSize.w = child.min_size.w + child.padding.w*2 + sts.spacing.w*2;
		parentMinSize.h = child.min_size.h + child.padding.h*2 + sts.spacing.h*2;
	}
	else {
		parentMinSize.w = sts.spacing.w*2;
		parentMinSize.h = sts.spacing.h*2;
	}
	
	if ( onlyMinSize ) {
		enlarge_parent(margin_tl);
		enlarge_parent(margin_br);
		free_vobject_status(&child);
		return;
	}
	
	if ( sts.size.w < parentMinSize.w + margin_tl.w + margin_br.w )
		parentSize.w = parentMinSize.w;
	if ( sts.size.h < parentMinSize.h + margin_tl.h + margin_br.h )
		parentSize.h = parentMinSize.h;

	ensmall_parent(margin_tl);
	ensmall_parent(margin_br);

	layout_calc_one(parentSize.w, sts.spacing.w, child.padding.w, child.alignh, child.min_size.w,
					childrenRect->l, childrenRect->r);
	layout_calc_one(parentSize.h, sts.spacing.h, child.padding.h, child.alignv, child.min_size.h,
					childrenRect->t, childrenRect->b);
	
	free_vobject_status(&child);
}



void
VLayout::layout_in_align_view(VObject* obj, bool vert, bool onlyMinSize)
{
	VObjectStatus child;
	VObjectList *list;
	int i;
	
	obj->get_status(&sts, VSF_SIZE | VSF_CHILDREN | VSF_SPACING | VSF_HOMOGEN);
	convert_to_nmc_list(&sts.children);

	parentSize = sts.size;

	if ( childrenRect ) {
		delete childrenRect;
		childrenRect = 0;
	}
	
	if ( sts.children == 0 ) {
		nosChildren = 0;
		parentMinSize.w = 0;
		parentMinSize.h = 0;
		return;
	}
	
	nosChildren = 0;
	for ( list = sts.children ; list ; list = list->next )
		nosChildren++;
	childrenRect = new VRect[nosChildren];
	
	short *x_list = new short[nosChildren];
	short *w_list = new short[nosChildren];
	short *p_list = new short[nosChildren];
	short *tp_list = new short[nosChildren];
	char *align_list = new char[nosChildren];
	int sum_size = 0;
	int sum_pad = 0;
	short tmax = 0;
	short tpad_max = 0;

	VObjectList *prev = 0, *next;
	for ( i = 0, list = sts.children ; list ; i++, list = next ) {

		next = list->next;
		list->object->get_status(&child, 
				VSF_VISIBLE | VSF_MIN_SIZE | VSF_PADDING | VSF_ALIGN);
		if ( ! child.visible ) {
			nosChildren--;
			next = vobject_list_remove(list, prev, &sts.children);
			continue;
		}
		prev = list;
		
		w_list[i] = !vert ? child.min_size.w : child.min_size.h;
		sum_pad += p_list[i] = !vert ? child.padding.w : child.padding.h;
		align_list[i] = !vert ? child.alignh : child.alignv;
		sum_size += w_list[i];
		if ( tmax < (vert ? child.min_size.w : child.min_size.h) )
			tmax = vert ? child.min_size.w : child.min_size.h;
		if ( tpad_max < (vert ? child.padding.w : child.padding.h) )
			tpad_max = vert ? child.padding.w : child.padding.h;
		tp_list[i] = vert ? child.padding.w : child.padding.h;
		free_vobject_status(&child);
	}
	
	if ( nosChildren == 0 ) {	// no visible children
		parentMinSize.w = 0;
		parentMinSize.h = 0;
		goto end;
	}
	
	if ( !vert ) {
		parentMinSize.w = sum_size + sum_pad*2 + sts.spacing.w*(nosChildren-1);
		parentMinSize.h = tmax + tpad_max*2;
	}
	else {
		parentMinSize.w = tmax + tpad_max*2;
		parentMinSize.h = sum_size + sum_pad*2 + sts.spacing.h*(nosChildren-1);
	}
	
	if ( onlyMinSize ) {
		enlarge_parent(margin_tl);
		enlarge_parent(margin_br);
		goto end;
	}
	
	if ( sts.size.w < parentMinSize.w + margin_tl.w + margin_br.w )
		parentSize.w = parentMinSize.w;
	if ( sts.size.h < parentMinSize.h + margin_tl.h + margin_br.h )
		parentSize.h = parentMinSize.h;
	
	ensmall_parent(margin_tl);
	ensmall_parent(margin_br);

	if ( sts.homogeneous ) {
		if ( !vert )
			layout_calc_align_homogen(nosChildren, parentMinSize.w, parentSize.w,
							sts.spacing.w, p_list, align_list, x_list, w_list);
		else
			layout_calc_align_homogen(nosChildren, parentMinSize.h, parentSize.h,
							sts.spacing.h, p_list, align_list, x_list, w_list);
	}
	else {
		if ( !vert )
			layout_calc_align(nosChildren, parentMinSize.w, parentSize.w,
							sts.spacing.w,
							p_list, align_list, x_list, w_list);
		else
			layout_calc_align(nosChildren, parentMinSize.h, parentSize.h,
							sts.spacing.h,
							p_list, align_list, x_list, w_list);
	}
	
	for ( i = 0, list = sts.children ; list ; i++, list = list->next ) {
		childrenRect[i].l = !vert ? x_list[i] : tp_list[i];
		childrenRect[i].t = !vert ? tp_list[i] : x_list[i];
		childrenRect[i].r = !vert ? x_list[i] + w_list[i] : parentSize.w - tp_list[i];
		childrenRect[i].b = !vert ? parentSize.h - tp_list[i] : x_list[i] + w_list[i];
	}

end:
	delete[] x_list;
	delete[] w_list;
	delete[] p_list;
	delete[] tp_list;
	delete[] align_list;
}


struct table_layout {
	short x, y, w, h;
	short pw, ph;
	unsigned alignh:2, alignv:2;
	short row, col, rs, cs;
};

int
table_layout_sort_rows(const void *a0, const void *b0)
{
	const table_layout *a = *(const table_layout **)a0;
	const table_layout *b = *(const table_layout **)b0;
	if ( a->rs > b->rs )
		return 1;
	if ( a->rs < b->rs )
		return -1;
	return 0;
}

int
table_layout_sort_cols(const void *a0, const void *b0)
{
	const table_layout *a = *(const table_layout **)a0;
	const table_layout *b = *(const table_layout **)b0;
	if ( a->cs > b->cs )
		return 1;
	if ( a->cs < b->cs )
		return -1;
	return 0;
}

void
VLayout::layout_in_table_view(VObject* obj, bool onlyMinSize)
{
	VObjectStatus child;
	VObjectList *list;
	VTableView *table = static_cast<VTableView*>(obj);
	int i, j, k;
	int cols2expand = 0;
	int rows2expand = 0;
	float cw = 0, ch = 0, ex, f;
	
	table->get_status(&sts, VSF_SIZE | VSF_CHILDREN | VSF_SPACING | VSF_HOMOGEN);
	convert_to_nmc_list(&sts.children);

	parentSize = sts.size;

	if ( childrenRect ) {
		delete childrenRect;
		childrenRect = 0;
	}
	
	if ( sts.children == 0 ) {
		nosChildren = 0;
		parentMinSize.w = 0;
		parentMinSize.h = 0;
		return;
	}
	
	nosChildren = 0;
	for ( list = sts.children ; list ; list = list->next )
		nosChildren++;
	childrenRect = new VRect[nosChildren];
	
	short rows, cols;
	table->get_table_size(&rows, &cols);
	if ( rows < 1 || cols < 1 ) {
		parentMinSize.w = 0;
		parentMinSize.h = 0;
		return;
	}
	
	table_layout *tl = new table_layout[nosChildren];

	table_layout **c_order;
	table_layout **r_order;
	
	short *cell_x = new short[cols];
	short *cell_y = new short[rows];

	float *cell_w = new float[cols];
	float *cell_h = new float[rows];
	
	char *policy_col = new char[cols];	// 1-> expand when min_size < size
	char *policy_row = new char[rows];	// 0-> never expand
	
	for ( i = 0 ; i < rows ; i++ ) {
		policy_row[i] = 0;
		cell_h[i] = 0.0;
	}
	for ( i = 0 ; i < cols ; i++ ) {
		policy_col[i] = 0;
		cell_w[i] = 0.0;
	}
	
	float w_top = 0, h_top = 0;
	
	VObjectList *prev = 0, *next;
	for ( i = 0, list = sts.children ; list ; i++, list = next ) {
		next = list->next;
		VObject *target = list->object;
		target->get_status(&child,
				VSF_VISIBLE | VSF_MIN_SIZE | VSF_PADDING | VSF_ALIGN);
		if ( ! child.visible ) {
			nosChildren--;
			i--;
			next = vobject_list_remove(list, prev, &sts.children);
			continue;
		}
		prev = list;
		
		table->get_child_place(target, &tl[i].row, &tl[i].col, &tl[i].rs, &tl[i].cs);
		tl[i].w = child.min_size.w;
		tl[i].h = child.min_size.h;
		tl[i].pw = child.padding.w;
		tl[i].ph = child.padding.h;
		tl[i].alignh = child.alignh;
		tl[i].alignv = child.alignv;

		cw = (float)(tl[i].w + tl[i].pw*2) / tl[i].cs;
		if ( w_top < cw )
			w_top = cw;
		ch = (float)(tl[i].h + tl[i].ph*2) / tl[i].rs;
		if ( h_top < ch )
			h_top = ch;
		
		if ( tl[i].alignv == VALIGN_EXPAND || tl[i].alignv == VALIGN_FILL )
			for ( j = tl[i].row ; j < tl[i].row+tl[i].rs ; j++ )
				policy_row[j] = 1;
		if ( tl[i].alignh == VALIGN_EXPAND || tl[i].alignh == VALIGN_FILL )
			for ( j = tl[i].col ; j < tl[i].col+tl[i].cs ; j++ )
				policy_col[j] = 1;

		free_vobject_status(&child);
	}
	
	if ( nosChildren == 0 ) {	// no visible children
		parentMinSize.w = 0;
		parentMinSize.h = 0;
		goto end;
	}
	
	if ( sts.homogeneous ) {
		parentMinSize.w = (short)(w_top * cols) + sts.spacing.w * (cols-1);
		parentMinSize.h = (short)(h_top * rows) + sts.spacing.h * (rows-1);
	}
	else {
		// evaluate min cell size from the fewer occupation child
		c_order = new table_layout*[nosChildren];
		r_order = new table_layout*[nosChildren];
		for ( i = 0 ; i < nosChildren ; i++ )
			c_order[i] = r_order[i] = &tl[i];
		qsort(c_order, nosChildren, sizeof(table_layout*), table_layout_sort_cols);
		qsort(r_order, nosChildren, sizeof(table_layout*), table_layout_sort_rows);
		
		for ( i = 0 ; i < nosChildren ; i++ ) {
			short w = c_order[i]->w + c_order[i]->pw*2;
			cw = 0;
			for ( j = c_order[i]->col ; j < c_order[i]->col + c_order[i]->cs ; j++ )
				cw += cell_w[j];
			if ( cw < w ) {
				ex = (float)(w-cw)/c_order[i]->cs;
				for ( j = c_order[i]->col ; j < c_order[i]->col + c_order[i]->cs ; j++ )
					cell_w[j] += ex;
			}
		}

		for ( i = 0 ; i < nosChildren ; i++ ) {
			short h = r_order[i]->h + r_order[i]->ph*2;
			ch = 0;
			for ( j = r_order[i]->row ; j < r_order[i]->row + r_order[i]->rs ; j++ )
				ch += cell_h[j];
			if ( ch < h ) {
				ex = (float)(h-ch)/r_order[i]->rs;
				for ( j = r_order[i]->row ; j < r_order[i]->row + r_order[i]->rs ; j++ )
					cell_h[j] += ex;
			}
		}

		delete[] r_order;
		delete[] c_order;

		parentMinSize.w = sts.spacing.w * (cols-1);
		parentMinSize.h = sts.spacing.h * (rows-1);
		for ( i = 0 ; i < cols ; i++ )
			parentMinSize.w += (short)cell_w[i];
		for ( i = 0 ; i < rows ; i++ )
			parentMinSize.h += (short)cell_h[i];
	}
	
	if ( onlyMinSize ) {
		enlarge_parent(margin_tl);
		enlarge_parent(margin_br);
		goto end;
	}
	
	if ( sts.size.w < parentMinSize.w + margin_tl.w + margin_br.w )
		parentSize.w = parentMinSize.w;
	if ( sts.size.h < parentMinSize.h + margin_tl.h + margin_br.h )
		parentSize.h = parentMinSize.h;

	ensmall_parent(margin_tl);
	ensmall_parent(margin_br);

	if ( sts.homogeneous ) {
		for ( i = 0 ; i < cols ; i++ )
			cell_w[i] = w_top;
		for ( i = 0 ; i < rows ; i++ )
			cell_h[i] = h_top;
	}
	
	for ( i = 0 ; i < cols ; i++ )
		if ( policy_col[i] )
			cols2expand++;
	for ( i = 0 ; i < rows ; i++ )
		if ( policy_row[i] )
			rows2expand++;
	
	f = 0;
	ex = (float)(parentSize.w - parentMinSize.w) / cols2expand;
	for ( i = 0 ; i < cols ; i++ ) {
		cell_x[i] = (short)f;
		if ( policy_col[i] )
			cell_w[i] += ex;
		f += cell_w[i] + sts.spacing.w;
	}
	f = 0;
	ex = (float)(parentSize.h - parentMinSize.h) / rows2expand;
	for ( i = 0 ; i < rows ; i++ ) {
		cell_y[i] = (short)f;
		if ( policy_row[i] )
			cell_h[i] += ex;
		f += cell_h[i] + sts.spacing.h;
	}
	
	for ( i = 0, list = sts.children ; list ; i++, list = list->next ) {
		j = tl[i].col;
		k = j + tl[i].cs - 1;
		layout_calc_one((short)cell_w[k] + (short)cell_x[k]- (short)cell_x[j], 0,
					tl[i].pw, tl[i].alignh, tl[i].w,
					childrenRect[i].l, childrenRect[i].r);
		childrenRect[i].l += cell_x[j];
		childrenRect[i].r += cell_x[j];

		j = tl[i].row;
		k = j + tl[i].rs - 1;
		layout_calc_one((short)cell_h[k] + (short)cell_y[k]- (short)cell_y[j], 0,
					tl[i].ph, tl[i].alignv, tl[i].h,
					childrenRect[i].t, childrenRect[i].b);
		childrenRect[i].t += cell_y[j];
		childrenRect[i].b += cell_y[j];
	}

end:
	delete[] tl;
	delete[] cell_x;
	delete[] cell_y;
	delete[] cell_w;
	delete[] cell_h;
	delete[] policy_col;
	delete[] policy_row;
}

void
VLayout::layout_in_split_view(
		VObject* obj_a,
		bool vert,
		unsigned short &pos,
		unsigned short &min,
		unsigned short &max,
		char child_status[2],
		bool onlyMinSize)
{
	VSplitView *obj = (VSplitView*)obj_a;
	VObjectStatus child[2];
	VObject *c[2] = {0,0};
	int i;
	short x[2]={-1,-1}, y[2];
	bool vis[2] = {!(child_status[0] & VSplitView::child_hidden),
				   !(child_status[1] & VSplitView::child_hidden)};
	
	obj->get_status(&sts, VSF_CHILDREN | VSF_SIZE | VSF_ATTR);
	convert_to_nmc_list(&sts.children);
	for ( i = 0 ; i < 2 ; i++ )
		if ( vis[i] )
			c[i] = obj->get_child(i);

	parentSize = sts.size;

	if ( childrenRect ) {
		delete childrenRect;
		childrenRect = 0;
	}
	
	if ( ! (c[0] || c[1]) ) {
		nosChildren = 0;
		parentMinSize.w = 0;
		parentMinSize.h = 0;
		return;
	}
	
	nosChildren = 0;
	if ( c[0] )
		nosChildren++;
	if ( c[1] )
		nosChildren++;
	if ( sts.children && sts.children->next && !c[0] ) {
		vobject_list_remove(sts.children, 0, &sts.children);;
	}
	childrenRect = new VRect[nosChildren];
	
	int cur_c = 0;

	for ( i = 0 ; i < 2 ; i++ ) {
		if ( c[i] ) {
			c[i]->get_status(&child[i], 
					VSF_VISIBLE | VSF_MIN_SIZE | VSF_PADDING | VSF_ALIGN);
			if ( ! child[i].visible ) {
				nosChildren--;
			}
		}
	}

	if ( nosChildren == 0 ) {	// no visible children
		parentMinSize.w = 0;
		parentMinSize.h = 0;
		goto end;
	}
	
	if ( vert ) {
		if ( !vis[0] && !vis[1] )
			parentMinSize.h = 0;
		else
			parentMinSize.h = SPLIT_BAR_WIDTH;

		if ( pos == (unsigned short)-1 ) {
			for ( i = 0 ; i < 2 ; i++ )
				parentMinSize.h += x[i] = obj->get_def_size()[i];
		}
		for ( i = 0 ; i < 2 ; i++ ) {
			if ( x[i] >= 0 )
				continue;
			x[i] = c[i] ? child[i].min_size.h + child[i].padding.h*2 : 0;
			if ( c[i] && !(child_status[i] & VSplitView::child_shrinked) )
				parentMinSize.h += x[i];
		}

		parentMinSize.w = 0;
		for ( i = 0 ; i < 2 ; i++ ) {
			y[i] = c[i] ? child[i].min_size.w + child[i].padding.w*2 : 0;
			if ( c[i] && parentMinSize.w < y[i] )
				parentMinSize.w = y[i];
		}
	}
	else {
		if ( c[0] && c[1] )
			parentMinSize.w = SPLIT_BAR_WIDTH;
		else
			parentMinSize.w = 0;

		if ( pos == (unsigned short)-1 ) {
			for ( i = 0 ; i < 2 ; i++ )
				parentMinSize.w += x[i] = obj->get_def_size()[i];
		}
		for ( i = 0 ; i < 2 ; i++ ) {
			if ( x[i] >= 0 )
				continue;
			x[i] = c[i] ? child[i].min_size.w + child[i].padding.w*2 : 0;
			if ( c[i] && !(child_status[i] & VSplitView::child_shrinked) )
				parentMinSize.w += x[i];
		}

		parentMinSize.h = 0;
		for ( i = 0 ; i < 2 ; i++ ) {
			y[i] = c[i] ? child[i].min_size.h + child[i].padding.h*2 : 0;
			if ( c[i] && parentMinSize.h < y[i] )
				parentMinSize.h = y[i];
		}
	}

	if ( onlyMinSize ) {
		enlarge_parent(margin_tl);
		enlarge_parent(margin_br);
		goto end;
	}
	
	if ( sts.size.w < parentMinSize.w + margin_tl.w + margin_br.w )
		parentSize.w = parentMinSize.w;
	if ( sts.size.h < parentMinSize.h + margin_tl.h + margin_br.h )
		parentSize.h = parentMinSize.h;
	
	ensmall_parent(margin_tl);
	ensmall_parent(margin_br);


	if ( vis[0] && vis[1] ) {		// calc the split bar position and min/max
		min = vis[0] ? x[0] : 0;
		max = vis[1] ? (vert ? parentSize.h : parentSize.w) - x[1] - SPLIT_BAR_WIDTH
					 : 0xffff;
		if ( child_status[0] & VSplitView::child_shrinked )
			pos = 0;
		else if ( child_status[1] & VSplitView::child_shrinked )
			pos = (vert ? parentSize.h : parentSize.w) - SPLIT_BAR_WIDTH;
		else {
			VSize last_size = obj->get_last_size();
			if ( last_size.w > 0 && last_size.h > 0 ) {
				switch ( sts.attr & VSplitView::resize_mask ) {
				  case VSplitView::expand0_on_resize:
					pos += vert ? parentSize.h-last_size.h : parentSize.w-last_size.w;
					break;
				  case VSplitView::expand1_on_resize:
					break;
				  case VSplitView::keep_ratio_on_resize:
					if ( vert )
						pos = (unsigned long)pos * parentSize.h / last_size.h;
					else
						pos = (unsigned long)pos * parentSize.w / last_size.w;
					break;
				}
			}
			pos = (pos < min) ? min : (pos > max) ? max : pos;
		}
	}

	cur_c = 0;
	if ( c[0] ) {
		layout_calc_one( !vert && vis[1] ? pos : parentSize.w, 0,
						child[0].padding.w, child[0].alignh, child[0].min_size.w,
						childrenRect[cur_c].l, childrenRect[cur_c].r);
		layout_calc_one( vert && vis[1] ? pos : parentSize.h, 0,
						child[0].padding.h, child[0].alignv, child[0].min_size.h,
						childrenRect[cur_c].t, childrenRect[cur_c].b);
		cur_c++;
	}
	if ( c[1] ) {
		layout_calc_one(parentSize.w - (!vert && vis[0] ? pos+SPLIT_BAR_WIDTH : 0), 0,
						child[1].padding.w, child[1].alignh, child[1].min_size.w,
						childrenRect[cur_c].l, childrenRect[cur_c].r);
		layout_calc_one(parentSize.h - (vert && vis[0] ? pos+SPLIT_BAR_WIDTH : 0), 0,
						child[1].padding.h, child[1].alignv, child[1].min_size.h,
						childrenRect[cur_c].t, childrenRect[cur_c].b);
						
		if ( vis[0] ) {
			if ( !vert ) {
				childrenRect[cur_c].l += pos+SPLIT_BAR_WIDTH+1;
				childrenRect[cur_c].r += pos+SPLIT_BAR_WIDTH+1;
			}
			else {
				childrenRect[cur_c].t += pos+SPLIT_BAR_WIDTH+1;
				childrenRect[cur_c].b += pos+SPLIT_BAR_WIDTH+1;
			}
		}
		cur_c++;
	}

end:
	if ( c[0] )
		free_vobject_status(&child[0]);
	if ( c[1] )
		free_vobject_status(&child[1]);
}


void
VLayout::enlarge_parent(VSize d)
{
	parentMinSize.w += d.w;
	parentMinSize.h += d.h;
}

void
VLayout::ensmall_parent(VSize d)
{
	parentSize.w -= d.w;
	parentSize.h -= d.h;
}

void
VLayout::shift_children(VSize s)
{
	for ( int i = 0 ; i < nosChildren ; i++ ) {
		childrenRect[i].l += s.w;
		childrenRect[i].r += s.w;
		childrenRect[i].t += s.h;
		childrenRect[i].b += s.h;
	}
}

void
VLayout::do_layout(VObject* obj)
{
	VObjectList* list = sts.children;
	VObjectStatus csts;
	int i;
	for ( i = 0 ; i < nosChildren ; i++ ) {
		csts.size.w = childrenRect[i].r - childrenRect[i].l;
		csts.size.h = childrenRect[i].b - childrenRect[i].t;
		csts.position.x = childrenRect[i].l + margin_tl.w;
		csts.position.y = childrenRect[i].t + margin_tl.h;
/*
printf("** do layout - child : %s %dx%d (%d-%d)\n", list->object->describe_self(),
csts.size.w, csts.size.h, csts.position.x, csts.position.y);
*/
		list->object->set_status(&csts, VSF_SIZE | VSF_POSITION | VSF_LAYOUT);
		list = list->next;
	}
	
	csts.size.w = parentSize.w + margin_tl.w + margin_br.w;
	csts.size.h = parentSize.h + margin_tl.h + margin_br.h;
	csts.min_size = parentMinSize;

	// specialization for VScrollView
	if ( obj->get_type() == VScrollView::object_type ) {
		VScrollView *scv = static_cast<VScrollView*>(obj);
		VSize in_size = scv->get_internal_size_cur();
		if ( scv->get_internal_size().w == 0 && in_size.w < parentMinSize.w )
			in_size.w = parentMinSize.w;
		if ( scv->get_internal_size().h == 0 && in_size.h < parentMinSize.h )
			in_size.h = parentMinSize.h;
		scv->set_internal_size_do(in_size);
		return;
	}
	
	obj->set_status(&csts, VSF_SIZE | VSF_MIN_SIZE);
/*
printf("** do layout - parent : %s %dx%d (%dx%d)\n", obj->describe_self(),
csts.size.w, csts.size.h, csts.min_size.w, csts.min_size.h);
*/
}



const VObject*
VLayout::get_ancestor_window(const VObject* obj)
{
	VObjectStatus sts;
	if ( (obj->get_status(&sts, VSF_PARENT)).code == V_ER_NO_ERR )
		return sts.window;
	return 0;
}

void
VLayout::mark(const VObject* obj)
{
	VObjectList* list;
	obj = get_ancestor_window(obj);
	if ( ! obj )
		return;
	lock_task(v_layout_lock);
	for ( list = marked ; list ; list = list->next ) {
		if ( list->object == obj )
			goto end;
	}
	list = new VObjectList;
	list->object = const_cast<VObject*>(obj);
	list->next = marked;
	marked = list;
end:
	unlock_task(v_layout_lock, "VLayout::mark");
}

void
VLayout::unmark(const VObject* obj)
{
	VObjectList *list, *prev;
	obj = get_ancestor_window(obj);
	if ( ! obj )
		return;
	lock_task(v_layout_lock);
	for ( prev = 0, list = marked ; list ; prev = list, list = list->next ) {
		if ( list->object == obj ) {
			if ( prev )
				prev->next = list->next;
			else
				marked = list->next;
			delete list;
			goto end;
		}
	}
end:
	unlock_task(v_layout_lock, "VLayout::unmark");
}


bool
VLayout::layout_marked_window()
{
	VObject *object;
	VObjectStatus sts;
	if ( ! marked )
		return true;
	
	while ( marked ) {
		lock_task(v_layout_lock);
		object = marked ? marked->object : 0;
		unlock_task(v_layout_lock, "layout_marked_window");
		if ( object == 0 )
			break;
		unmark(object);

		object->get_status(&sts, VSF_VISIBLE);
		if ( sts.visible == 0 )
			continue;
		object->lock_every_child();
		object->set_status(&sts, VSF_CALC_MIN);
		object->set_status(&sts, VSF_LAYOUT);
#ifdef V_LAYOUT_REDRAW_AFTER_LAYOUT
		object->redraw();
#endif
		object->unlock_every_child();
	}

	return false;
}

